What can your web server do?
Marcin Stożek "Perk"
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
default_type text/html;
location / {
root /data/www;
}
}
}
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
default_type text/html;
location / {
content_by_lua_block {
ngx.say("Hello world!")
}
}
}
}
NGINX
OpenResty
* [init, init_worker], [set, rewrite, access, ssl_certificate], [content, balancer, header, body_filter], [log]
content_by_lua_file
header_by_lua_block
Create correlation id as fast as request reaches our server, pass it to every subsequent request inside our network.
Don't let this correlation id leak outside.
# proxy:/nginx.conf
location / {
set_by_lua $correlation_id '
local correlation_id = string.format("%010d", math.random(0, 10000000000))
ngx.log(ngx.INFO, "Created correlation-id: " .. correlation_id)
return correlation_id
';
proxy_set_header Correlation-ID $correlation_id;
proxy_pass http://app-server;
header_filter_by_lua_block {
ngx.var.app_correlation_id =
ngx.resp.get_headers()["App-Correlation-ID"] or "something-is-wrong-id"
ngx.header.content_length = nil
ngx.header.app_correlation_id = nil
}
body_filter_by_lua '
ngx.arg[1] = string.gsub(ngx.arg[1], "placeholder", ngx.var.correlation_id)
';
}
# app - localhost:8080
$ curl --header "correlation-id: 0xCAFEBABE" -v localhost:8080
> correlation-id: 0xCAFEBABE
...
< App-Correlation-ID: 0xCAFEBABE
...
<!DOCTYPE html>...
# proxy - localhost:80
$ curl --header "correlation-id: 0xCAFEBABE" -v localhost:80
> correlation-id: 0xCAFEBABE
...
(no App-Correlation-ID in here)
...
<!DOCTYPE html>
sent correlation-id: 812312...
received app-correlation-id: 812312...
Manage HTTP session by the proxy server
$ luarocks install lua-resty-session
location /create {
content_by_lua_block {
local session = require "resty.session".start()
session.data.name = "OpenResty Fan"
session:save()
}
}
location /check {
content_by_lua_block {
local session = require "resty.session".open()
ngx.say("Hello " .. session.data.name or "Anonymous")
}
}
location /destroy {
content_by_lua_block {
local session = require "resty.session".start()
session:destroy()
}
}
Route to dynamic hosts based on
Redis / Eureka / custom file / whatever
driven by user-agent
location / {
resolver 127.0.0.11; # Docker's embedded DNS server
set $target '';
access_by_lua_block {
local key = ngx.var.http_user_agent
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1 second
local ok, err = red:connect("redis", 6379)
local host, err = red:get(key)
ngx.var.target = host
}
proxy_pass http://$target;
}
Dynamic images resize with caching and DOS protection.
$ luarocks install magick
$ luarocks install luafilesystem
server {
listen 80;
location @image_server {
content_by_lua_file "image_server.lua";
}
location ~ ^/images/(?<sig>[^/]+)/(?<size>[^/]+)/(?<path>.*\.(?<ext>[a-z_]*))$ {
root cache;
set_md5 $digest "$size/$path";
try_files /$digest.$ext @image_server;
}
location / {
content_by_lua_file "show_links.lua";
}
}
file:/image_server.lua
local function return_not_found(msg)
ngx.status = ngx.HTTP_NOT_FOUND
ngx.header["Content-type"] = "text/html"
ngx.say(msg or "not found")
ngx.exit(0)
end
local function calculate_signature(str)
return ngx.encode_base64(ngx.hmac_sha1(secret, str))
:gsub("[+/=]", {["+"] = "-", ["/"] = "_", ["="] = ","})
:sub(1,12)
end
local signature = calculate_signature(size .. "/" .. path)
if signature ~= sig then
return_not_found("invalid signature, valid one is: " .. signature)
end
local source_fname = images_dir .. path
-- make sure the file exists
local file = io.open(source_fname)
if not file then
return_not_found("file " .. path .. " not found")
end
file:close()
file:/show_links.lua
local function values(t)
local i = 0
return function() i = i + 1; return t[i] end
end
local function create_link(res, file)
local sig = calculate_signature(res .. "/" .. file)
local href="http://localhost/images/" .. sig .. "/" .. res .. "/" .. file
html_result = html_result .. "<a href='" .. href .. "'>" .. file .. ")</a>"
end
local resolutions = {"1280x1024", "1024x768", "800x600", "640x480"}
local lfs = require "lfs"
for file in lfs.dir(images_dir) do
local attr = lfs.attributes(images_dir .. "/" .. file)
if attr.mode == "file" then
for res in values(resolutions) do
create_link(res, file)
create_link("nz-" .. res, file)
end
end
end
User story:
As a user from New Zealand
I want my images rotated
so that I don't need to rotate my phone.
(or just WAF)
CloudFlare uses WAF written in Lua an runs it on top of NGINX...
sounds familiar?
but it depends of course...
here's some benchmark results