Links
- ngx.location.capture
- Ref for examples: http://devblog.mixlr.com/2012/09/01/nginx-lua/
- chaoslawful/lua-nginx-module
- agentzh/lua-resty-redis
- OpenResty
- Lua CJSON
- lua-upstream-nginx-module
- OAuth
- lua-resty-upstream-healthcheck
- lua-resty-string
- lua-resty-lrucache
- lua-resty-lock
- See also:
Notes
Note on ngx.exit: When calling ngx.exit(ngx.OK)
within
a rewrite_by_lua handler,
the nginx request processing control flow will still
continue to the content handler. To terminate the current
request from within
a rewrite_by_lua handler,
calling ngx.exit with
status > 200 (ngx.HTTP_OK
) and status < 300
(ngx.HTTP_SPECIAL_RESPONSE
) for successful quits
and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
(or its
friends) for failures.
Snippets
print
print(...)
→ ngx.log(ngx.NOTICE,
...)
Note: There is a hard coded 2048 byte limitation on error message lengths in the Nginx core. This limit includes trailing newlines and leading time stamps. If the message size exceeds this limit, Nginx will truncate the message text accordingly.
ngx.say
and ngx.print
Ref: ngx.print
ngx.say
≈ ngx.print
but adds a trailing newline.
Convenient way to proxy pass to dev_appserver backend so I don't have to remember the port number.
Note: Set the redis kv on app startup in dev mode.
This allows me to visit http://appname.localhost
and
proxy pass to whatever port the dev_appserver is currently
running on. (Requires wildcard localhost subdomains – ref
os_x/dns.
nginx.conf
http { # Misc nginx config # LUA library paths. Ensure that cjson.so and lua-resty are available. lua_package_path '/Users/chirayu/ck/vcs/ck_3p/lib/lua/?.lua;/Users/chirayu/ck/vcs/ck5/lua/?.lua;;'; lua_package_cpath '/Users/chirayu/ck/vcs/ck_3p/lib/lua/?.so;;'; init_by_lua ' ck_nginx_main = require "ck.nginx.main" ck_nginx_main.config.HOME_PATH = "/Users/chirayu/" ck_nginx_main.config.WEB_ROOT = "/etc/ck/web_roots" ck_nginx_main.config.IMPLICIT_WEBROOTS_ENABLED = true '; server { server_name ~^(?<ck_subdomain>.*)\.(localhost|cheshire\.local\.c-k\.me)$ ~^(?<ck_domain>.*\..*)$ ; listen [::1]:80; listen 127.0.0.1:80; # Example: # # redis-cli hset ck.config.local.subdomain sample.dart.app '{"type": "proxy", "port": 6060}' location @proxy_pass { # Pre-req: Must set $proxy_port proxy_cache off; proxy_pass_header Server; #ckck proxy_pass_header Connection; # ckck #ckck proxy_pass_header Upgrade; # ckck # It's better to use $host instead of $http_host. It's equal to # the value of $http_host (the name of the server in the # request-header), but when there is no such header, it's equal to # the name of the nginx server instead of being blank. proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Scheme $scheme; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; proxy_send_lowat 12000; # To support websocket proxying. proxy_buffering off; # proxy_buffer_size 4k; # proxy_buffers 4 32k; # proxy_busy_buffers_size 64k; # proxy_temp_file_write_size 64k; proxy_pass http://127.0.0.1:$ck_proxy_port; # WebSockets also proxied. # Ref: http://nginx.org/en/docs/http/websocket.html # Requires nginx 1.3.13+ proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; # TODO(chirayu): Is there any point to using the # $connection_upgrade map here? Passing $http_connection through # appears to work fine. # proxy_set_header Connection $connection_upgrade; proxy_set_header Connection $http_connection; } location @link { root $ck_link_root; default_type application/octet-stream; # Ref: http://nginx.org/en/docs/http/ngx_http_autoindex_module.html autoindex on; # Show directory listings. autoindex_exact_size off; # Display human readable sizes. } location @wsgi { # Needs --reload-os-env on uwsgi. # Ref: http://uwsgi.readthedocs.org/en/latest/features/magic-variables.html uwsgi_param UWSGI_SETENV CK_PATH_PREFIX=/; uwsgi_param SCRIPT_NAME /; uwsgi_param UWSGI_MODULE ck.web.wsgi_apps.$ck_wsgi_name; uwsgi_param UWSGI_CALLABLE wsgi_app; uwsgi_modifier1 30; # properly sets PATH_INFO uwsgi_modifier2 0; uwsgi_pass uwsgi_vhost_on_nginx; include /etc/ck/customize/nginx/uwsgi_params; } location / { set $ck_proxy_port -1 ; set $ck_link_root "" ; set $ck_wsgi_name "" ; rewrite_by_lua 'ck_nginx_main.rewrite()'; } } }
LUA lib "ck.nginx.main"
do local ngx = require("ngx") local cjson = require("cjson") local redis = require("resty.redis") local API_CONFIG = { HOME_PATH = "/Users/chirayu/", WEB_ROOT = "/Users/chirayu/z/web_roots/", IMPLICIT_WEBROOTS_ENABLED = false, } local function _SubdomainHandler_NewFields() return { red = nil } end local _SubdomainHandler = _SubdomainHandler_NewFields() function _SubdomainHandler:new() local result = _SubdomainHandler_NewFields() setmetatable(result, self) self.__index = self -- self.__newindex = self return result end function _SubdomainHandler:_get_redis() if self.red ~= nil then return self.red end local red = redis:new() red:set_timeout(1000) -- 1 sec local ok, err = red:connect("127.0.0.1", 6379) if not ok then return nil end self.red = red return self.red end function _SubdomainHandler:_close_redis() if self.red ~= nil then -- put it into the connection pool of size 100, -- with 0 idle timeout local ok, err = self.red:set_keepalive(0, 100) self.red = nil end end function _SubdomainHandler:_bail(message) self:_close_redis() ngx.status = ngx.HTTP_NOT_FOUND; ngx.header["Content-Type"] = "text/plain"; ngx.say(message) return ngx.exit(ngx.HTTP_NOT_FOUND); end function _SubdomainHandler:_lookup_subdomain(subdomain) local json = self.red:hget("ck.config.local.subdomain", subdomain) if not json or json == ngx.null then return nil, self:_bail("Could not lookup ck.config.local.subdomain:" .. subdomain) end local config = cjson.decode(json) return config, nil end function _SubdomainHandler:rewrite(domain_key) local red = self:_get_redis() if not red then return self:_bail("Could not connect to redis") end local config, err = self:_lookup_subdomain(domain_key) if err then return err end if type(config) == "string" then config, err = self:_lookup_subdomain(config) if err then return err end end self:_close_redis() if config.type == "proxy" then ngx.var.ck_proxy_port = config.port return ngx.exec("@proxy_pass") elseif config.type == "link" then if config.root ~= nil then if string.sub(config.root, 1, 2) == "~/" then ngx.var.ck_link_root = API_CONFIG.HOME_PATH .. string.sub(config.root, 3) else ngx.var.ck_link_root = config.root end else if API_CONFIG.IMPLICIT_WEBROOTS_ENABLED then ngx.var.ck_link_root = API_CONFIG.WEB_ROOT .. domain_key else return self:_bail("Implicit web_roots disabled on this host. - chirayu") end end return ngx.exec("@link") elseif config.type == "wsgi" then ngx.var.ck_wsgi_name = domain_key return ngx.exec("@wsgi") else return self:_bail("Unknown subdomain type:" .. config.type) end end return { config = API_CONFIG, rewrite = function() local domain_key = ngx.var["ck_subdomain"] if domain_key == nil then -- make absolute domain for lookup domain_key = ngx.var["ck_domain"] .. "." end return _SubdomainHandler:new():rewrite(domain_key) end } end