├── README.md
├── _example
├── Dockerfile
└── nginx
│ ├── conf
│ ├── nginx.conf
│ └── root-certs.pem
│ ├── html
│ ├── error.html
│ └── org-members.html
│ └── lualib
│ ├── example
│ └── gh.lua
│ └── resty
│ ├── core.lua
│ ├── core
│ ├── base.lua
│ ├── base64.lua
│ ├── ctx.lua
│ ├── exit.lua
│ ├── hash.lua
│ ├── misc.lua
│ ├── regex.lua
│ ├── request.lua
│ ├── response.lua
│ ├── shdict.lua
│ ├── time.lua
│ ├── uri.lua
│ ├── var.lua
│ └── worker.lua
│ ├── http.lua
│ ├── http_headers.lua
│ ├── shcache.lua
│ └── template.lua
├── alpine
└── Dockerfile
└── debian
└── Dockerfile
/README.md:
--------------------------------------------------------------------------------
1 | # OpenResty Docker image
2 |
3 | This repository contains Dockerfiles for [ficusio/openresty](https://hub.docker.com/r/ficusio/openresty/) image, which has two flavors.
4 |
5 | ### Flavors
6 |
7 | The main one is [Alpine linux](https://hub.docker.com/_/alpine/)-based `ficusio/openresty:latest`. Its virtual size is just 31MB, yet it contains a fully functional [OpenResty](http://openresty.org) bundle v1.9.3.1 and [`apk` package manager](http://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management), which allows you to easily install [lots of pre-built packages](https://pkgs.alpinelinux.org/packages).
8 |
9 | The other flavor is `ficusio/openresty:debian`. It is based on `debian:wheezy` and thus is much bigger in size (256MB). It is mostly useful for NginX profiling, as it may not be easy to build different profiling tools with [musl libc](http://www.musl-libc.org/), which is used in Alpine Linux.
10 |
11 | ### Paths & config
12 |
13 | NginX is configured with `/opt/openresty/nginx` [prefix path](http://nginx.org/en/docs/configure.html), which means that, by default, it loads configuration from `/opt/openresty/nginx/conf/nginx.conf` file. The default HTML root path is `/opt/openresty/nginx/html/`.
14 |
15 | OpenResty bundle includes several useful Lua modules located in `/opt/openresty/lualib/` directory. This directory is already present in Lua package path, so you don't need to specify it in NginX `lua_package_path` directive.
16 |
17 | The Lua NginX module is built with LuaJIT 2.1, which is also available as stand-alone `lua` binary.
18 |
19 | NginX stores various temporary files in `/var/nginx/` directory. If you wish to launch the container in [read-only mode](https://github.com/docker/docker/pull/10093), you need to convert that directory into volume to make it writable:
20 |
21 | ```sh
22 | # To launch container
23 | docker run --name nginx --read-only -v /var/nginx ... ficusio/openresty
24 |
25 | # To remove container and its volume
26 | docker rm -v nginx
27 | ```
28 |
29 | See [this PR](https://github.com/ficusio/openresty/pull/7) for background.
30 |
31 | ### `ONBUILD` hook
32 |
33 | This image uses [`ONBUILD` hook](http://docs.docker.com/engine/reference/builder/#onbuild) that automatically copies all files and subdirectories from the `nginx/` directory located at the root of Docker build context (i.e. next to your `Dockerfile`) into `/opt/openresty/nginx/`. The minimal configuration needed to get NginX running is the following:
34 |
35 | ```coffee
36 | project_root/
37 | ├ nginx/ # all subdirs/files will be copied to /opt/openresty/nginx/
38 | | └ conf/
39 | | └ nginx.conf # your NginX configuration file
40 | └ Dockerfile
41 | ```
42 |
43 | Dockerfile:
44 |
45 | ```dockerfile
46 | FROM ficusio/openresty:latest
47 | EXPOSE 8080
48 | ```
49 |
50 | Check [the sample application](https://github.com/ficusio/openresty/tree/master/_example) for more useful example.
51 |
52 | ### Command-line parameters
53 |
54 | NginX is launched with the `nginx -g 'daemon off; error_log /dev/stderr info;'` command. This means that you should not specify the `daemon` directive in your `nginx.conf` file, because it will lead to NginX config check error (duplicate directive).
55 |
56 | No-daemon mode is needed to allow host OS' service manager, like `systemd`, or [Docker itself](http://docs.docker.com/engine/reference/commandline/cli/#restart-policies) to detect that NginX has exited and restart the container. Otherwise in-container service manager would be required.
57 |
58 | Error log is redirected to `stderr` to simplify debugging and log collection with [Docker logging drivers](https://docs.docker.com/engine/reference/logging/overview/) or tools like [logspout](https://github.com/gliderlabs/logspout).
59 |
60 | If you wish to run it with different command-line options, you can add `CMD` directive to your Dockerfile. It will override the command provided in this image. Another option is to pass a command to `docker run` directly:
61 |
62 | ```text
63 | $ docker run --rm -it --name test ficusio/openresty bash
64 | root@06823698db68:/opt/openresty/nginx $ ls -l
65 | total 12
66 | drwxr-xr-x 2 root root 4096 Feb 1 14:48 conf
67 | drwxr-xr-x 2 root root 4096 Feb 1 14:48 html
68 | drwxr-xr-x 2 root root 4096 Feb 1 14:48 sbin
69 | ```
70 |
71 | ### Usage during development
72 |
73 | To avoid rebuilding your Docker image after each modification of Lua code or NginX config, you can add a simple script that mounts config/content directories to appropriate locations and starts NginX:
74 |
75 | ```bash
76 | #!/usr/bin/env bash
77 |
78 | exec docker run --rm -it \
79 | --name my-app-dev \
80 | -v "$(pwd)/nginx/conf":/opt/openresty/nginx/conf \
81 | -v "$(pwd)/nginx/lualib":/opt/openresty/nginx/lualib \
82 | -p 8080:8080 \
83 | ficusio/openresty:debian "$@"
84 |
85 | # you may add more -v options to mount another directories, e.g. nginx/html/
86 |
87 | # do not do -v "$(pwd)/nginx":/opt/openresty/nginx because it will hide
88 | # the NginX binary located at /opt/openresty/nginx/sbin/nginx
89 | ```
90 |
91 | Place it next to your `Dockerfile`, make executable and use during development. You may also want to temporarily disable [Lua code cache](https://github.com/openresty/lua-nginx-module#lua_code_cache) to allow testing code modifications without re-starting NginX.
92 |
--------------------------------------------------------------------------------
/_example/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ficusio/openresty:latest
2 | EXPOSE 8080
--------------------------------------------------------------------------------
/_example/nginx/conf/nginx.conf:
--------------------------------------------------------------------------------
1 | worker_processes 2;
2 |
3 | events {
4 | worker_connections 1024;
5 | }
6 |
7 | http {
8 | access_log off;
9 |
10 | keepalive_timeout 65;
11 | resolver 8.8.8.8;
12 |
13 | lua_ssl_trusted_certificate "root-certs.pem";
14 | lua_ssl_verify_depth 2;
15 | lua_package_path "$prefix/lualib/?.lua;;";
16 |
17 | lua_shared_dict locks 1M;
18 | lua_shared_dict cache 10M;
19 |
20 | # see https://github.com/openresty/lua-resty-core
21 | init_by_lua '
22 | require "resty.core"
23 | ';
24 |
25 | server {
26 | listen 8080;
27 | default_type text/html;
28 |
29 | location = / {
30 | content_by_lua '
31 | ngx.say "
Hello from Lua-land! Try this link :)"
32 | ';
33 | }
34 |
35 | # /show-org/orgname[?nocache] - show GitHib organization members
36 | #
37 | location ~* ^/show-org/([\w\d]+)/?$ {
38 | content_by_lua '
39 | local orgname = ngx.var[1]
40 | local nocache = ngx.req.get_uri_args().nocache ~= nil
41 | local gh, template = require "example.gh", require "resty.template"
42 | local org, err, status
43 | if nocache then
44 | org, err, status = gh.getOrg(orgname)
45 | else
46 | org, err, status = gh.getOrgCached(orgname)
47 | end
48 | if err then
49 | ngx.status = status or 500
50 | template.render("error.html", { message = err })
51 | else
52 | template.render("org-members.html", { org = org })
53 | end
54 | ';
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/_example/nginx/html/error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sorry
5 |
6 |
7 | Error: {{message}}.
8 |
9 |
--------------------------------------------------------------------------------
/_example/nginx/html/org-members.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Public members of {{org.name}} organization
5 |
40 |
41 |
42 | Public members of {{org.name}} organization:
43 | {% for _, member in pairs(org.members) do %}
44 |
45 | {{member.login}}
46 |
47 | {% end %}
48 |
49 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/example/gh.lua:
--------------------------------------------------------------------------------
1 | local http = require "resty.http"
2 | local json = require "cjson.safe"
3 | local shcache = require "resty.shcache"
4 | local spawn = ngx.thread.spawn
5 | local wait = ngx.thread.wait
6 |
7 |
8 | local _M = {}
9 |
10 |
11 | local function requestJSON(uri)
12 | local httpc = http.new()
13 | local res, err = httpc:request_uri(uri)
14 | if err or not res then
15 | return nil, "failed to send request: " .. (err or "unknown error")
16 | end
17 | if res.status ~= 200 then
18 | local result, err = json.decode(res.body)
19 | local msg = not err and result.message or ("unexpected status " .. res.status)
20 | return nil, msg, res.status
21 | end
22 | local result, err = json.decode(res.body)
23 | if err then
24 | return nil, "cannot parse response: " .. err
25 | end
26 | return result, nil, res.status
27 | end
28 |
29 |
30 | function _M.getOrgInfo(orgname)
31 | return requestJSON("https://api.github.com/orgs/" .. orgname)
32 | end
33 |
34 |
35 | function _M.getOrgMembers(orgname)
36 | return requestJSON("https://api.github.com/orgs/" .. orgname .. "/members")
37 | end
38 |
39 |
40 | function _M.getOrg(orgname)
41 | -- perform both requests in parallel
42 | local tInfo = spawn(_M.getOrgInfo, orgname)
43 | local tMembers = spawn(_M.getOrgMembers, orgname)
44 | local ok, info, err, status = wait(tInfo)
45 | if not ok or err then
46 | return nil, err or "terminated", status
47 | end
48 | local ok, members, err, status = wait(tMembers)
49 | if not ok or err then
50 | return nil, err or "terminated", status
51 | end
52 | info.members = members
53 | return info, nil, status
54 | end
55 |
56 |
57 | function _M.getOrgCached(orgname)
58 | local cache, err = shcache:new(ngx.shared.cache, {
59 | external_lookup = _M._orgLookup,
60 | external_lookup_arg = orgname,
61 | encode = json.encode,
62 | decode = json.decode
63 | }, {
64 | positive_ttl = 15,
65 | negative_ttl = 5,
66 | locks_shdict = "locks",
67 | name = "orgs"
68 | })
69 | if err or not cache then
70 | return nil, "cannot create cache: " .. (err or "error not provided")
71 | end
72 | local data = cache:load(orgname)
73 | if not data then
74 | return nil, "weird error: nil data"
75 | end
76 | return data.org, data.err, data.status
77 | end
78 |
79 |
80 | function _M._orgLookup(orgname)
81 | local org, err, status = _M.getOrg(orgname)
82 | local ttl = nil
83 | if err then
84 | print("lookup error: " .. err)
85 | ttl = 5
86 | end
87 | return { org = org, err = err, status = status }, nil, ttl
88 | end
89 |
90 |
91 | return _M
92 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | require "resty.core.uri"
5 | require "resty.core.hash"
6 | require "resty.core.base64"
7 | require "resty.core.regex"
8 | require "resty.core.exit"
9 | require "resty.core.shdict"
10 | require "resty.core.var"
11 | require "resty.core.ctx"
12 | require "resty.core.misc"
13 | require "resty.core.request"
14 | require "resty.core.response"
15 | require "resty.core.time"
16 | require "resty.core.worker"
17 |
18 |
19 | local base = require "resty.core.base"
20 |
21 |
22 | return {
23 | version = base.version
24 | }
25 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/base.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local ffi_new = ffi.new
6 | local error = error
7 | local setmetatable = setmetatable
8 | local floor = math.floor
9 | local ceil = math.ceil
10 |
11 |
12 | local str_buf_size = 4096
13 | local str_buf
14 | local size_ptr
15 | local FREE_LIST_REF = 0
16 |
17 |
18 | if not ngx.config
19 | or not ngx.config.ngx_lua_version
20 | or ngx.config.ngx_lua_version < 9011
21 | then
22 | error("ngx_lua 0.9.11+ required")
23 | end
24 |
25 |
26 | if string.find(jit.version, " 2.0") then
27 | ngx.log(ngx.WARN, "use of lua-resty-core with LuaJIT 2.0 is "
28 | .. "not recommended; use LuaJIT 2.1+ instead")
29 | end
30 |
31 |
32 | local ok, new_tab = pcall(require, "table.new")
33 | if not ok then
34 | new_tab = function (narr, nrec) return {} end
35 | end
36 |
37 |
38 | local ok, clear_tab = pcall(require, "table.clear")
39 | if not ok then
40 | clear_tab = function (tab)
41 | for k, _ in pairs(tab) do
42 | tab[k] = nil
43 | end
44 | end
45 | end
46 |
47 |
48 | -- XXX for now LuaJIT 2.1 cannot compile require()
49 | -- so we make the fast code path Lua only in our own
50 | -- wrapper so that most of the require() calls in hot
51 | -- Lua code paths can be JIT compiled.
52 | do
53 | local orig_require = require
54 | local pkg_loaded = package.loaded
55 | local function my_require(name)
56 | local mod = pkg_loaded[name]
57 | if mod then
58 | return mod
59 | end
60 | return orig_require(name)
61 | end
62 | getfenv(0).require = my_require
63 | end
64 |
65 |
66 | if not pcall(ffi.typeof, "ngx_str_t") then
67 | ffi.cdef[[
68 | typedef struct {
69 | size_t len;
70 | const unsigned char *data;
71 | } ngx_str_t;
72 | ]]
73 | end
74 |
75 |
76 | if not pcall(ffi.typeof, "ngx_http_request_t") then
77 | ffi.cdef[[
78 | struct ngx_http_request_s;
79 | typedef struct ngx_http_request_s ngx_http_request_t;
80 | ]]
81 | end
82 |
83 |
84 | if not pcall(ffi.typeof, "ngx_http_lua_ffi_str_t") then
85 | ffi.cdef[[
86 | typedef struct {
87 | int len;
88 | const unsigned char *data;
89 | } ngx_http_lua_ffi_str_t;
90 | ]]
91 | end
92 |
93 |
94 | local c_buf_type = ffi.typeof("char[?]")
95 |
96 |
97 | local _M = new_tab(0, 16)
98 |
99 |
100 | _M.version = "0.1.0"
101 | _M.new_tab = new_tab
102 | _M.clear_tab = clear_tab
103 |
104 |
105 | local errmsg
106 |
107 |
108 | function _M.get_errmsg_ptr()
109 | if not errmsg then
110 | errmsg = ffi_new("char *[1]")
111 | end
112 | return errmsg
113 | end
114 |
115 |
116 | if not ngx then
117 | return error("no existing ngx. table found")
118 | end
119 |
120 |
121 | function _M.set_string_buf_size(size)
122 | if size <= 0 then
123 | return
124 | end
125 | if str_buf then
126 | str_buf = nil
127 | end
128 | str_buf_size = ceil(size)
129 | end
130 |
131 |
132 | function _M.get_string_buf_size()
133 | return str_buf_size
134 | end
135 |
136 |
137 | function _M.get_size_ptr()
138 | if not size_ptr then
139 | size_ptr = ffi_new("size_t[1]")
140 | end
141 |
142 | return size_ptr
143 | end
144 |
145 |
146 | function _M.get_string_buf(size, must_alloc)
147 | -- ngx.log(ngx.ERR, "str buf size: ", str_buf_size)
148 | if size > str_buf_size or must_alloc then
149 | return ffi_new(c_buf_type, size)
150 | end
151 |
152 | if not str_buf then
153 | str_buf = ffi_new(c_buf_type, str_buf_size)
154 | end
155 |
156 | return str_buf
157 | end
158 |
159 |
160 | function _M.ref_in_table(tb, key)
161 | if key == nil then
162 | return -1
163 | end
164 | local ref = tb[FREE_LIST_REF]
165 | if ref and ref ~= 0 then
166 | tb[FREE_LIST_REF] = tb[ref]
167 |
168 | else
169 | ref = #tb + 1
170 | end
171 | tb[ref] = key
172 |
173 | -- print("ref key_id returned ", ref)
174 | return ref
175 | end
176 |
177 |
178 | _M.FFI_OK = 0
179 | _M.FFI_NO_REQ_CTX = -100
180 | _M.FFI_BAD_CONTEXT = -101
181 | _M.FFI_ERROR = -1
182 | _M.FFI_BUSY = -3
183 | _M.FFI_DONE = -4
184 | _M.FFI_DECLINED = -5
185 |
186 |
187 | return _M
188 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/base64.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 | local ffi_string = ffi.string
8 | local ffi_new = ffi.new
9 | local C = ffi.C
10 | local setmetatable = setmetatable
11 | local ngx = ngx
12 | local type = type
13 | local tostring = tostring
14 | local error = error
15 | local get_string_buf = base.get_string_buf
16 | local get_size_ptr = base.get_size_ptr
17 | local floor = math.floor
18 | local print = print
19 | local tonumber = tonumber
20 |
21 |
22 | ffi.cdef[[
23 | size_t ngx_http_lua_ffi_encode_base64(const unsigned char *src,
24 | size_t len, unsigned char *dst);
25 |
26 | int ngx_http_lua_ffi_decode_base64(const unsigned char *src,
27 | size_t len, unsigned char *dst,
28 | size_t *dlen);
29 | ]]
30 |
31 |
32 | local function base64_encoded_length(len)
33 | return floor((len + 2) / 3) * 4
34 | end
35 |
36 |
37 | ngx.encode_base64 = function (s)
38 | if type(s) ~= 'string' then
39 | if not s then
40 | s = ''
41 | else
42 | s = tostring(s)
43 | end
44 | end
45 | local slen = #s
46 | local dlen = base64_encoded_length(slen)
47 | -- print("dlen: ", tonumber(dlen))
48 | local dst = get_string_buf(dlen)
49 | dlen = C.ngx_http_lua_ffi_encode_base64(s, slen, dst)
50 | return ffi_string(dst, dlen)
51 | end
52 |
53 |
54 | local function base64_decoded_length(len)
55 | return floor((len + 3) / 4) * 3
56 | end
57 |
58 |
59 | ngx.decode_base64 = function (s)
60 | if type(s) ~= 'string' then
61 | return error("string argument only")
62 | end
63 | local slen = #s
64 | local dlen = base64_decoded_length(slen)
65 | -- print("dlen: ", tonumber(dlen))
66 | local dst = get_string_buf(dlen)
67 | local pdlen = get_size_ptr()
68 | local ok = C.ngx_http_lua_ffi_decode_base64(s, slen, dst, pdlen)
69 | if ok == 0 then
70 | return nil
71 | end
72 | return ffi_string(dst, pdlen[0])
73 | end
74 |
75 |
76 | return {
77 | version = base.version
78 | }
79 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/ctx.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local debug = require 'debug'
6 | local base = require "resty.core.base"
7 | local misc = require "resty.core.misc"
8 |
9 |
10 | local register_getter = misc.register_ngx_magic_key_getter
11 | local register_setter = misc.register_ngx_magic_key_setter
12 | local registry = debug.getregistry()
13 | local new_tab = base.new_tab
14 | local ref_in_table = base.ref_in_table
15 | local getfenv = getfenv
16 | local C = ffi.C
17 | local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
18 | local FFI_OK = base.FFI_OK
19 |
20 |
21 | ffi.cdef[[
22 | int ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r);
23 | int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref);
24 | ]]
25 |
26 |
27 | local _M = {
28 | _VERSION = base.version
29 | }
30 |
31 |
32 | local function get_ctx_table()
33 | local r = getfenv(0).__ngx_req
34 |
35 | if not r then
36 | return error("no request found")
37 | end
38 |
39 | local ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref(r)
40 | if ctx_ref == FFI_NO_REQ_CTX then
41 | return error("no request ctx found")
42 | end
43 |
44 | local ctxs = registry.ngx_lua_ctx_tables
45 | if ctx_ref < 0 then
46 | local ctx = new_tab(0, 4)
47 | ctx_ref = ref_in_table(ctxs, ctx)
48 | if C.ngx_http_lua_ffi_set_ctx_ref(r, ctx_ref) ~= FFI_OK then
49 | return nil
50 | end
51 | return ctx
52 | end
53 | return ctxs[ctx_ref]
54 | end
55 | register_getter("ctx", get_ctx_table)
56 |
57 |
58 | local function set_ctx_table(ctx)
59 | local r = getfenv(0).__ngx_req
60 |
61 | if not r then
62 | return error("no request found")
63 | end
64 |
65 | local ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref(r)
66 | if ctx_ref == FFI_NO_REQ_CTX then
67 | return error("no request ctx found")
68 | end
69 |
70 | local ctxs = registry.ngx_lua_ctx_tables
71 | if ctx_ref < 0 then
72 | ctx_ref = ref_in_table(ctxs, ctx)
73 | C.ngx_http_lua_ffi_set_ctx_ref(r, ctx_ref)
74 | return
75 | end
76 | ctxs[ctx_ref] = ctx
77 | end
78 | register_setter("ctx", set_ctx_table)
79 |
80 |
81 | return _M
82 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/exit.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local ffi_string = ffi.string
6 | local C = ffi.C
7 | local ngx = ngx
8 | local tostring = tostring
9 | local error = error
10 | local base = require "resty.core.base"
11 | local get_string_buf = base.get_string_buf
12 | local get_size_ptr = base.get_size_ptr
13 | local base = require "resty.core.base"
14 | local getfenv = getfenv
15 | local co_yield = coroutine._yield
16 |
17 |
18 | ffi.cdef[[
19 | int ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status,
20 | unsigned char *err, size_t *errlen);
21 | ]]
22 |
23 |
24 | local ERR_BUF_SIZE = 128
25 | local FFI_DONE = base.FFI_DONE
26 |
27 |
28 | ngx.exit = function (rc)
29 | local err = get_string_buf(ERR_BUF_SIZE)
30 | local errlen = get_size_ptr()
31 | local r = getfenv(0).__ngx_req
32 | if r == nil then
33 | return error("no request found")
34 | end
35 | errlen[0] = ERR_BUF_SIZE
36 | local rc = C.ngx_http_lua_ffi_exit(r, rc, err, errlen)
37 | if rc == 0 then
38 | -- print("yielding...")
39 | return co_yield()
40 | end
41 | if rc == FFI_DONE then
42 | return
43 | end
44 | return error(ffi_string(err, errlen[0]))
45 | end
46 |
47 |
48 | return {
49 | version = base.version
50 | }
51 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/hash.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local ffi_string = ffi.string
6 | local ffi_new = ffi.new
7 | local C = ffi.C
8 | local setmetatable = setmetatable
9 | local ngx = ngx
10 | local type = type
11 | local tostring = tostring
12 | local error = error
13 | local base = require "resty.core.base"
14 |
15 |
16 | ffi.cdef[[
17 | void ngx_http_lua_ffi_md5_bin(const unsigned char *src, size_t len,
18 | unsigned char *dst);
19 |
20 | void ngx_http_lua_ffi_md5(const unsigned char *src, size_t len,
21 | unsigned char *dst);
22 |
23 | int ngx_http_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
24 | unsigned char *dst);
25 | ]]
26 |
27 |
28 | local MD5_DIGEST_LEN = 16
29 | local md5_buf = ffi_new("unsigned char[?]", MD5_DIGEST_LEN)
30 |
31 | ngx.md5_bin = function (s)
32 | if type(s) ~= 'string' then
33 | if not s then
34 | s = ''
35 | else
36 | s = tostring(s)
37 | end
38 | end
39 | C.ngx_http_lua_ffi_md5_bin(s, #s, md5_buf)
40 | return ffi_string(md5_buf, MD5_DIGEST_LEN)
41 | end
42 |
43 |
44 | local MD5_HEX_DIGEST_LEN = MD5_DIGEST_LEN * 2
45 | local md5_hex_buf = ffi_new("unsigned char[?]", MD5_HEX_DIGEST_LEN)
46 |
47 | ngx.md5 = function (s)
48 | if type(s) ~= 'string' then
49 | if not s then
50 | s = ''
51 | else
52 | s = tostring(s)
53 | end
54 | end
55 | C.ngx_http_lua_ffi_md5(s, #s, md5_hex_buf)
56 | return ffi_string(md5_hex_buf, MD5_HEX_DIGEST_LEN)
57 | end
58 |
59 |
60 | local SHA_DIGEST_LEN = 20
61 | local sha_buf = ffi_new("unsigned char[?]", SHA_DIGEST_LEN)
62 |
63 | ngx.sha1_bin = function (s)
64 | if type(s) ~= 'string' then
65 | if not s then
66 | s = ''
67 | else
68 | s = tostring(s)
69 | end
70 | end
71 | local ok = C.ngx_http_lua_ffi_sha1_bin(s, #s, sha_buf)
72 | if ok == 0 then
73 | return error("SHA-1 support missing in Nginx")
74 | end
75 | return ffi_string(sha_buf, SHA_DIGEST_LEN)
76 | end
77 |
78 |
79 | return {
80 | version = base.version
81 | }
82 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/misc.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local base = require "resty.core.base"
5 | local ffi = require "ffi"
6 |
7 |
8 | local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
9 | local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
10 | local new_tab = base.new_tab
11 | local C = ffi.C
12 | local getmetatable = getmetatable
13 | local ngx_magic_key_getters = new_tab(0, 4)
14 | local ngx_magic_key_setters = new_tab(0, 2)
15 | local ngx = ngx
16 | local getfenv = getfenv
17 | local type = type
18 |
19 |
20 | local _M = new_tab(0, 3)
21 | _M._VERSION = base.version
22 |
23 |
24 | local function register_getter(key, func)
25 | ngx_magic_key_getters[key] = func
26 | end
27 | _M.register_ngx_magic_key_getter = register_getter
28 |
29 |
30 | local function register_setter(key, func)
31 | ngx_magic_key_setters[key] = func
32 | end
33 | _M.register_ngx_magic_key_setter = register_setter
34 |
35 |
36 | local mt = getmetatable(ngx)
37 |
38 |
39 | local old_index = mt.__index
40 | mt.__index = function (tb, key)
41 | local f = ngx_magic_key_getters[key]
42 | if f then
43 | return f()
44 | end
45 | return old_index(tb, key)
46 | end
47 |
48 |
49 | local old_newindex = mt.__newindex
50 | mt.__newindex = function (tb, key, ctx)
51 | local f = ngx_magic_key_setters[key]
52 | if f then
53 | return f(ctx)
54 | end
55 | return old_newindex(tb, key, ctx)
56 | end
57 |
58 |
59 | ffi.cdef[[
60 | int ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r);
61 | int ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int r);
62 | int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r);
63 | int ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r);
64 | ]]
65 |
66 |
67 | -- ngx.status
68 |
69 | local function get_status()
70 | local r = getfenv(0).__ngx_req
71 |
72 | if not r then
73 | return error("no request found")
74 | end
75 |
76 | local rc = C.ngx_http_lua_ffi_get_resp_status(r)
77 |
78 | if rc == FFI_BAD_CONTEXT then
79 | return error("API disabled in the current context")
80 | end
81 |
82 | return rc
83 | end
84 | register_getter("status", get_status)
85 |
86 |
87 | local function set_status(status)
88 | local r = getfenv(0).__ngx_req
89 |
90 | if not r then
91 | return error("no request found")
92 | end
93 |
94 | if type(status) ~= 'number' then
95 | status = tonumber(status)
96 | end
97 |
98 | local rc = C.ngx_http_lua_ffi_set_resp_status(r, status)
99 |
100 | if rc == FFI_BAD_CONTEXT then
101 | return error("API disabled in the current context")
102 | end
103 |
104 | return
105 | end
106 | register_setter("status", set_status)
107 |
108 |
109 | -- ngx.is_subrequest
110 |
111 | local function is_subreq()
112 | local r = getfenv(0).__ngx_req
113 |
114 | if not r then
115 | return error("no request found")
116 | end
117 |
118 | local rc = C.ngx_http_lua_ffi_is_subrequest(r)
119 |
120 | if rc == FFI_BAD_CONTEXT then
121 | return error("API disabled in the current context")
122 | end
123 |
124 | return rc == 1 and true or false
125 | end
126 | register_getter("is_subrequest", is_subreq)
127 |
128 |
129 | -- ngx.headers_sent
130 |
131 | local function headers_sent()
132 | local r = getfenv(0).__ngx_req
133 |
134 | if not r then
135 | return error("no request found")
136 | end
137 |
138 | local rc = C.ngx_http_lua_ffi_headers_sent(r)
139 |
140 | if rc == FFI_NO_REQ_CTX then
141 | return error("no request ctx found")
142 | end
143 |
144 | if rc == FFI_BAD_CONTEXT then
145 | return error("API disabled in the current context")
146 | end
147 |
148 | return rc == 1
149 | end
150 | register_getter("headers_sent", headers_sent)
151 |
152 |
153 | return _M
154 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/regex.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 | local bit = require "bit"
7 | require "resty.core.time" -- for ngx.now used by resty.lrucache
8 | local lrucache = require "resty.lrucache"
9 |
10 | local lrucache_get = lrucache.get
11 | local lrucache_set = lrucache.set
12 | local ffi_string = ffi.string
13 | local ffi_new = ffi.new
14 | local ffi_gc = ffi.gc
15 | local ffi_copy = ffi.copy
16 | local ffi_cast = ffi.cast
17 | local C = ffi.C
18 | local bor = bit.bor
19 | local band = bit.band
20 | local lshift = bit.lshift
21 | local sub = string.sub
22 | local fmt = string.format
23 | local byte = string.byte
24 | local setmetatable = setmetatable
25 | local concat = table.concat
26 | local ngx = ngx
27 | local type = type
28 | local tostring = tostring
29 | local error = error
30 | local get_string_buf = base.get_string_buf
31 | local get_string_buf_size = base.get_string_buf_size
32 | local get_size_ptr = base.get_size_ptr
33 | local new_tab = base.new_tab
34 | local floor = math.floor
35 | local print = print
36 | local tonumber = tonumber
37 | local ngx_log = ngx.log
38 | local ngx_ERR = ngx.ERR
39 |
40 |
41 | if not ngx.re then
42 | ngx.re = {}
43 | end
44 |
45 |
46 | local MAX_ERR_MSG_LEN = 128
47 |
48 |
49 | local FLAG_COMPILE_ONCE = 0x01
50 | local FLAG_DFA = 0x02
51 | local FLAG_JIT = 0x04
52 | local FLAG_DUPNAMES = 0x08
53 | local FLAG_NO_UTF8_CHECK = 0x10
54 |
55 |
56 | local PCRE_CASELESS = 0x0000001
57 | local PCRE_MULTILINE = 0x0000002
58 | local PCRE_DOTALL = 0x0000004
59 | local PCRE_EXTENDED = 0x0000008
60 | local PCRE_ANCHORED = 0x0000010
61 | local PCRE_UTF8 = 0x0000800
62 | local PCRE_DUPNAMES = 0x0080000
63 | local PCRE_JAVASCRIPT_COMPAT = 0x2000000
64 |
65 |
66 | local PCRE_ERROR_NOMATCH = -1
67 |
68 |
69 | local regex_match_cache
70 | local regex_sub_func_cache = new_tab(0, 4)
71 | local regex_sub_str_cache = new_tab(0, 4)
72 | local max_regex_cache_size
73 | local regex_cache_size = 0
74 | local script_engine
75 |
76 |
77 | ffi.cdef[[
78 | typedef struct {
79 | ngx_str_t value;
80 | void *lengths;
81 | void *values;
82 | } ngx_http_lua_complex_value_t;
83 |
84 | typedef struct {
85 | void *pool;
86 | unsigned char *name_table;
87 | int name_count;
88 | int name_entry_size;
89 |
90 | int ncaptures;
91 | int *captures;
92 |
93 | void *regex;
94 | void *regex_sd;
95 |
96 | ngx_http_lua_complex_value_t *replace;
97 |
98 | const char *pattern;
99 | } ngx_http_lua_regex_t;
100 |
101 | ngx_http_lua_regex_t *
102 | ngx_http_lua_ffi_compile_regex(const unsigned char *pat,
103 | size_t pat_len, int flags,
104 | int pcre_opts, unsigned char *errstr,
105 | size_t errstr_size);
106 |
107 | int ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags,
108 | const unsigned char *s, size_t len, int pos);
109 |
110 | void ngx_http_lua_ffi_destroy_regex(ngx_http_lua_regex_t *re);
111 |
112 | int ngx_http_lua_ffi_compile_replace_template(ngx_http_lua_regex_t *re,
113 | const unsigned char
114 | *replace_data,
115 | size_t replace_len);
116 |
117 | struct ngx_http_lua_script_engine_s;
118 | typedef struct ngx_http_lua_script_engine_s *ngx_http_lua_script_engine_t;
119 |
120 | ngx_http_lua_script_engine_t *ngx_http_lua_ffi_create_script_engine(void);
121 |
122 | void ngx_http_lua_ffi_init_script_engine(ngx_http_lua_script_engine_t *e,
123 | const unsigned char *subj,
124 | ngx_http_lua_regex_t *compiled,
125 | int count);
126 |
127 | void ngx_http_lua_ffi_destroy_script_engine(
128 | ngx_http_lua_script_engine_t *e);
129 |
130 | size_t ngx_http_lua_ffi_script_eval_len(ngx_http_lua_script_engine_t *e,
131 | ngx_http_lua_complex_value_t *cv);
132 |
133 | size_t ngx_http_lua_ffi_script_eval_data(ngx_http_lua_script_engine_t *e,
134 | ngx_http_lua_complex_value_t *cv,
135 | unsigned char *dst);
136 |
137 | uint32_t ngx_http_lua_ffi_max_regex_cache_size(void);
138 | ]]
139 |
140 |
141 | local c_str_type = ffi.typeof("const char *")
142 |
143 | local cached_re_opts = new_tab(0, 4)
144 |
145 | local _M = {
146 | version = base.version
147 | }
148 |
149 |
150 | local buf_grow_ratio = 2
151 |
152 | function _M.set_buf_grow_ratio(ratio)
153 | buf_grow_ratio = ratio
154 | end
155 |
156 |
157 | local function get_max_regex_cache_size()
158 | if max_regex_cache_size then
159 | return max_regex_cache_size
160 | end
161 | max_regex_cache_size = C.ngx_http_lua_ffi_max_regex_cache_size()
162 | return max_regex_cache_size
163 | end
164 |
165 |
166 | local function parse_regex_opts(opts)
167 | local t = cached_re_opts[opts]
168 | if t then
169 | return t[1], t[2]
170 | end
171 |
172 | local flags = 0
173 | local pcre_opts = 0
174 | local len = #opts
175 |
176 | for i = 1, len do
177 | local opt = byte(opts, i)
178 | if opt == byte("o") then
179 | flags = bor(flags, FLAG_COMPILE_ONCE)
180 |
181 | elseif opt == byte("j") then
182 | flags = bor(flags, FLAG_JIT)
183 |
184 | elseif opt == byte("i") then
185 | pcre_opts = bor(pcre_opts, PCRE_CASELESS)
186 |
187 | elseif opt == byte("s") then
188 | pcre_opts = bor(pcre_opts, PCRE_DOTALL)
189 |
190 | elseif opt == byte("m") then
191 | pcre_opts = bor(pcre_opts, PCRE_MULTILINE)
192 |
193 | elseif opt == byte("u") then
194 | pcre_opts = bor(pcre_opts, PCRE_UTF8)
195 |
196 | elseif opt == byte("U") then
197 | pcre_opts = bor(pcre_opts, PCRE_UTF8)
198 | flags = bor(flags, FLAG_NO_UTF8_CHECK)
199 |
200 | elseif opt == byte("x") then
201 | pcre_opts = bor(pcre_opts, PCRE_EXTENDED)
202 |
203 | elseif opt == byte("d") then
204 | flags = bor(flags, FLAG_DFA)
205 |
206 | elseif opt == byte("a") then
207 | pcre_opts = bor(pcre_opts, PCRE_ANCHORED)
208 |
209 | elseif opt == byte("D") then
210 | pcre_opts = bor(pcre_opts, PCRE_DUPNAMES)
211 | flags = bor(flags, FLAG_DUPNAMES)
212 |
213 | elseif opt == byte("J") then
214 | pcre_opts = bor(pcre_opts, PCRE_JAVASCRIPT_COMPAT)
215 |
216 | else
217 | return error(fmt('unknown flag "%s" (flags "%s")',
218 | sub(opts, i, i), opts))
219 | end
220 | end
221 |
222 | cached_re_opts[opts] = {flags, pcre_opts}
223 | return flags, pcre_opts
224 | end
225 |
226 |
227 | local function collect_named_captures(compiled, flags, res)
228 | local name_count = compiled.name_count
229 | local name_table = compiled.name_table
230 | local entry_size = compiled.name_entry_size
231 |
232 | local ind = 0
233 | local dup_names = (band(flags, FLAG_DUPNAMES) ~= 0)
234 | for i = 1, name_count do
235 | local n = bor(lshift(name_table[ind], 8), name_table[ind + 1])
236 | -- ngx.say("n = ", n)
237 | local name = ffi_string(name_table + ind + 2)
238 | local cap = res[n]
239 | if cap then
240 | if dup_names then
241 | local old = res[name]
242 | if old then
243 | old[#old + 1] = cap
244 | else
245 | res[name] = {cap}
246 | end
247 | else
248 | res[name] = cap
249 | end
250 | end
251 |
252 | ind = ind + entry_size
253 | end
254 | end
255 |
256 |
257 | local function collect_captures(compiled, rc, subj, flags, res)
258 | local cap = compiled.captures
259 | local name_count = compiled.name_count
260 |
261 | if not res then
262 | res = new_tab(rc, name_count)
263 | end
264 |
265 | local i = 0
266 | local n = 0
267 | while i < rc do
268 | local from = cap[n]
269 | if from >= 0 then
270 | local to = cap[n + 1]
271 | res[i] = sub(subj, from + 1, to)
272 | end
273 | i = i + 1
274 | n = n + 2
275 | end
276 |
277 | if name_count > 0 then
278 | collect_named_captures(compiled, flags, res)
279 | end
280 |
281 | return res
282 | end
283 |
284 |
285 | local function destroy_compiled_regex(compiled)
286 | C.ngx_http_lua_ffi_destroy_regex(ffi_gc(compiled, nil))
287 | end
288 |
289 |
290 | local function re_match_compile(regex, opts)
291 | local flags = 0
292 | local pcre_opts = 0
293 |
294 | if opts then
295 | flags, pcre_opts = parse_regex_opts(opts)
296 | else
297 | opts = ""
298 | end
299 |
300 | local compiled, key
301 | local compile_once = (band(flags, FLAG_COMPILE_ONCE) == 1)
302 |
303 | -- FIXME: better put this in the outer scope when fixing the ngx.re API's
304 | -- compatibility in the init_by_lua* context.
305 | if not regex_match_cache then
306 | local sz = get_max_regex_cache_size()
307 | if sz <= 0 then
308 | compile_once = false
309 | else
310 | regex_match_cache = lrucache.new(sz)
311 | end
312 | end
313 |
314 | if compile_once then
315 | key = regex .. '\0' .. opts
316 | compiled = lrucache_get(regex_match_cache, key)
317 | end
318 |
319 | -- compile the regex
320 |
321 | if compiled == nil then
322 | -- print("compiled regex not found, compiling regex...")
323 | local errbuf = get_string_buf(MAX_ERR_MSG_LEN)
324 |
325 | compiled = C.ngx_http_lua_ffi_compile_regex(regex, #regex,
326 | flags, pcre_opts,
327 | errbuf, MAX_ERR_MSG_LEN)
328 |
329 | if compiled == nil then
330 | return nil, ffi_string(errbuf)
331 | end
332 |
333 | ffi_gc(compiled, C.ngx_http_lua_ffi_destroy_regex)
334 |
335 | -- print("ncaptures: ", compiled.ncaptures)
336 |
337 | if compile_once then
338 | -- print("inserting compiled regex into cache")
339 | lrucache_set(regex_match_cache, key, compiled)
340 | end
341 | end
342 |
343 | return compiled, compile_once, flags
344 | end
345 |
346 |
347 | local function re_match_helper(subj, regex, opts, ctx, want_caps, res, nth)
348 | local compiled, compile_once, flags = re_match_compile(regex, opts)
349 | if compiled == nil then
350 | -- compiled_once holds the error string
351 | if not want_caps then
352 | return nil, nil, compile_once
353 | end
354 | return nil, compile_once
355 | end
356 |
357 | -- exec the compiled regex
358 |
359 | local rc
360 | do
361 | local pos
362 | if ctx then
363 | pos = ctx.pos
364 | if not pos or pos <= 0 then
365 | pos = 0
366 | else
367 | pos = pos - 1
368 | end
369 |
370 | else
371 | pos = 0
372 | end
373 |
374 | rc = C.ngx_http_lua_ffi_exec_regex(compiled, flags, subj, #subj, pos)
375 | end
376 |
377 | if rc == PCRE_ERROR_NOMATCH then
378 | if not compile_once then
379 | destroy_compiled_regex(compiled)
380 | end
381 | return nil
382 | end
383 |
384 | if rc < 0 then
385 | if not compile_once then
386 | destroy_compiled_regex(compiled)
387 | end
388 | if not want_caps then
389 | return nil, nil, "pcre_exec() failed: " .. rc
390 | end
391 | return nil, "pcre_exec() failed: " .. rc
392 | end
393 |
394 | if rc == 0 then
395 | if band(flags, FLAG_DFA) == 0 then
396 | if not want_caps then
397 | return nil, nil, "capture size too small"
398 | end
399 | return nil, "capture size too small"
400 | end
401 |
402 | rc = 1
403 | end
404 |
405 | -- print("cap 0: ", compiled.captures[0])
406 | -- print("cap 1: ", compiled.captures[1])
407 |
408 | if ctx then
409 | ctx.pos = compiled.captures[1] + 1
410 | end
411 |
412 | if not want_caps then
413 | if not nth or nth < 0 then
414 | nth = 0
415 | end
416 |
417 | if nth > compiled.ncaptures then
418 | return nil, nil, "nth out of bound"
419 | end
420 |
421 | if nth >= rc then
422 | return nil, nil
423 | end
424 |
425 | local from = compiled.captures[nth * 2] + 1
426 | local to = compiled.captures[nth * 2 + 1]
427 |
428 | if from < 0 or to < 0 then
429 | return nil, nil
430 | end
431 |
432 | return from, to
433 | end
434 |
435 | res = collect_captures(compiled, rc, subj, flags, res)
436 |
437 | if not compile_once then
438 | destroy_compiled_regex(compiled)
439 | end
440 |
441 | return res
442 | end
443 |
444 |
445 | function ngx.re.match(subj, regex, opts, ctx, res)
446 | return re_match_helper(subj, regex, opts, ctx, true, res)
447 | end
448 |
449 |
450 | function ngx.re.find(subj, regex, opts, ctx, nth)
451 | return re_match_helper(subj, regex, opts, ctx, false, nil, nth)
452 | end
453 |
454 |
455 | local function new_script_engine(subj, compiled, count)
456 | if not script_engine then
457 | script_engine = C.ngx_http_lua_ffi_create_script_engine()
458 | if script_engine == nil then
459 | return nil
460 | end
461 | ffi_gc(script_engine, C.ngx_http_lua_ffi_destroy_script_engine)
462 | end
463 |
464 | C.ngx_http_lua_ffi_init_script_engine(script_engine, subj, compiled,
465 | count)
466 | return script_engine
467 | end
468 |
469 |
470 | local function check_buf_size(buf, buf_size, pos, len, new_len, must_alloc)
471 | if new_len > buf_size then
472 | buf_size = buf_size * buf_grow_ratio
473 | if buf_size < new_len then
474 | buf_size = new_len
475 | end
476 | local new_buf = get_string_buf(buf_size, must_alloc)
477 | ffi_copy(new_buf, buf, len)
478 | buf = new_buf
479 | pos = buf + len
480 | end
481 | return buf, buf_size, pos, new_len
482 | end
483 |
484 |
485 | local function re_sub_compile(regex, opts, replace, func)
486 | local flags = 0
487 | local pcre_opts = 0
488 |
489 | if opts then
490 | flags, pcre_opts = parse_regex_opts(opts)
491 | else
492 | opts = ""
493 | end
494 |
495 | local compiled
496 | local compile_once = (band(flags, FLAG_COMPILE_ONCE) == 1)
497 | if compile_once then
498 | if func then
499 | local subcache = regex_sub_func_cache[opts]
500 | if subcache then
501 | -- print("cache hit!")
502 | compiled = subcache[regex]
503 | end
504 |
505 | else
506 | local subcache = regex_sub_str_cache[opts]
507 | if subcache then
508 | local subsubcache = subcache[regex]
509 | if subsubcache then
510 | -- print("cache hit!")
511 | compiled = subsubcache[replace]
512 | end
513 | end
514 | end
515 | end
516 |
517 | -- compile the regex
518 |
519 | if compiled == nil then
520 | -- print("compiled regex not found, compiling regex...")
521 | local errbuf = get_string_buf(MAX_ERR_MSG_LEN)
522 |
523 | compiled = C.ngx_http_lua_ffi_compile_regex(regex, #regex, flags,
524 | pcre_opts, errbuf,
525 | MAX_ERR_MSG_LEN)
526 |
527 | if compiled == nil then
528 | return nil, ffi_string(errbuf)
529 | end
530 |
531 | ffi_gc(compiled, C.ngx_http_lua_ffi_destroy_regex)
532 |
533 | if func == nil then
534 | local rc =
535 | C.ngx_http_lua_ffi_compile_replace_template(compiled,
536 | replace, #replace)
537 | if rc ~= 0 then
538 | if not compile_once then
539 | destroy_compiled_regex(compiled)
540 | end
541 | return nil, "failed to compile the replacement template"
542 | end
543 | end
544 |
545 | -- print("ncaptures: ", compiled.ncaptures)
546 |
547 | if compile_once then
548 | if regex_cache_size < get_max_regex_cache_size() then
549 | -- print("inserting compiled regex into cache")
550 | if func then
551 | local subcache = regex_sub_func_cache[opts]
552 | if not subcache then
553 | regex_sub_func_cache[opts] = {[regex] = compiled}
554 |
555 | else
556 | subcache[regex] = compiled
557 | end
558 |
559 | else
560 | local subcache = regex_sub_str_cache[opts]
561 | if not subcache then
562 | regex_sub_str_cache[opts] =
563 | {[regex] = {[replace] = compiled}}
564 |
565 | else
566 | local subsubcache = subcache[regex]
567 | if not subsubcache then
568 | subcache[regex] = {[replace] = compiled}
569 |
570 | else
571 | subsubcache[replace] = compiled
572 | end
573 | end
574 | end
575 |
576 | regex_cache_size = regex_cache_size + 1
577 | else
578 | compile_once = false
579 | end
580 | end
581 | end
582 |
583 | return compiled, compile_once, flags
584 | end
585 |
586 |
587 | local function re_sub_func_helper(subj, regex, replace, opts, global)
588 | local compiled, compile_once, flags =
589 | re_sub_compile(regex, opts, nil, replace)
590 | if not compiled then
591 | -- error string is in compile_once
592 | return nil, nil, compile_once
593 | end
594 |
595 | -- exec the compiled regex
596 |
597 | local subj_len = #subj
598 | local count = 0
599 | local pos = 0
600 | local cp_pos = 0
601 |
602 | local dst_buf_size = get_string_buf_size()
603 | -- Note: we have to always allocate the string buffer because
604 | -- the user might call whatever resty.core's API functions recursively
605 | -- in the user callback function.
606 | local dst_buf = get_string_buf(dst_buf_size, true)
607 | local dst_pos = dst_buf
608 | local dst_len = 0
609 |
610 | while true do
611 | local rc = C.ngx_http_lua_ffi_exec_regex(compiled, flags, subj,
612 | subj_len, pos)
613 | if rc == PCRE_ERROR_NOMATCH then
614 | break
615 | end
616 |
617 | if rc < 0 then
618 | if not compile_once then
619 | destroy_compiled_regex(compiled)
620 | end
621 | return nil, nil, "pcre_exec() failed: " .. rc
622 | end
623 |
624 | if rc == 0 then
625 | if band(flags, FLAG_DFA) == 0 then
626 | if not compile_once then
627 | destroy_compiled_regex(compiled)
628 | end
629 | return nil, nil, "capture size too small"
630 | end
631 |
632 | rc = 1
633 | end
634 |
635 | count = count + 1
636 | local prefix_len = compiled.captures[0] - cp_pos
637 |
638 | local res = collect_captures(compiled, rc, subj, flags)
639 |
640 | local bit = replace(res)
641 | local bit_len = #bit
642 |
643 | local new_dst_len = dst_len + prefix_len + bit_len
644 | dst_buf, dst_buf_size, dst_pos, dst_len =
645 | check_buf_size(dst_buf, dst_buf_size, dst_pos, dst_len,
646 | new_dst_len, true)
647 |
648 | if prefix_len > 0 then
649 | ffi_copy(dst_pos, ffi_cast(c_str_type, subj) + cp_pos,
650 | prefix_len)
651 | dst_pos = dst_pos + prefix_len
652 | end
653 |
654 | if bit_len > 0 then
655 | ffi_copy(dst_pos, bit, bit_len)
656 | dst_pos = dst_pos + bit_len
657 | end
658 |
659 | cp_pos = compiled.captures[1]
660 | pos = cp_pos
661 | if pos == compiled.captures[0] then
662 | pos = pos + 1
663 | if pos > subj_len then
664 | break
665 | end
666 | end
667 |
668 | if not global then
669 | break
670 | end
671 | end
672 |
673 | if not compile_once then
674 | destroy_compiled_regex(compiled)
675 | end
676 |
677 | if count > 0 then
678 | if pos < subj_len then
679 | local suffix_len = subj_len - cp_pos
680 |
681 | local new_dst_len = dst_len + suffix_len
682 | dst_buf, dst_buf_size, dst_pos, dst_len =
683 | check_buf_size(dst_buf, dst_buf_size, dst_pos, dst_len,
684 | new_dst_len, true)
685 |
686 | ffi_copy(dst_pos, ffi_cast(c_str_type, subj) + cp_pos,
687 | suffix_len)
688 | end
689 | return ffi_string(dst_buf, dst_len), count
690 | end
691 |
692 | return subj, 0
693 | end
694 |
695 |
696 | local function re_sub_str_helper(subj, regex, replace, opts, global)
697 | local compiled, compile_once, flags =
698 | re_sub_compile(regex, opts, replace, nil)
699 | if not compiled then
700 | -- error string is in compile_once
701 | return nil, nil, compile_once
702 | end
703 |
704 | -- exec the compiled regex
705 |
706 | local subj_len = #subj
707 | local count = 0
708 | local pos = 0
709 | local cp_pos = 0
710 |
711 | local dst_buf_size = get_string_buf_size()
712 | local dst_buf = get_string_buf(dst_buf_size)
713 | local dst_pos = dst_buf
714 | local dst_len = 0
715 |
716 | while true do
717 | local rc = C.ngx_http_lua_ffi_exec_regex(compiled, flags, subj,
718 | subj_len, pos)
719 | if rc == PCRE_ERROR_NOMATCH then
720 | break
721 | end
722 |
723 | if rc < 0 then
724 | if not compile_once then
725 | destroy_compiled_regex(compiled)
726 | end
727 | return nil, nil, "pcre_exec() failed: " .. rc
728 | end
729 |
730 | if rc == 0 then
731 | if band(flags, FLAG_DFA) == 0 then
732 | if not compile_once then
733 | destroy_compiled_regex(compiled)
734 | end
735 | return nil, nil, "capture size too small"
736 | end
737 |
738 | rc = 1
739 | end
740 |
741 | count = count + 1
742 | local prefix_len = compiled.captures[0] - cp_pos
743 |
744 | local cv = compiled.replace
745 | if cv.lengths ~= nil then
746 | local e = new_script_engine(subj, compiled, rc)
747 | if e == nil then
748 | return nil, nil, "failed to create script engine"
749 | end
750 |
751 | local bit_len = C.ngx_http_lua_ffi_script_eval_len(e, cv)
752 | local new_dst_len = dst_len + prefix_len + bit_len
753 | dst_buf, dst_buf_size, dst_pos, dst_len =
754 | check_buf_size(dst_buf, dst_buf_size, dst_pos, dst_len,
755 | new_dst_len)
756 |
757 | if prefix_len > 0 then
758 | ffi_copy(dst_pos, ffi_cast(c_str_type, subj) + cp_pos,
759 | prefix_len)
760 | dst_pos = dst_pos + prefix_len
761 | end
762 |
763 | if bit_len > 0 then
764 | C.ngx_http_lua_ffi_script_eval_data(e, cv, dst_pos)
765 | dst_pos = dst_pos + bit_len
766 | end
767 |
768 | else
769 | local bit_len = cv.value.len
770 |
771 | dst_buf, dst_buf_size, dst_pos, dst_len =
772 | check_buf_size(dst_buf, dst_buf_size, dst_pos, dst_len,
773 | dst_len + prefix_len + bit_len)
774 |
775 | if prefix_len > 0 then
776 | ffi_copy(dst_pos, ffi_cast(c_str_type, subj) + cp_pos,
777 | prefix_len)
778 | dst_pos = dst_pos + prefix_len
779 | end
780 |
781 | if bit_len > 0 then
782 | ffi_copy(dst_pos, cv.value.data, bit_len)
783 | dst_pos = dst_pos + bit_len
784 | end
785 | end
786 |
787 | cp_pos = compiled.captures[1]
788 | pos = cp_pos
789 | if pos == compiled.captures[0] then
790 | pos = pos + 1
791 | if pos > subj_len then
792 | break
793 | end
794 | end
795 |
796 | if not global then
797 | break
798 | end
799 | end
800 |
801 | if not compile_once then
802 | destroy_compiled_regex(compiled)
803 | end
804 |
805 | if count > 0 then
806 | if pos < subj_len then
807 | local suffix_len = subj_len - cp_pos
808 |
809 | local new_dst_len = dst_len + suffix_len
810 | dst_buf, dst_buf_size, dst_pos, dst_len =
811 | check_buf_size(dst_buf, dst_buf_size, dst_pos, dst_len,
812 | new_dst_len)
813 |
814 | ffi_copy(dst_pos, ffi_cast(c_str_type, subj) + cp_pos,
815 | suffix_len)
816 | end
817 | return ffi_string(dst_buf, dst_len), count
818 | end
819 |
820 | return subj, 0
821 | end
822 |
823 |
824 | local function re_sub_helper(subj, regex, replace, opts, global)
825 | local repl_type = type(replace)
826 | if repl_type == "function" then
827 | return re_sub_func_helper(subj, regex, replace, opts, global)
828 | end
829 |
830 | if repl_type ~= "string" then
831 | replace = tostring(replace)
832 | end
833 |
834 | return re_sub_str_helper(subj, regex, replace, opts, global)
835 | end
836 |
837 |
838 | function ngx.re.sub(subj, regex, replace, opts)
839 | return re_sub_helper(subj, regex, replace, opts, false)
840 | end
841 |
842 |
843 | function ngx.re.gsub(subj, regex, replace, opts)
844 | return re_sub_helper(subj, regex, replace, opts, true)
845 | end
846 |
847 |
848 | return _M
849 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/request.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 |
8 | local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
9 | local FFI_DECLINED = base.FFI_DECLINED
10 | local FFI_OK = base.FFI_OK
11 | local new_tab = base.new_tab
12 | local C = ffi.C
13 | local ffi_cast = ffi.cast
14 | local ffi_str = ffi.string
15 | local get_string_buf = base.get_string_buf
16 | local get_size_ptr = base.get_size_ptr
17 | local setmetatable = setmetatable
18 | local gsub = ngx.re.gsub
19 | local lower = string.lower
20 | local rawget = rawget
21 | local ngx = ngx
22 | local getfenv = getfenv
23 | local type = type
24 | local error = error
25 | local tostring = tostring
26 | local tonumber = tonumber
27 |
28 |
29 | ffi.cdef[[
30 | typedef struct {
31 | ngx_http_lua_ffi_str_t key;
32 | ngx_http_lua_ffi_str_t value;
33 | } ngx_http_lua_ffi_table_elt_t;
34 |
35 | int ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r,
36 | int max);
37 |
38 | int ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r,
39 | ngx_http_lua_ffi_table_elt_t *out, int count, int raw);
40 |
41 | int ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r,
42 | int max);
43 |
44 | size_t ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r);
45 |
46 | int ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r,
47 | unsigned char *buf, ngx_http_lua_ffi_table_elt_t *out, int count);
48 |
49 | double ngx_http_lua_ffi_req_start_time(ngx_http_request_t *r);
50 |
51 | int ngx_http_lua_ffi_req_get_method(ngx_http_request_t *r);
52 |
53 | int ngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r,
54 | char *name, size_t *len);
55 |
56 | int ngx_http_lua_ffi_req_set_method(ngx_http_request_t *r, int method);
57 |
58 | int ngx_http_lua_ffi_req_header_set_single_value(ngx_http_request_t *r,
59 | const unsigned char *key, size_t key_len, const unsigned char *value,
60 | size_t value_len);
61 | ]]
62 |
63 |
64 | local table_elt_type = ffi.typeof("ngx_http_lua_ffi_table_elt_t*")
65 | local table_elt_size = ffi.sizeof("ngx_http_lua_ffi_table_elt_t")
66 | local req_headers_mt = {
67 | __index = function (tb, key)
68 | return rawget(tb, (gsub(lower(key), '_', '-', "jo")))
69 | end
70 | }
71 |
72 |
73 | function ngx.req.get_headers(max_headers, raw)
74 | local r = getfenv(0).__ngx_req
75 | if not r then
76 | return error("no request found")
77 | end
78 |
79 | if not max_headers then
80 | max_headers = -1
81 | end
82 |
83 | if not raw then
84 | raw = 0
85 | else
86 | raw = 1
87 | end
88 |
89 | local n = C.ngx_http_lua_ffi_req_get_headers_count(r, max_headers)
90 | if n == FFI_BAD_CONTEXT then
91 | return error("API disabled in the current context")
92 | end
93 |
94 | if n == 0 then
95 | return {}
96 | end
97 |
98 | local raw_buf = get_string_buf(n * table_elt_size)
99 | local buf = ffi_cast(table_elt_type, raw_buf)
100 |
101 | local rc = C.ngx_http_lua_ffi_req_get_headers(r, buf, n, raw)
102 | if rc == 0 then
103 | local headers = new_tab(0, n)
104 | for i = 0, n - 1 do
105 | local h = buf[i]
106 |
107 | local key = h.key
108 | key = ffi_str(key.data, key.len)
109 |
110 | local value = h.value
111 | value = ffi_str(value.data, value.len)
112 |
113 | local existing = headers[key]
114 | if existing then
115 | if type(existing) == "table" then
116 | existing[#existing + 1] = value
117 | else
118 | headers[key] = {existing, value}
119 | end
120 |
121 | else
122 | headers[key] = value
123 | end
124 | end
125 | if raw == 0 then
126 | return setmetatable(headers, req_headers_mt)
127 | end
128 | return headers
129 | end
130 |
131 | return nil
132 | end
133 |
134 |
135 | function ngx.req.get_uri_args(max_args)
136 | local r = getfenv(0).__ngx_req
137 | if not r then
138 | return error("no request found")
139 | end
140 |
141 | if not max_args then
142 | max_args = -1
143 | end
144 |
145 | local n = C.ngx_http_lua_ffi_req_get_uri_args_count(r, max_args)
146 | if n == FFI_BAD_CONTEXT then
147 | return error("API disabled in the current context")
148 | end
149 |
150 | if n == 0 then
151 | return {}
152 | end
153 |
154 | local args_len = C.ngx_http_lua_ffi_req_get_querystring_len(r)
155 |
156 | local strbuf = get_string_buf(args_len + n * table_elt_size)
157 | local kvbuf = ffi_cast(table_elt_type, strbuf + args_len)
158 |
159 | local nargs = C.ngx_http_lua_ffi_req_get_uri_args(r, strbuf, kvbuf, n)
160 |
161 | local args = new_tab(0, nargs)
162 | for i = 0, nargs - 1 do
163 | local arg = kvbuf[i]
164 |
165 | local key = arg.key
166 | key = ffi_str(key.data, key.len)
167 |
168 | local value = arg.value
169 | local len = value.len
170 | if len == -1 then
171 | value = true
172 | else
173 | value = ffi_str(value.data, len)
174 | end
175 |
176 | local existing = args[key]
177 | if existing then
178 | if type(existing) == "table" then
179 | existing[#existing + 1] = value
180 | else
181 | args[key] = {existing, value}
182 | end
183 |
184 | else
185 | args[key] = value
186 | end
187 | end
188 | return args
189 | end
190 |
191 |
192 | function ngx.req.start_time()
193 | local r = getfenv(0).__ngx_req
194 | if not r then
195 | return error("no request found")
196 | end
197 |
198 | return tonumber(C.ngx_http_lua_ffi_req_start_time(r))
199 | end
200 |
201 |
202 | do
203 | local methods = {
204 | [0x0002] = "GET",
205 | [0x0004] = "HEAD",
206 | [0x0008] = "POST",
207 | [0x0010] = "PUT",
208 | [0x0020] = "DELETE",
209 | [0x0040] = "MKCOL",
210 | [0x0080] = "COPY",
211 | [0x0100] = "MOVE",
212 | [0x0200] = "OPTIONS",
213 | [0x0400] = "PROPFIND",
214 | [0x0800] = "PROPPATCH",
215 | [0x1000] = "LOCK",
216 | [0x2000] = "UNLOCK",
217 | [0x4000] = "PATCH",
218 | [0x8000] = "TRACE",
219 | }
220 |
221 | function ngx.req.get_method()
222 | local r = getfenv(0).__ngx_req
223 | if not r then
224 | return error("no request found")
225 | end
226 |
227 | do
228 | local id = C.ngx_http_lua_ffi_req_get_method(r)
229 | if id == FFI_BAD_CONTEXT then
230 | return error("API disabled in the current context")
231 | end
232 |
233 | local method = methods[id]
234 | if method then
235 | return method
236 | end
237 | end
238 |
239 | local buf = get_string_buf(32)
240 | local sizep = get_size_ptr()
241 | sizep[0] = 32
242 |
243 | local rc = C.ngx_http_lua_ffi_req_get_method_name(r, buf, sizep)
244 | if rc ~= 0 then
245 | return nil
246 | end
247 |
248 | return ffi_str(buf, sizep[0])
249 | end
250 | end -- do
251 |
252 |
253 | function ngx.req.set_method(method)
254 | local r = getfenv(0).__ngx_req
255 | if not r then
256 | return error("no request found")
257 | end
258 |
259 | if type(method) ~= "number" then
260 | return error("bad method number")
261 | end
262 |
263 | local rc = C.ngx_http_lua_ffi_req_set_method(r, method)
264 | if rc == FFI_OK then
265 | return
266 | end
267 |
268 | if rc == FFI_BAD_CONTEXT then
269 | return error("API disabled in the current context")
270 | end
271 |
272 | if rc == FFI_DECLINED then
273 | return error("unsupported HTTP method: " .. method)
274 | end
275 |
276 | return error("unknown error: " .. rc)
277 | end
278 |
279 |
280 | do
281 | local orig_func = ngx.req.set_header
282 |
283 | function ngx.req.set_header(name, value)
284 | if type(value) == "table" then
285 | return orig_func(name, value)
286 | end
287 |
288 | local r = getfenv(0).__ngx_req
289 | if not r then
290 | return error("no request found")
291 | end
292 |
293 | if type(name) ~= "string" then
294 | name = tostring(name)
295 | end
296 |
297 | local rc
298 | if not value then
299 | rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name,
300 | #name, nil, 0)
301 |
302 | else
303 | if type(value) ~= "string" then
304 | value = tostring(value)
305 | end
306 |
307 | rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name,
308 | #name, value, #value)
309 | end
310 |
311 | if rc == FFI_OK or rc == FFI_DECLINED then
312 | return
313 | end
314 |
315 | if rc == FFI_BAD_CONTEXT then
316 | return error("API disabled in the current context")
317 | end
318 |
319 | return error("error")
320 | end
321 | end -- do
322 |
323 |
324 | function ngx.req.clear_header(name, value)
325 | local r = getfenv(0).__ngx_req
326 | if not r then
327 | return error("no request found")
328 | end
329 |
330 | if type(name) ~= "string" then
331 | name = tostring(name)
332 | end
333 |
334 | local rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name, #name,
335 | nil, 0)
336 |
337 | if rc == FFI_OK or rc == FFI_DECLINED then
338 | return
339 | end
340 |
341 | if rc == FFI_BAD_CONTEXT then
342 | return error("API disabled in the current context")
343 | end
344 |
345 | return error("error")
346 | end
347 |
348 |
349 | return {
350 | version = base.version
351 | }
352 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/response.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 |
8 | local C = ffi.C
9 | local ffi_new = ffi.new
10 | local ffi_cast = ffi.cast
11 | local ffi_str = ffi.string
12 | local new_tab = base.new_tab
13 | local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
14 | local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
15 | -- local FFI_ERROR = base.FFI_ERROR
16 | local FFI_DECLINED = base.FFI_DECLINED
17 | local get_string_buf = base.get_string_buf
18 | local getmetatable = getmetatable
19 | local type = type
20 | local tostring = tostring
21 | local getfenv = getfenv
22 | local error = error
23 | local ngx = ngx
24 |
25 |
26 | local MAX_HEADER_VALUES = 100
27 | local errmsg = base.get_errmsg_ptr()
28 | local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
29 | local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
30 |
31 |
32 | ffi.cdef[[
33 | int ngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r,
34 | const char *key_data, size_t key_len, int is_nil,
35 | const char *sval, size_t sval_len, ngx_http_lua_ffi_str_t *mvals,
36 | size_t mvals_len, char **errmsg);
37 |
38 | int ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r,
39 | const unsigned char *key, size_t key_len,
40 | unsigned char *key_buf, ngx_http_lua_ffi_str_t *values,
41 | int max_nvalues);
42 | ]]
43 |
44 |
45 | local function set_resp_header(tb, key, value)
46 | local r = getfenv(0).__ngx_req
47 | if not r then
48 | return error("no request found")
49 | end
50 |
51 | if type(key) ~= "string" then
52 | key = tostring(key)
53 | end
54 |
55 | local rc
56 | if value == nil then
57 | rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, true, nil, 0,
58 | nil, 0, errmsg)
59 | else
60 | local sval, sval_len, mvals, mvals_len, buf
61 |
62 | if type(value) == "table" then
63 | mvals_len = #value
64 | buf = get_string_buf(ffi_str_size * mvals_len)
65 | mvals = ffi_cast(ffi_str_type, buf)
66 | for i = 1, mvals_len do
67 | local s = value[i]
68 | if type(s) ~= "string" then
69 | s = tostring(s)
70 | value[i] = s
71 | end
72 | local str = mvals[i - 1]
73 | str.data = s
74 | str.len = #s
75 | end
76 |
77 | sval_len = 0
78 |
79 | else
80 | if type(value) ~= "string" then
81 | sval = tostring(value)
82 | else
83 | sval = value
84 | end
85 | sval_len = #sval
86 |
87 | mvals_len = 0
88 | end
89 |
90 | rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, false, sval,
91 | sval_len, mvals, mvals_len,
92 | errmsg)
93 | end
94 |
95 | if rc == 0 or rc == FFI_DECLINED then
96 | return
97 | end
98 |
99 | if rc == FFI_NO_REQ_CTX then
100 | return error("no request ctx found")
101 | end
102 |
103 | if rc == FFI_BAD_CONTEXT then
104 | return error("API disabled in the current context")
105 | end
106 |
107 | -- rc == FFI_ERROR
108 | return error(ffi_str(errmsg[0]))
109 | end
110 |
111 |
112 | local function get_resp_header(tb, key)
113 | local r = getfenv(0).__ngx_req
114 | if not r then
115 | return error("no request found")
116 | end
117 |
118 | if type(key) ~= "string" then
119 | key = tostring(key)
120 | end
121 |
122 | local key_len = #key
123 |
124 | local key_buf = get_string_buf(key_len + ffi_str_size * MAX_HEADER_VALUES)
125 | local values = ffi_cast(ffi_str_type, key_buf + key_len)
126 | local n = C.ngx_http_lua_ffi_get_resp_header(r, key, key_len, key_buf,
127 | values, MAX_HEADER_VALUES)
128 |
129 | -- print("retval: ", n)
130 |
131 | if n == FFI_BAD_CONTEXT then
132 | return error("API disabled in the current context")
133 | end
134 |
135 | if n == 0 then
136 | return nil
137 | end
138 |
139 | if n == 1 then
140 | local v = values[0]
141 | return ffi_str(v.data, v.len)
142 | end
143 |
144 | if n > 0 then
145 | local ret = new_tab(n, 0)
146 | for i = 1, n do
147 | local v = values[i - 1]
148 | ret[i] = ffi_str(v.data, v.len)
149 | end
150 | return ret
151 | end
152 |
153 | -- n == FFI_ERROR
154 | return error("no memory")
155 | end
156 |
157 |
158 | do
159 | local mt = getmetatable(ngx.header)
160 | mt.__newindex = set_resp_header
161 | mt.__index = get_resp_header
162 | end
163 |
164 |
165 | return {
166 | version = base.version
167 | }
168 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/shdict.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 | local ffi_new = ffi.new
8 | local ffi_str = ffi.string
9 | local C = ffi.C
10 | local get_string_buf = base.get_string_buf
11 | local get_string_buf_size = base.get_string_buf_size
12 | local get_size_ptr = base.get_size_ptr
13 | local tonumber = tonumber
14 | local tostring = tostring
15 | local next = next
16 | local type = type
17 | local error = error
18 | local ngx_shared = ngx.shared
19 | local getmetatable = getmetatable
20 |
21 |
22 | ffi.cdef[[
23 | int ngx_http_lua_ffi_shdict_get(void *zone, const unsigned char *key,
24 | size_t key_len, int *value_type, unsigned char **str_value_buf,
25 | size_t *str_value_len, double *num_value, int *user_flags,
26 | int get_stale, int *is_stale);
27 |
28 | int ngx_http_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
29 | size_t key_len, double *value, char **err);
30 |
31 | int ngx_http_lua_ffi_shdict_store(void *zone, int op,
32 | const unsigned char *key, size_t key_len, int value_type,
33 | const unsigned char *str_value_buf, size_t str_value_len,
34 | double num_value, int exptime, int user_flags, char **errmsg,
35 | int *forcible);
36 |
37 | int ngx_http_lua_ffi_shdict_flush_all(void *zone);
38 | ]]
39 |
40 |
41 | if not pcall(function () return C.free end) then
42 | ffi.cdef[[
43 | void free(void *ptr);
44 | ]]
45 | end
46 |
47 |
48 | local value_type = ffi_new("int[1]")
49 | local user_flags = ffi_new("int[1]")
50 | local num_value = ffi_new("double[1]")
51 | local is_stale = ffi_new("int[1]")
52 | local forcible = ffi_new("int[1]")
53 | local str_value_buf = ffi_new("unsigned char *[1]")
54 | local errmsg = base.get_errmsg_ptr()
55 |
56 |
57 | local function shdict_store(zone, op, key, value, exptime, flags)
58 | if not zone or type(zone) ~= "userdata" then
59 | return error('bad "zone" argument')
60 | end
61 |
62 | if not exptime then
63 | exptime = 0
64 | end
65 |
66 | if not flags then
67 | flags = 0
68 | end
69 |
70 | if key == nil then
71 | return nil, "nil key"
72 | end
73 |
74 | if type(key) ~= "string" then
75 | key = tostring(key)
76 | end
77 |
78 | local key_len = #key
79 | if key_len == 0 then
80 | return nil, "empty key"
81 | end
82 | if key_len > 65535 then
83 | return nil, "key too long"
84 | end
85 |
86 | local str_value_buf
87 | local str_value_len = 0
88 | local num_value = 0
89 | local valtyp = type(value)
90 |
91 | -- print("value type: ", valtyp)
92 | -- print("exptime: ", exptime)
93 |
94 | if valtyp == "string" then
95 | valtyp = 4 -- LUA_TSTRING
96 | str_value_buf = value
97 | str_value_len = #value
98 |
99 | elseif valtyp == "number" then
100 | valtyp = 3 -- LUA_TNUMBER
101 | num_value = value
102 |
103 | elseif value == nil then
104 | valtyp = 0 -- LUA_TNIL
105 |
106 | elseif valtyp == "boolean" then
107 | valtyp = 1 -- LUA_TBOOLEAN
108 | num_value = value and 1 or 0
109 |
110 | else
111 | return nil, "bad value type"
112 | end
113 |
114 | local rc = C.ngx_http_lua_ffi_shdict_store(zone, op, key, key_len,
115 | valtyp, str_value_buf,
116 | str_value_len, num_value,
117 | exptime * 1000, flags, errmsg,
118 | forcible)
119 |
120 | -- print("rc == ", rc)
121 |
122 | if rc == 0 then -- NGX_OK
123 | return true, nil, forcible[0] == 1
124 | end
125 |
126 | -- NGX_DECLINED or NGX_ERROR
127 | return false, ffi_str(errmsg[0]), forcible[0] == 1
128 | end
129 |
130 |
131 | local function shdict_set(zone, key, value, exptime, flags)
132 | return shdict_store(zone, 0, key, value, exptime, flags)
133 | end
134 |
135 |
136 | local function shdict_safe_set(zone, key, value, exptime, flags)
137 | return shdict_store(zone, 0x0004, key, value, exptime, flags)
138 | end
139 |
140 |
141 | local function shdict_add(zone, key, value, exptime, flags)
142 | return shdict_store(zone, 0x0001, key, value, exptime, flags)
143 | end
144 |
145 |
146 | local function shdict_safe_add(zone, key, value, exptime, flags)
147 | return shdict_store(zone, 0x0005, key, value, exptime, flags)
148 | end
149 |
150 |
151 | local function shdict_replace(zone, key, value, exptime, flags)
152 | return shdict_store(zone, 0x0002, key, value, exptime, flags)
153 | end
154 |
155 |
156 | local function shdict_delete(zone, key)
157 | return shdict_set(zone, key, nil)
158 | end
159 |
160 |
161 | local function shdict_get(zone, key)
162 | if not zone or type(zone) ~= "userdata" then
163 | return error('bad "zone" argument')
164 | end
165 |
166 | if key == nil then
167 | return nil, "nil key"
168 | end
169 |
170 | if type(key) ~= "string" then
171 | key = tostring(key)
172 | end
173 |
174 | local key_len = #key
175 | if key_len == 0 then
176 | return nil, "empty key"
177 | end
178 | if key_len > 65535 then
179 | return nil, "key too long"
180 | end
181 |
182 | local size = get_string_buf_size()
183 | local buf = get_string_buf(size)
184 | str_value_buf[0] = buf
185 | local value_len = get_size_ptr()
186 | value_len[0] = size
187 |
188 | local rc = C.ngx_http_lua_ffi_shdict_get(zone, key, key_len, value_type,
189 | str_value_buf, value_len,
190 | num_value, user_flags, 0,
191 | is_stale)
192 | if rc ~= 0 then
193 | return error("failed to get the key")
194 | end
195 |
196 | local typ = value_type[0]
197 |
198 | if typ == 0 then -- LUA_TNIL
199 | return nil
200 | end
201 |
202 | local flags = tonumber(user_flags[0])
203 |
204 | local val
205 |
206 | if typ == 4 then -- LUA_TSTRING
207 | if str_value_buf[0] ~= buf then
208 | -- ngx.say("len: ", tonumber(value_len[0]))
209 | buf = str_value_buf[0]
210 | val = ffi_str(buf, value_len[0])
211 | C.free(buf)
212 | else
213 | val = ffi_str(buf, value_len[0])
214 | end
215 |
216 | elseif typ == 3 then -- LUA_TNUMBER
217 | val = tonumber(num_value[0])
218 |
219 | elseif typ == 1 then -- LUA_TBOOLEAN
220 | val = (tonumber(buf[0]) ~= 0)
221 |
222 | else
223 | return error("unknown value type: " .. typ)
224 | end
225 |
226 | if flags ~= 0 then
227 | return val, flags
228 | end
229 |
230 | return val
231 | end
232 |
233 |
234 | local function shdict_get_stale(zone, key)
235 | if not zone or type(zone) ~= "userdata" then
236 | return error("bad \"zone\" argument")
237 | end
238 |
239 | if key == nil then
240 | return nil, "nil key"
241 | end
242 |
243 | if type(key) ~= "string" then
244 | key = tostring(key)
245 | end
246 |
247 | local key_len = #key
248 | if key_len == 0 then
249 | return nil, "empty key"
250 | end
251 | if key_len > 65535 then
252 | return nil, "key too long"
253 | end
254 |
255 | local size = get_string_buf_size()
256 | local buf = get_string_buf(size)
257 | str_value_buf[0] = buf
258 | local value_len = get_size_ptr()
259 | value_len[0] = size
260 |
261 | local rc = C.ngx_http_lua_ffi_shdict_get(zone, key, key_len, value_type,
262 | str_value_buf, value_len,
263 | num_value, user_flags, 1,
264 | is_stale)
265 | if rc ~= 0 then
266 | return error("failed to get the key")
267 | end
268 |
269 | local typ = value_type[0]
270 |
271 | if typ == 0 then -- LUA_TNIL
272 | return nil
273 | end
274 |
275 | local flags = tonumber(user_flags[0])
276 | local val
277 |
278 | if typ == 4 then -- LUA_TSTRING
279 | if str_value_buf[0] ~= buf then
280 | -- ngx.say("len: ", tonumber(value_len[0]))
281 | buf = str_value_buf[0]
282 | val = ffi_str(buf, value_len[0])
283 | C.free(buf)
284 | else
285 | val = ffi_str(buf, value_len[0])
286 | end
287 |
288 | elseif typ == 3 then -- LUA_TNUMBER
289 | val = tonumber(num_value[0])
290 |
291 | elseif typ == 1 then -- LUA_TBOOLEAN
292 | val = (tonumber(buf[0]) ~= 0)
293 |
294 | else
295 | return error("unknown value type: " .. typ)
296 | end
297 |
298 | if flags ~= 0 then
299 | return val, flags, is_stale[0] == 1
300 | end
301 |
302 | return val, nil, is_stale[0] == 1
303 | end
304 |
305 |
306 | local function shdict_incr(zone, key, value)
307 | if not zone or type(zone) ~= "userdata" then
308 | return error("bad \"zone\" argument")
309 | end
310 |
311 | if key == nil then
312 | return nil, "nil key"
313 | end
314 |
315 | if type(key) ~= "string" then
316 | key = tostring(key)
317 | end
318 |
319 | local key_len = #key
320 | if key_len == 0 then
321 | return nil, "empty key"
322 | end
323 | if key_len > 65535 then
324 | return nil, "key too long"
325 | end
326 |
327 | if type(value) ~= "number" then
328 | value = tonumber(value)
329 | end
330 | num_value[0] = value
331 |
332 | local rc = C.ngx_http_lua_ffi_shdict_incr(zone, key, key_len, num_value,
333 | errmsg)
334 | if rc ~= 0 then -- ~= NGX_OK
335 | return nil, ffi_str(errmsg[0])
336 | end
337 |
338 | return tonumber(num_value[0])
339 | end
340 |
341 |
342 | local function shdict_flush_all(zone)
343 | if not zone or type(zone) ~= "userdata" then
344 | return error("bad \"zone\" argument")
345 | end
346 |
347 | C.ngx_http_lua_ffi_shdict_flush_all(zone)
348 | end
349 |
350 |
351 | if ngx_shared then
352 | local name, dict = next(ngx_shared, nil)
353 | if dict then
354 | local mt = getmetatable(dict)
355 | if mt then
356 | mt = mt.__index
357 | if mt then
358 | mt.get = shdict_get
359 | mt.get_stale = shdict_get_stale
360 | mt.incr = shdict_incr
361 | mt.set = shdict_set
362 | mt.safe_set = shdict_safe_set
363 | mt.add = shdict_add
364 | mt.safe_add = shdict_safe_add
365 | mt.replace = shdict_replace
366 | mt.delete = shdict_delete
367 | mt.flush_all = shdict_flush_all
368 | end
369 | end
370 | end
371 | end
372 |
373 |
374 | return {
375 | version = base.version
376 | }
377 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/time.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 |
8 | local tonumber = tonumber
9 | local C = ffi.C
10 | local ngx = ngx
11 |
12 |
13 | ffi.cdef[[
14 | double ngx_http_lua_ffi_now(void);
15 | long ngx_http_lua_ffi_time(void);
16 | ]]
17 |
18 |
19 | function ngx.now()
20 | return tonumber(C.ngx_http_lua_ffi_now())
21 | end
22 |
23 |
24 | function ngx.time()
25 | return tonumber(C.ngx_http_lua_ffi_time())
26 | end
27 |
28 |
29 | return {
30 | version = base.version
31 | }
32 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/uri.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local ffi_string = ffi.string
6 | local ffi_new = ffi.new
7 | local C = ffi.C
8 | local setmetatable = setmetatable
9 | local ngx = ngx
10 | local type = type
11 | local tostring = tostring
12 | local error = error
13 | local base = require "resty.core.base"
14 | local get_string_buf = base.get_string_buf
15 | local get_size_ptr = base.get_size_ptr
16 | local print = print
17 | local tonumber = tonumber
18 |
19 |
20 | ffi.cdef[[
21 | size_t ngx_http_lua_ffi_uri_escaped_length(const unsigned char *src,
22 | size_t len);
23 |
24 | void ngx_http_lua_ffi_escape_uri(const unsigned char *src, size_t len,
25 | unsigned char *dst);
26 |
27 | size_t ngx_http_lua_ffi_unescape_uri(const unsigned char *src,
28 | size_t len, unsigned char *dst);
29 | ]]
30 |
31 |
32 | ngx.escape_uri = function (s)
33 | if type(s) ~= 'string' then
34 | if not s then
35 | s = ''
36 | else
37 | s = tostring(s)
38 | end
39 | end
40 | local slen = #s
41 | local dlen = C.ngx_http_lua_ffi_uri_escaped_length(s, slen)
42 | -- print("dlen: ", tonumber(dlen))
43 | if dlen == slen then
44 | return s
45 | end
46 | local dst = get_string_buf(dlen)
47 | C.ngx_http_lua_ffi_escape_uri(s, slen, dst)
48 | return ffi_string(dst, dlen)
49 | end
50 |
51 |
52 | ngx.unescape_uri = function (s)
53 | if type(s) ~= 'string' then
54 | if not s then
55 | s = ''
56 | else
57 | s = tostring(s)
58 | end
59 | end
60 | local slen = #s
61 | local dlen = slen
62 | local dst = get_string_buf(dlen)
63 | dlen = C.ngx_http_lua_ffi_unescape_uri(s, slen, dst)
64 | return ffi_string(dst, dlen)
65 | end
66 |
67 |
68 | return {
69 | version = base.version,
70 | }
71 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/var.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 | local ffi_new = ffi.new
8 | local ffi_str = ffi.string
9 | local C = ffi.C
10 | local type = type
11 | local getfenv = getfenv
12 | local get_string_buf = base.get_string_buf
13 | local get_size_ptr = base.get_size_ptr
14 | local error = error
15 | local tostring = tostring
16 | local ngx_var = ngx.var
17 | local getmetatable = getmetatable
18 |
19 |
20 | ffi.cdef[[
21 | int ngx_http_lua_ffi_var_get(ngx_http_request_t *r,
22 | const char *name_data, size_t name_len, char *lowcase_buf,
23 | int capture_id, char **value, size_t *value_len, char **err);
24 |
25 | int ngx_http_lua_ffi_var_set(ngx_http_request_t *r,
26 | const unsigned char *name_data, size_t name_len,
27 | unsigned char *lowcase_buf, const unsigned char *value,
28 | size_t value_len, unsigned char *errbuf, size_t errlen);
29 | ]]
30 |
31 |
32 | local value_ptr = ffi_new("unsigned char *[1]")
33 | local errmsg = base.get_errmsg_ptr()
34 |
35 |
36 | local function var_get(self, name)
37 | local r = getfenv(0).__ngx_req
38 | if not r then
39 | return error("no request found")
40 | end
41 |
42 | local value_len = get_size_ptr()
43 | local rc
44 | if type(name) == "number" then
45 | rc = C.ngx_http_lua_ffi_var_get(r, nil, 0, nil, name, value_ptr,
46 | value_len, errmsg)
47 |
48 | else
49 | if type(name) ~= "string" then
50 | return error("bad variable name")
51 | end
52 |
53 | local name_len = #name
54 | local lowcase_buf = get_string_buf(name_len)
55 |
56 | rc = C.ngx_http_lua_ffi_var_get(r, name, name_len, lowcase_buf, 0,
57 | value_ptr, value_len, errmsg)
58 | end
59 |
60 | -- ngx.log(ngx.WARN, "rc = ", rc)
61 |
62 | if rc == 0 then -- NGX_OK
63 | return ffi_str(value_ptr[0], value_len[0])
64 | end
65 |
66 | if rc == -5 then -- NGX_DECLINED
67 | return nil
68 | end
69 |
70 | if rc == -1 then -- NGX_ERROR
71 | return error(ffi_str(errmsg[0]))
72 | end
73 | end
74 |
75 |
76 | local function var_set(self, name, value)
77 | local r = getfenv(0).__ngx_req
78 | if not r then
79 | return error("no request found")
80 | end
81 |
82 | if type(name) ~= "string" then
83 | return error("bad variable name")
84 | end
85 | local name_len = #name
86 |
87 | local errlen = 256
88 | local lowcase_buf = get_string_buf(name_len + errlen)
89 |
90 | local value_len
91 | if value == nil then
92 | value_len = 0
93 | else
94 | if type(value) ~= 'string' then
95 | value = tostring(value)
96 | end
97 | value_len = #value
98 | end
99 |
100 | local errbuf = lowcase_buf + name_len
101 | local rc = C.ngx_http_lua_ffi_var_set(r, name, name_len, lowcase_buf,
102 | value, value_len, errbuf, errlen)
103 |
104 | -- ngx.log(ngx.WARN, "rc = ", rc)
105 |
106 | if rc == 0 then -- NGX_OK
107 | return
108 | end
109 |
110 | if rc == -1 then -- NGX_ERROR
111 | return error(ffi_str(errbuf, errlen))
112 | end
113 | end
114 |
115 |
116 | if ngx_var then
117 | local mt = getmetatable(ngx_var)
118 | if mt then
119 | mt.__index = var_get
120 | mt.__newindex = var_set
121 | end
122 | end
123 |
124 |
125 | return {
126 | version = base.version
127 | }
128 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/core/worker.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require 'ffi'
5 | local base = require "resty.core.base"
6 |
7 |
8 | local C = ffi.C
9 |
10 |
11 | ffi.cdef[[
12 | int ngx_http_lua_ffi_worker_pid(void);
13 | int ngx_http_lua_ffi_worker_exiting(void);
14 | ]]
15 |
16 |
17 | function ngx.worker.exiting()
18 | return C.ngx_http_lua_ffi_worker_exiting() ~= 0 and true or false
19 | end
20 |
21 |
22 | function ngx.worker.pid()
23 | return C.ngx_http_lua_ffi_worker_pid()
24 | end
25 |
26 |
27 | return {
28 | _VERSION = base.version
29 | }
30 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/http.lua:
--------------------------------------------------------------------------------
1 | local http_headers = require "resty.http_headers"
2 |
3 | local ngx_socket_tcp = ngx.socket.tcp
4 | local ngx_req = ngx.req
5 | local ngx_req_socket = ngx_req.socket
6 | local ngx_req_get_headers = ngx_req.get_headers
7 | local ngx_req_get_method = ngx_req.get_method
8 | local str_gmatch = string.gmatch
9 | local str_lower = string.lower
10 | local str_upper = string.upper
11 | local str_find = string.find
12 | local str_sub = string.sub
13 | local str_gsub = string.gsub
14 | local tbl_concat = table.concat
15 | local tbl_insert = table.insert
16 | local ngx_encode_args = ngx.encode_args
17 | local ngx_re_match = ngx.re.match
18 | local ngx_log = ngx.log
19 | local ngx_DEBUG = ngx.DEBUG
20 | local ngx_ERR = ngx.ERR
21 | local ngx_NOTICE = ngx.NOTICE
22 | local ngx_var = ngx.var
23 | local co_yield = coroutine.yield
24 | local co_create = coroutine.create
25 | local co_status = coroutine.status
26 | local co_resume = coroutine.resume
27 |
28 |
29 | -- http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
30 | local HOP_BY_HOP_HEADERS = {
31 | ["connection"] = true,
32 | ["keep-alive"] = true,
33 | ["proxy-authenticate"] = true,
34 | ["proxy-authorization"] = true,
35 | ["te"] = true,
36 | ["trailers"] = true,
37 | ["transfer-encoding"] = true,
38 | ["upgrade"] = true,
39 | ["content-length"] = true, -- Not strictly hop-by-hop, but Nginx will deal
40 | -- with this (may send chunked for example).
41 | }
42 |
43 |
44 | -- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot
45 | -- be resumed. This protects user code from inifite loops when doing things like
46 | -- repeat
47 | -- local chunk, err = res.body_reader()
48 | -- if chunk then -- <-- This could be a string msg in the core wrap function.
49 | -- ...
50 | -- end
51 | -- until not chunk
52 | local co_wrap = function(func)
53 | local co = co_create(func)
54 | if not co then
55 | return nil, "could not create coroutine"
56 | else
57 | return function(...)
58 | if co_status(co) == "suspended" then
59 | return select(2, co_resume(co, ...))
60 | else
61 | return nil, "can't resume a " .. co_status(co) .. " coroutine"
62 | end
63 | end
64 | end
65 | end
66 |
67 |
68 | local _M = {
69 | _VERSION = '0.05',
70 | }
71 |
72 | local mt = { __index = _M }
73 |
74 |
75 | local HTTP = {
76 | [1.0] = " HTTP/1.0\r\n",
77 | [1.1] = " HTTP/1.1\r\n",
78 | }
79 |
80 | local USER_AGENT = "Resty/HTTP " .. _M._VERSION .. " (Lua)"
81 |
82 | local DEFAULT_PARAMS = {
83 | method = "GET",
84 | path = "/",
85 | version = 1.1,
86 | }
87 |
88 |
89 | function _M.new(self)
90 | local sock, err = ngx_socket_tcp()
91 | if not sock then
92 | return nil, err
93 | end
94 | return setmetatable({ sock = sock, keepalive = true }, mt)
95 | end
96 |
97 |
98 | function _M.set_timeout(self, timeout)
99 | local sock = self.sock
100 | if not sock then
101 | return nil, "not initialized"
102 | end
103 |
104 | return sock:settimeout(timeout)
105 | end
106 |
107 |
108 | function _M.ssl_handshake(self, ...)
109 | local sock = self.sock
110 | if not sock then
111 | return nil, "not initialized"
112 | end
113 |
114 | return sock:sslhandshake(...)
115 | end
116 |
117 |
118 | function _M.connect(self, ...)
119 | local sock = self.sock
120 | if not sock then
121 | return nil, "not initialized"
122 | end
123 |
124 | self.host = select(1, ...)
125 | self.keepalive = true
126 |
127 | return sock:connect(...)
128 | end
129 |
130 |
131 | function _M.set_keepalive(self, ...)
132 | local sock = self.sock
133 | if not sock then
134 | return nil, "not initialized"
135 | end
136 |
137 | if self.keepalive == true then
138 | return sock:setkeepalive(...)
139 | else
140 | -- The server said we must close the connection, so we cannot setkeepalive.
141 | -- If close() succeeds we return 2 instead of 1, to differentiate between
142 | -- a normal setkeepalive() failure and an intentional close().
143 | local res, err = sock:close()
144 | if res then
145 | return 2, "connection must be closed"
146 | else
147 | return res, err
148 | end
149 | end
150 | end
151 |
152 |
153 | function _M.get_reused_times(self)
154 | local sock = self.sock
155 | if not sock then
156 | return nil, "not initialized"
157 | end
158 |
159 | return sock:getreusedtimes()
160 | end
161 |
162 |
163 | function _M.close(self)
164 | local sock = self.sock
165 | if not sock then
166 | return nil, "not initialized"
167 | end
168 |
169 | return sock:close()
170 | end
171 |
172 |
173 | local function _should_receive_body(method, code)
174 | if method == "HEAD" then return nil end
175 | if code == 204 or code == 304 then return nil end
176 | if code >= 100 and code < 200 then return nil end
177 | return true
178 | end
179 |
180 |
181 | function _M.parse_uri(self, uri)
182 | local m, err = ngx_re_match(uri, [[^(http[s]*)://([^:/]+)(?::(\d+))?(.*)]],
183 | "jo")
184 |
185 | if not m then
186 | if err then
187 | return nil, "failed to match the uri: " .. err
188 | end
189 |
190 | return nil, "bad uri"
191 | else
192 | if not m[3] then
193 | if m[1] == "https" then
194 | m[3] = 443
195 | else
196 | m[3] = 80
197 | end
198 | end
199 | if not m[4] then m[4] = "/" end
200 | return m, nil
201 | end
202 | end
203 |
204 |
205 | local function _format_request(params)
206 | local version = params.version
207 | local headers = params.headers or {}
208 |
209 | local query = params.query or ""
210 | if query then
211 | if type(query) == "table" then
212 | query = "?" .. ngx_encode_args(query)
213 | end
214 | end
215 |
216 | -- Initialize request
217 | local req = {
218 | str_upper(params.method),
219 | " ",
220 | params.path,
221 | query,
222 | HTTP[version],
223 | -- Pre-allocate slots for minimum headers and carriage return.
224 | true,
225 | true,
226 | true,
227 | }
228 | local c = 6 -- req table index it's faster to do this inline vs table.insert
229 |
230 | -- Append headers
231 | for key, values in pairs(headers) do
232 | if type(values) ~= "table" then
233 | values = {values}
234 | end
235 |
236 | key = tostring(key)
237 | for _, value in pairs(values) do
238 | req[c] = key .. ": " .. tostring(value) .. "\r\n"
239 | c = c + 1
240 | end
241 | end
242 |
243 | -- Close headers
244 | req[c] = "\r\n"
245 |
246 | return tbl_concat(req)
247 | end
248 |
249 |
250 | local function _receive_status(sock)
251 | local line, err = sock:receive("*l")
252 | if not line then
253 | return nil, nil, err
254 | end
255 |
256 | return tonumber(str_sub(line, 10, 12)), tonumber(str_sub(line, 6, 8))
257 | end
258 |
259 |
260 |
261 | local function _receive_headers(sock)
262 | local headers = http_headers.new()
263 |
264 | repeat
265 | local line, err = sock:receive("*l")
266 | if not line then
267 | return nil, err
268 | end
269 |
270 | for key, val in str_gmatch(line, "([%w%-]+)%s*:%s*(.+)") do
271 | if headers[key] then
272 | if type(headers[key]) ~= "table" then
273 | headers[key] = { headers[key] }
274 | end
275 | tbl_insert(headers[key], tostring(val))
276 | else
277 | headers[key] = tostring(val)
278 | end
279 | end
280 | until str_find(line, "^%s*$")
281 |
282 | return headers, nil
283 | end
284 |
285 |
286 | local function _chunked_body_reader(sock, default_chunk_size)
287 | return co_wrap(function(max_chunk_size)
288 | local max_chunk_size = max_chunk_size or default_chunk_size
289 | local remaining = 0
290 | local length
291 |
292 | repeat
293 | -- If we still have data on this chunk
294 | if max_chunk_size and remaining > 0 then
295 |
296 | if remaining > max_chunk_size then
297 | -- Consume up to max_chunk_size
298 | length = max_chunk_size
299 | remaining = remaining - max_chunk_size
300 | else
301 | -- Consume all remaining
302 | length = remaining
303 | remaining = 0
304 | end
305 | else -- This is a fresh chunk
306 |
307 | -- Receive the chunk size
308 | local str, err = sock:receive("*l")
309 | if not str then
310 | co_yield(nil, err)
311 | end
312 |
313 | length = tonumber(str, 16)
314 |
315 | if not length then
316 | co_yield(nil, "unable to read chunksize")
317 | end
318 |
319 | if max_chunk_size and length > max_chunk_size then
320 | -- Consume up to max_chunk_size
321 | remaining = length - max_chunk_size
322 | length = max_chunk_size
323 | end
324 | end
325 |
326 | if length > 0 then
327 | local str, err = sock:receive(length)
328 | if not str then
329 | co_yield(nil, err)
330 | end
331 |
332 | max_chunk_size = co_yield(str) or default_chunk_size
333 |
334 | -- If we're finished with this chunk, read the carriage return.
335 | if remaining == 0 then
336 | sock:receive(2) -- read \r\n
337 | end
338 | else
339 | -- Read the last (zero length) chunk's carriage return
340 | sock:receive(2) -- read \r\n
341 | end
342 |
343 | until length == 0
344 | end)
345 | end
346 |
347 |
348 | local function _body_reader(sock, content_length, default_chunk_size)
349 | return co_wrap(function(max_chunk_size)
350 | local max_chunk_size = max_chunk_size or default_chunk_size
351 |
352 | if not content_length and max_chunk_size then
353 | -- We have no length, but wish to stream.
354 | -- HTTP 1.0 with no length will close connection, so read chunks to the end.
355 | repeat
356 | local str, err, partial = sock:receive(max_chunk_size)
357 | if not str and err == "closed" then
358 | max_chunk_size = co_yield(partial, err) or default_chunk_size
359 | end
360 |
361 | max_chunk_size = co_yield(str) or default_chunk_size
362 | until not str
363 |
364 | elseif not content_length then
365 | -- We have no length but don't wish to stream.
366 | -- HTTP 1.0 with no length will close connection, so read to the end.
367 | co_yield(sock:receive("*a"))
368 |
369 | elseif not max_chunk_size then
370 | -- We have a length and potentially keep-alive, but want everything.
371 | co_yield(sock:receive(content_length))
372 |
373 | else
374 | -- We have a length and potentially a keep-alive, and wish to stream
375 | -- the response.
376 | local received = 0
377 | repeat
378 | local length = max_chunk_size
379 | if received + length > content_length then
380 | length = content_length - received
381 | end
382 |
383 | if length > 0 then
384 | local str, err = sock:receive(length)
385 | if not str then
386 | max_chunk_size = co_yield(nil, err) or default_chunk_size
387 | end
388 | received = received + length
389 |
390 | max_chunk_size = co_yield(str) or default_chunk_size
391 | end
392 |
393 | until length == 0
394 | end
395 | end)
396 | end
397 |
398 |
399 | local function _no_body_reader()
400 | return nil
401 | end
402 |
403 |
404 | local function _read_body(res)
405 | local reader = res.body_reader
406 |
407 | if not reader then
408 | -- Most likely HEAD or 304 etc.
409 | return nil, "no body to be read"
410 | end
411 |
412 | local chunks = {}
413 | local c = 1
414 |
415 | local chunk, err
416 | repeat
417 | chunk, err = reader()
418 |
419 | if err then
420 | return nil, err, tbl_concat(chunks) -- Return any data so far.
421 | end
422 | if chunk then
423 | chunks[c] = chunk
424 | c = c + 1
425 | end
426 | until not chunk
427 |
428 | return tbl_concat(chunks)
429 | end
430 |
431 |
432 | local function _trailer_reader(sock)
433 | return co_wrap(function()
434 | co_yield(_receive_headers(sock))
435 | end)
436 | end
437 |
438 |
439 | local function _read_trailers(res)
440 | local reader = res.trailer_reader
441 | if not reader then
442 | return nil, "no trailers"
443 | end
444 |
445 | local trailers = reader()
446 | setmetatable(res.headers, { __index = trailers })
447 | end
448 |
449 |
450 | local function _send_body(sock, body)
451 | if type(body) == 'function' then
452 | repeat
453 | local chunk, err, partial = body()
454 |
455 | if chunk then
456 | local ok,err = sock:send(chunk)
457 |
458 | if not ok then
459 | return nil, err
460 | end
461 | elseif err ~= nil then
462 | return nil, err, partial
463 | end
464 |
465 | until chunk == nil
466 | elseif body ~= nil then
467 | local bytes, err = sock:send(body)
468 |
469 | if not bytes then
470 | return nil, err
471 | end
472 | end
473 | return true, nil
474 | end
475 |
476 |
477 | local function _handle_continue(sock, body)
478 | local status, version, err = _receive_status(sock)
479 | if not status then
480 | return nil, err
481 | end
482 |
483 | -- Only send body if we receive a 100 Continue
484 | if status == 100 then
485 | local ok, err = sock:receive("*l") -- Read carriage return
486 | if not ok then
487 | return nil, err
488 | end
489 | _send_body(sock, body)
490 | end
491 | return status, version, err
492 | end
493 |
494 |
495 | function _M.send_request(self, params)
496 | -- Apply defaults
497 | setmetatable(params, { __index = DEFAULT_PARAMS })
498 |
499 | local sock = self.sock
500 | local body = params.body
501 | local headers = http_headers.new()
502 |
503 | local params_headers = params.headers
504 | if params_headers then
505 | -- We assign one by one so that the metatable can handle case insensitivity
506 | -- for us. You can blame the spec for this inefficiency.
507 | for k,v in pairs(params_headers) do
508 | headers[k] = v
509 | end
510 | end
511 |
512 | -- Ensure minimal headers are set
513 | if type(body) == 'string' and not headers["Content-Length"] then
514 | headers["Content-Length"] = #body
515 | end
516 | if not headers["Host"] then
517 | headers["Host"] = self.host
518 | end
519 | if not headers["User-Agent"] then
520 | headers["User-Agent"] = USER_AGENT
521 | end
522 | if params.version == 1.0 and not headers["Connection"] then
523 | headers["Connection"] = "Keep-Alive"
524 | end
525 |
526 | params.headers = headers
527 |
528 | -- Format and send request
529 | local req = _format_request(params)
530 | ngx_log(ngx_DEBUG, "\n", req)
531 | local bytes, err = sock:send(req)
532 |
533 | if not bytes then
534 | return nil, err
535 | end
536 |
537 | -- Send the request body, unless we expect: continue, in which case
538 | -- we handle this as part of reading the response.
539 | if headers["Expect"] ~= "100-continue" then
540 | local ok, err, partial = _send_body(sock, body)
541 | if not ok then
542 | return nil, err, partial
543 | end
544 | end
545 |
546 | return true
547 | end
548 |
549 |
550 | function _M.read_response(self, params)
551 | local sock = self.sock
552 |
553 | local status, version, err
554 |
555 | -- If we expect: continue, we need to handle this, sending the body if allowed.
556 | -- If we don't get 100 back, then status is the actual status.
557 | if params.headers["Expect"] == "100-continue" then
558 | local _status, _version, _err = _handle_continue(sock, params.body)
559 | if not _status then
560 | return nil, _err
561 | elseif _status ~= 100 then
562 | status, version, err = _status, _version, _err
563 | end
564 | end
565 |
566 | -- Just read the status as normal.
567 | if not status then
568 | status, version, err = _receive_status(sock)
569 | if not status then
570 | return nil, err
571 | end
572 | end
573 |
574 |
575 | local res_headers, err = _receive_headers(sock)
576 | if not res_headers then
577 | return nil, err
578 | end
579 |
580 | -- Determine if we should keepalive or not.
581 | local ok, connection = pcall(str_lower, res_headers["Connection"])
582 | if ok then
583 | if (version == 1.1 and connection == "close") or
584 | (version == 1.0 and connection ~= "keep-alive") then
585 | self.keepalive = false
586 | end
587 | end
588 |
589 | local body_reader = _no_body_reader
590 | local trailer_reader, err = nil, nil
591 | local has_body = false
592 |
593 | -- Receive the body_reader
594 | if _should_receive_body(params.method, status) then
595 | local ok, encoding = pcall(str_lower, res_headers["Transfer-Encoding"])
596 | if ok and version == 1.1 and encoding == "chunked" then
597 | body_reader, err = _chunked_body_reader(sock)
598 | has_body = true
599 | else
600 |
601 | local ok, length = pcall(tonumber, res_headers["Content-Length"])
602 | if ok then
603 | body_reader, err = _body_reader(sock, length)
604 | has_body = true
605 | end
606 | end
607 | end
608 |
609 | if res_headers["Trailer"] then
610 | trailer_reader, err = _trailer_reader(sock)
611 | end
612 |
613 | if err then
614 | return nil, err
615 | else
616 | return {
617 | status = status,
618 | headers = res_headers,
619 | has_body = has_body,
620 | body_reader = body_reader,
621 | read_body = _read_body,
622 | trailer_reader = trailer_reader,
623 | read_trailers = _read_trailers,
624 | }
625 | end
626 | end
627 |
628 |
629 | function _M.request(self, params)
630 | local res, err = self:send_request(params)
631 | if not res then
632 | return res, err
633 | else
634 | return self:read_response(params)
635 | end
636 | end
637 |
638 |
639 | function _M.request_pipeline(self, requests)
640 | for i, params in ipairs(requests) do
641 | if params.headers and params.headers["Expect"] == "100-continue" then
642 | return nil, "Cannot pipeline request specifying Expect: 100-continue"
643 | end
644 |
645 | local res, err = self:send_request(params)
646 | if not res then
647 | return res, err
648 | end
649 | end
650 |
651 | local responses = {}
652 | for i, params in ipairs(requests) do
653 | responses[i] = setmetatable({
654 | params = params,
655 | response_read = false,
656 | }, {
657 | -- Read each actual response lazily, at the point the user tries
658 | -- to access any of the fields.
659 | __index = function(t, k)
660 | local res, err
661 | if t.response_read == false then
662 | res, err = _M.read_response(self, t.params)
663 | t.response_read = true
664 |
665 | if not res then
666 | ngx_log(ngx_ERR, err)
667 | else
668 | for rk, rv in pairs(res) do
669 | t[rk] = rv
670 | end
671 | end
672 | end
673 | return rawget(t, k)
674 | end,
675 | })
676 | end
677 | return responses
678 | end
679 |
680 |
681 | function _M.request_uri(self, uri, params)
682 | if not params then params = {} end
683 |
684 | local parsed_uri, err = self:parse_uri(uri)
685 | if not parsed_uri then
686 | return nil, err
687 | end
688 |
689 | local scheme, host, port, path = unpack(parsed_uri)
690 | if not params.path then params.path = path end
691 |
692 | local c, err = self:connect(host, port)
693 | if not c then
694 | return nil, err
695 | end
696 |
697 | if scheme == "https" then
698 | local verify = true
699 | if params.ssl_verify == false then
700 | verify = false
701 | end
702 | local ok, err = self:ssl_handshake(nil, host, verify)
703 | if not ok then
704 | return nil, err
705 | end
706 | end
707 |
708 | local res, err = self:request(params)
709 | if not res then
710 | return nil, err
711 | end
712 |
713 | local body, err = res:read_body()
714 | if not body then
715 | return nil, err
716 | end
717 |
718 | res.body = body
719 |
720 | local ok, err = self:set_keepalive()
721 | if not ok then
722 | ngx_log(ngx_ERR, err)
723 | end
724 |
725 | return res, nil
726 | end
727 |
728 |
729 | function _M.get_client_body_reader(self, chunksize)
730 | local chunksize = chunksize or 65536
731 | local ok, sock, err = pcall(ngx_req_socket)
732 |
733 | if not ok then
734 | return nil, sock -- pcall err
735 | end
736 |
737 | if not sock then
738 | if err == "no body" then
739 | return nil
740 | else
741 | return nil, err
742 | end
743 | end
744 |
745 | local headers = ngx_req_get_headers()
746 | local length = headers.content_length
747 | local encoding = headers.transfer_encoding
748 | if length then
749 | return _body_reader(sock, tonumber(length), chunksize)
750 | elseif encoding and str_lower(encoding) == 'chunked' then
751 | -- Not yet supported by ngx_lua but should just work...
752 | return _chunked_body_reader(sock, chunksize)
753 | else
754 | return nil
755 | end
756 | end
757 |
758 |
759 | function _M.proxy_request(self, chunksize)
760 | return self:request{
761 | method = ngx_req_get_method(),
762 | path = ngx_var.uri .. ngx_var.is_args .. (ngx_var.query_string or ""),
763 | body = self:get_client_body_reader(chunksize),
764 | headers = ngx_req_get_headers(),
765 | }
766 | end
767 |
768 |
769 | function _M.proxy_response(self, response, chunksize)
770 | if not response then
771 | ngx_log(ngx_ERR, "no response provided")
772 | return
773 | end
774 |
775 | ngx.status = response.status
776 |
777 | -- Filter out hop-by-hop headeres
778 | for k,v in pairs(response.headers) do
779 | if not HOP_BY_HOP_HEADERS[str_lower(k)] then
780 | ngx.header[k] = v
781 | end
782 | end
783 |
784 | local reader = response.body_reader
785 | repeat
786 | local chunk, err = reader(chunksize)
787 | if err then
788 | ngx_log(ngx_ERR, err)
789 | break
790 | end
791 |
792 | if chunk then
793 | ngx.print(chunk)
794 | end
795 | until not chunk
796 | end
797 |
798 |
799 | return _M
800 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/http_headers.lua:
--------------------------------------------------------------------------------
1 | local rawget, rawset, setmetatable =
2 | rawget, rawset, setmetatable
3 |
4 | local str_gsub = string.gsub
5 | local str_lower = string.lower
6 |
7 |
8 | local _M = {
9 | _VERSION = '0.01',
10 | }
11 |
12 |
13 | -- Returns an empty headers table with internalised case normalisation.
14 | -- Supports the same cases as in ngx_lua:
15 | --
16 | -- headers.content_length
17 | -- headers["content-length"]
18 | -- headers["Content-Length"]
19 | function _M.new(self)
20 | local mt = {
21 | normalised = {},
22 | }
23 |
24 |
25 | mt.__index = function(t, k)
26 | local k_hyphened = str_gsub(k, "_", "-")
27 | local matched = rawget(t, k)
28 | if matched then
29 | return matched
30 | else
31 | local k_normalised = str_lower(k_hyphened)
32 | return rawget(t, mt.normalised[k_normalised])
33 | end
34 | end
35 |
36 |
37 | -- First check the normalised table. If there's no match (first time) add an entry for
38 | -- our current case in the normalised table. This is to preserve the human (prettier) case
39 | -- instead of outputting lowercased header names.
40 | --
41 | -- If there's a match, we're being updated, just with a different case for the key. We use
42 | -- the normalised table to give us the original key, and perorm a rawset().
43 | mt.__newindex = function(t, k, v)
44 | -- we support underscore syntax, so always hyphenate.
45 | local k_hyphened = str_gsub(k, "_", "-")
46 |
47 | -- lowercase hyphenated is "normalised"
48 | local k_normalised = str_lower(k_hyphened)
49 |
50 | if not mt.normalised[k_normalised] then
51 | mt.normalised[k_normalised] = k_hyphened
52 | rawset(t, k_hyphened, v)
53 | else
54 | rawset(t, mt.normalised[k_normalised], v)
55 | end
56 | end
57 |
58 | return setmetatable({}, mt)
59 | end
60 |
61 |
62 | return _M
63 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/shcache.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) 2013 Matthieu Tourne
2 | -- @author Matthieu Tourne
3 |
4 | -- small overlay over shdict, smart cache load mechanism
5 |
6 | -- TODO (rdsharma): remove this line once we have tracked down bugs
7 | jit.off(true, true)
8 |
9 | local _M = {}
10 |
11 | local resty_lock = require("resty.lock")
12 | local conf = {}
13 |
14 | local DEBUG = conf.DEBUG or false
15 |
16 | -- defaults in secs
17 | local DEFAULT_POSITIVE_TTL = 10 -- cache for, successful lookup
18 | local DEFAULT_NEGATIVE_TTL = 2 -- cache for, failed lookup
19 | local DEFAULT_ACTUALIZE_TTL = 2 -- stale data, actualize data for
20 |
21 | -- default lock options, in secs
22 | local DEFAULT_LOCK_EXPTIME = 1 -- max wait if failing to call unlock()
23 | local DEFAULT_LOCK_TIMEOUT = 0.5 -- max waiting time of lock()
24 | local DEFAULT_LOCK_MAXSTEP = 0.1 -- max sleeping interval
25 |
26 | if conf then
27 | DEFAULT_NEGATIVE_TTL = conf.DEFAULT_NEGATIVE_TTL or DEFAULT_NEGATIVE_TTL
28 | DEFAULT_ACTUALIZE_TTL = conf.DEFAULT_ACTUALIZE_TTL or DEFAULT_ACTUALIZE_TTL
29 | end
30 |
31 | local bit = require("bit")
32 | local band = bit.band
33 | local bor = bit.bor
34 | local st_format = string.format
35 |
36 | -- there are only really 5 states total
37 | -- is_stale is_neg is_from_cache
38 | local MISS_STATE = 0 -- 0 0 0
39 | local HIT_POSITIVE_STATE = 1 -- 0 0 1
40 | local HIT_NEGATIVE_STATE = 3 -- 0 1 1
41 | local STALE_POSITIVE_STATE = 5 -- 1 0 1
42 |
43 | -- stale negative doesn't really make sense, use HIT_NEGATIVE instead
44 | -- local STALE_NEGATIVE_STATE = 7 -- 1 1 1
45 |
46 | -- xor to set
47 | local NEGATIVE_FLAG = 2
48 | local STALE_FLAG = 4
49 |
50 | local STATES = {
51 | [MISS_STATE] = 'MISS',
52 | [HIT_POSITIVE_STATE] = 'HIT',
53 | [HIT_NEGATIVE_STATE] = 'HIT_NEGATIVE',
54 | [STALE_POSITIVE_STATE] = 'STALE',
55 | -- [STALE_NEGATIVE_STATE] = 'STALE_NEGATIVE',
56 | }
57 |
58 | local function get_status(flags)
59 | return STATES[flags] or st_format('UNDEF (0x%x)', flags)
60 | end
61 |
62 | local EMPTY_DATA = '_EMPTY_'
63 |
64 | -- install debug functions
65 | if DEBUG then
66 | local resty_lock_lock = resty_lock.lock
67 |
68 | resty_lock.lock = function (...)
69 | local _, key = ...
70 | print("lock key: ", tostring(key))
71 | return resty_lock_lock(...)
72 | end
73 |
74 | local resty_lock_unlock = resty_lock.unlock
75 |
76 | resty_lock.unlock = function (...)
77 | print("unlock")
78 | return resty_lock_unlock(...)
79 | end
80 | end
81 |
82 |
83 | -- store the object in the context
84 | -- useful for debugging and tracking cache status
85 | local function _store_object(self, name)
86 | if DEBUG then
87 | print('storing shcache: ', name, ' into ngx.ctx')
88 | end
89 |
90 | local ngx_ctx = ngx.ctx
91 |
92 | if not ngx_ctx.shcache then
93 | ngx_ctx.shcache = {}
94 | end
95 | ngx_ctx.shcache[name] = self
96 | end
97 |
98 | local obj_mt = {
99 | __index = _M,
100 | }
101 |
102 | -- default function for callbacks.encode / decode.
103 | local function _identity(data)
104 | return data
105 | end
106 |
107 | -- shdict: ngx.shared.DICT, created by the lua_shared_dict directive
108 | -- callbacks: see shcache state machine for user defined functions
109 | -- * callbacks.external_lookup is required
110 | -- * callbacks.external_lookup_arg is the (opaque) user argument for
111 | -- external_lookup
112 | -- * callbacks.encode : optional encoding before saving to shmem
113 | -- * callbacks.decode : optional decoding when retreiving from shmem
114 | -- opts:
115 | --
116 | -- The TTL values are passed directly to the ngx.shared.DICT.set
117 | -- function (see
118 | -- http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT.set for the
119 | -- documentation). But note that the value 0 does not mean "do not
120 | -- cache at all", it means "no expiry time". So, for example, setting
121 | -- opts.negative_ttl to 0 means that a failed lookup will be cached
122 | -- forever.
123 | --
124 | -- * opts.positive_ttl : save a valid external lookup for, in seconds
125 | -- * opts.negative_ttl : save a invalid lookup for, in seconds
126 | -- * opts.actualize_ttl : re-actualize a stale record for, in seconds
127 | --
128 | -- * opts.lock_options : set option to lock see:
129 | -- http://github.com/agentzh/lua-resty-lock
130 | -- for more details.
131 | -- * opts.locks_shdict : specificy the name of the shdict containing
132 | -- the locks
133 | -- (useful if you might have locks key collisions)
134 | -- uses "locks" by default.
135 | -- * opts.name : if shcache object is named, it will automatically
136 | -- register itself in ngx.ctx.shcache
137 | -- (useful for logging).
138 | local function new(self, shdict, callbacks, opts)
139 | if not shdict then
140 | return nil, "shdict does not exist"
141 | end
142 |
143 | -- check that callbacks.external_lookup is set
144 | if not callbacks then
145 | return nil, "no callbacks argument"
146 | end
147 |
148 | local ext_lookup = callbacks.external_lookup
149 | if not ext_lookup then
150 | return nil, "no external_lookup callback specified"
151 | end
152 |
153 | local ext_udata = callbacks.external_lookup_arg
154 |
155 | local encode = callbacks.encode
156 | if not encode then
157 | encode = _identity
158 | end
159 |
160 | local decode = callbacks.decode
161 | if not decode then
162 | decode = _identity
163 | end
164 |
165 | local opts = opts or {}
166 |
167 | -- merge default lock options with the ones passed to new()
168 | local lock_options = opts.lock_options or {}
169 | if not lock_options.exptime then
170 | lock_options.exptime = DEFAULT_LOCK_EXPTIME
171 | end
172 | if not lock_options.timeout then
173 | lock_options.timeout = DEFAULT_LOCK_TIMEOUT
174 | end
175 | if not lock_options.max_step then
176 | lock_options.max_step = DEFAULT_LOCK_MAXSTEP
177 | end
178 |
179 | local name = opts.name
180 |
181 | local obj = {
182 | shdict = shdict,
183 |
184 | encode = encode,
185 | decode = decode,
186 | ext_lookup = ext_lookup,
187 | ext_udata = ext_udata,
188 |
189 | positive_ttl = opts.positive_ttl or DEFAULT_POSITIVE_TTL,
190 | negative_ttl = opts.negative_ttl or DEFAULT_NEGATIVE_TTL,
191 |
192 | -- ttl to actualize stale data to
193 | actualize_ttl = opts.actualize_ttl or DEFAULT_ACTUALIZE_TTL,
194 |
195 | lock_options = lock_options,
196 |
197 | locks_shdict = opts.lock_shdict or "locks",
198 |
199 | -- positive ttl specified by external lookup function
200 | lookup_ttl = nil,
201 |
202 | -- STATUS --
203 |
204 | from_cache = false,
205 | cache_status = 'UNDEF',
206 | cache_state = MISS_STATE,
207 | lock_status = 'NO_LOCK',
208 |
209 | -- shdict:set() pushed out another value
210 | forcible_set = false,
211 |
212 | -- cache hit on second attempt (post lock)
213 | hit2 = false,
214 |
215 | name = name,
216 | }
217 |
218 | local locks = ngx.shared[obj.locks_shdict]
219 |
220 | -- check for existence, locks is not directly used
221 | if not locks then
222 | ngx.log(ngx.CRIT, 'shared mem locks is missing.\n',
223 | '## add to you lua conf: lua_shared_dict locks 5M; ##')
224 | return nil
225 | end
226 |
227 | local self = setmetatable(obj, obj_mt)
228 |
229 | -- if the shcache object is named
230 | -- keep track of the object in the context
231 | -- (useful for gathering stats at log phase)
232 | if name then
233 | _store_object(self, name)
234 | end
235 |
236 | return self
237 | end
238 | _M.new = new
239 |
240 | local function _enter_critical_section(self, key)
241 | if DEBUG then
242 | print('Entering critical section, shcache: ', self.name or '')
243 | end
244 |
245 | self.in_critical_section = true
246 |
247 | local critical_sections = ngx.ctx.critical_sections
248 | if not critical_sections then
249 | critical_sections = {
250 | count = 1,
251 | die = false,
252 | workers = { [self] = key },
253 | }
254 | ngx.ctx.critical_sections = critical_sections
255 | return
256 | end
257 |
258 | -- TODO (mtourne): uncomment when ngx.thread.exit api exists.
259 |
260 | -- prevents new thread to enter a critical section if we're set to die.
261 | -- if critical_sections.die then
262 | -- ngx.thread.exit()
263 | -- end
264 |
265 | critical_sections.count = critical_sections.count + 1
266 | critical_sections.workers[self] = key
267 |
268 | if DEBUG then
269 | print('critical sections count: ', critical_sections.count)
270 | end
271 | end
272 |
273 | local function _exit_critical_section(self)
274 | if DEBUG then
275 | print('Leaving critical section, shcache: ', self.name or '')
276 | end
277 |
278 | local critical_sections = ngx.ctx.critical_sections
279 | if not critical_sections then
280 | ngx.log(ngx.ERR, 'weird state: ngx.ctx.critical_sections missing')
281 | return
282 | end
283 |
284 | critical_sections.count = critical_sections.count - 1
285 | critical_sections.workers[self] = nil
286 |
287 | if DEBUG then
288 | print('die: ', critical_sections.die, ', count: ',
289 | critical_sections.count)
290 | end
291 |
292 | local status = critical_sections.die
293 | if status and critical_sections.count <= 0 then
294 | -- safe to exit.
295 | if DEBUG then
296 | print('Last critical section, exiting.')
297 | end
298 | ngx.exit(status)
299 | end
300 | end
301 |
302 | -- acquire a lock
303 | local function _get_lock(self)
304 | local lock = self.lock
305 | if not lock then
306 | lock = resty_lock:new(self.locks_shdict, self.lock_options)
307 | self.lock = lock
308 | end
309 | return lock
310 | end
311 |
312 | -- remove the lock if there is any
313 | local function _unlock(self)
314 | local lock = self.lock
315 | if lock then
316 | local ok, err = lock:unlock()
317 | if not ok then
318 | ngx.log(ngx.ERR, "failed to unlock :" , err)
319 | end
320 | self.lock = nil
321 | end
322 | end
323 |
324 | local function _return(self, data, flags)
325 | -- make sure we remove the locks if any before returning data
326 | _unlock(self)
327 |
328 | -- set cache status
329 | local cache_status = get_status(self.cache_state)
330 |
331 | if cache_status == 'MISS' and not data then
332 | cache_status = 'NO_DATA'
333 | end
334 |
335 | self.cache_status = cache_status
336 |
337 | if self.in_critical_section then
338 | -- data has been cached, and lock on key is removed
339 | -- this is the end of the critical section.
340 | _exit_critical_section(self)
341 | self.in_critical_section = false
342 | end
343 |
344 | return data, self.from_cache
345 | end
346 |
347 | local function _set(self, key, data, ttl, flags)
348 | if DEBUG then
349 | print("saving key: ", key, ", for: ", ttl)
350 | end
351 |
352 | local ok, err, forcible = self.shdict:set(key, data, ttl, flags)
353 |
354 | self.forcible_set = forcible
355 |
356 | if not ok then
357 | ngx.log(ngx.ERR, 'failed to set key: ', key, ', err: ', err)
358 | end
359 |
360 | return ok
361 | end
362 |
363 | -- check if the data returned by :get() is considered empty
364 | local function _is_empty(data, flags)
365 | return flags and band(flags, NEGATIVE_FLAG) and data == EMPTY_DATA
366 | end
367 |
368 | -- save positive, encode the data if needed before :set()
369 | local function _save_positive(self, key, data, ttl)
370 | if DEBUG then
371 | if ttl then
372 | print("key: ", key, ". save positive, lookup ttl: ", ttl)
373 | else
374 | print("key: ", key, ". save positive, ttl: ", self.positive_ttl)
375 | end
376 | end
377 |
378 | data = self.encode(data)
379 |
380 | if ttl then
381 | self.lookup_ttl = ttl
382 | return _set(self, key, data, ttl, HIT_POSITIVE_STATE)
383 | end
384 |
385 | return _set(self, key, data, self.positive_ttl, HIT_POSITIVE_STATE)
386 | end
387 |
388 | -- save negative, no encoding required (no data actually saved)
389 | local function _save_negative(self, key)
390 | if DEBUG then
391 | print("key: ", key, ". save negative, ttl: ", self.negative_ttl)
392 | end
393 | return _set(self, key, EMPTY_DATA, self.negative_ttl, HIT_NEGATIVE_STATE)
394 | end
395 |
396 | -- save actualize, will boost a stale record to a live one
397 | local function _save_actualize(self, key, data, flags)
398 | local new_flags = bor(flags, STALE_FLAG)
399 |
400 | if DEBUG then
401 | print("key: ", key, ". save actualize, ttl: ", self.actualize_ttl,
402 | ". new state: ", get_status(new_flags))
403 | end
404 |
405 | _set(self, key, data, self.actualize_ttl, new_flags)
406 | return new_flags
407 | end
408 |
409 | local function _process_cached_data(self, data, flags)
410 | if DEBUG then
411 | print("data: ", data, st_format(", flags: %x", flags))
412 | end
413 |
414 | self.cache_state = flags
415 | self.from_cache = true
416 |
417 | if _is_empty(data, flags) then
418 | -- empty cached data
419 | return nil
420 | else
421 | return self.decode(data)
422 | end
423 | end
424 |
425 | -- wrapper to get data from the shdict
426 | local function _get(self, key)
427 | -- always call get_stale() as it does not free element
428 | -- like get does on each call
429 | local data, flags, stale = self.shdict:get_stale(key)
430 |
431 | if data and stale then
432 | if DEBUG then
433 | print("found stale data for key : ", key)
434 | end
435 |
436 | self.stale_data = { data, flags }
437 |
438 | return nil, nil
439 | end
440 |
441 | return data, flags
442 | end
443 |
444 | local function _get_stale(self)
445 | local stale_data = self.stale_data
446 | if stale_data then
447 | return unpack(stale_data)
448 | end
449 |
450 | return nil, nil
451 | end
452 |
453 | local function load(self, key)
454 | -- start: check for existing cache
455 | -- clear previous data stored in stale_data
456 | self.stale_data = nil
457 | local data, flags = _get(self, key)
458 |
459 | -- hit: process_cache_hit
460 | if data then
461 | data = _process_cached_data(self, data, flags)
462 | return _return(self, data)
463 | end
464 |
465 | -- miss: set lock
466 |
467 | -- lock: set a lock before performing external lookup
468 | local lock = _get_lock(self)
469 | local elapsed, err = lock:lock(key)
470 |
471 | if not elapsed then
472 | -- failed to acquire lock, still proceed normally to external_lookup
473 | -- unlock() might fail.
474 | local timeout
475 | local opts = self.lock_options
476 | if opts then
477 | timeout = opts.timeout
478 | end
479 | ngx.log(ngx.ERR, "failed to acquire the lock on key \"", key, "\" for ",
480 | timeout, " sec: ", err)
481 | self.lock_status = 'ERROR'
482 | -- _unlock won't try to unlock() without a valid lock
483 | self.lock = nil
484 | else
485 | -- lock acquired successfuly
486 |
487 | if elapsed > 0 then
488 |
489 | -- elapsed > 0 => waited lock (other thread might have :set() the data)
490 | -- (more likely to get a HIT on cache_load 2)
491 | self.lock_status = 'WAITED'
492 |
493 | else
494 |
495 | -- elapsed == 0 => immediate lock
496 | -- it is less likely to get a HIT on cache_load 2
497 | -- but still perform it (race condition cases)
498 | self.lock_status = 'IMMEDIATE'
499 | end
500 |
501 | -- perform cache_load 2
502 | data, flags = _get(self, key)
503 | if data then
504 | -- hit2 : process cache hit
505 |
506 | self.hit2 = true
507 |
508 | -- unlock before de-serializing cached data
509 | _unlock(self)
510 | data = _process_cached_data(self, data, flags)
511 | return _return(self, data)
512 | end
513 |
514 | -- continue to external lookup
515 | end
516 |
517 | -- mark the beginning of the critical section
518 | -- (we want to wait for the data to be looked up and cached successfully)
519 | _enter_critical_section(self, key)
520 |
521 | -- perform external lookup
522 | local data, err, ttl = self.ext_lookup(self.ext_udata)
523 |
524 | if data then
525 | -- succ: save positive and return the data
526 | _save_positive(self, key, data, ttl)
527 | return _return(self, data)
528 | else
529 | ngx.log(ngx.WARN, 'external lookup failed: ', err)
530 | end
531 |
532 | -- external lookup failed
533 | -- attempt to load stale data
534 | data, flags = _get_stale(self)
535 | if data and not _is_empty(data, flags) then
536 | -- hit_stale + valid (positive) data
537 |
538 | flags = _save_actualize(self, key, data, flags)
539 | -- unlock before de-serializing data
540 | _unlock(self)
541 | data = _process_cached_data(self, data, flags)
542 | return _return(self, data)
543 | end
544 |
545 | if DEBUG and data then
546 | -- there is data, but it failed _is_empty() => stale negative data
547 | print('STALE_NEGATIVE data => cache as a new HIT_NEGATIVE')
548 | end
549 |
550 | -- nothing has worked, save negative and return empty
551 | _save_negative(self, key)
552 | return _return(self, nil)
553 | end
554 | _M.load = load
555 |
556 | return _M
557 |
--------------------------------------------------------------------------------
/_example/nginx/lualib/resty/template.lua:
--------------------------------------------------------------------------------
1 | local setmetatable = setmetatable
2 | local tostring = tostring
3 | local setfenv = setfenv
4 | local concat = table.concat
5 | local assert = assert
6 | local write = io.write
7 | local open = io.open
8 | local load = load
9 | local type = type
10 | local dump = string.dump
11 | local find = string.find
12 | local gsub = string.gsub
13 | local byte = string.byte
14 | local sub = string.sub
15 |
16 | local HTML_ENTITIES = {
17 | ["&"] = "&",
18 | ["<"] = "<",
19 | [">"] = ">",
20 | ['"'] = """,
21 | ["'"] = "'",
22 | ["/"] = "/"
23 | }
24 |
25 | local CODE_ENTITIES = {
26 | ["{"] = "{",
27 | ["}"] = "}",
28 | ["&"] = "&",
29 | ["<"] = "<",
30 | [">"] = ">",
31 | ['"'] = """,
32 | ["'"] = "'",
33 | ["/"] = "/"
34 | }
35 |
36 | local ok, newtab = pcall(require, "table.new")
37 | if not ok then newtab = function() return {} end end
38 |
39 | local caching, ngx_var, ngx_capture, ngx_null = true
40 | local template = newtab(0, 13);
41 |
42 | template._VERSION = "1.5-dev"
43 | template.cache = {}
44 | template.concat = concat
45 |
46 | local function rpos(view, s)
47 | while s > 0 do
48 | local c = sub(view, s, s)
49 | if c == " " or c == "\t" or c == "\0" or c == "\x0B" then
50 | s = s - 1
51 | else
52 | break;
53 | end
54 | end
55 | return s
56 | end
57 |
58 | local function read_file(path)
59 | local file = open(path, "rb")
60 | if not file then return nil end
61 | local content = file:read "*a"
62 | file:close()
63 | return content
64 | end
65 |
66 | local function load_lua(path)
67 | return read_file(path) or path
68 | end
69 |
70 | local function load_ngx(path)
71 | local file, location = path, ngx_var.template_location
72 | if sub(file, 1) == "/" then file = sub(file, 2) end
73 | if location and location ~= "" then
74 | if sub(location, -1) == "/" then location = sub(location, 1, -2) end
75 | local res = ngx_capture(location .. '/' .. file)
76 | if res.status == 200 then return res.body end
77 | end
78 | local root = ngx_var.template_root or ngx_var.document_root
79 | if sub(root, -1) == "/" then root = sub(root, 1, -2) end
80 | return read_file(root .. "/" .. file) or path
81 | end
82 |
83 | if ngx then
84 | template.print = ngx.print or write
85 | template.load = load_ngx
86 | ngx_var, ngx_capture, ngx_null = ngx.var, ngx.location.capture, ngx.null
87 | else
88 | template.print = write
89 | template.load = load_lua
90 | end
91 |
92 | local load_chunk
93 |
94 | if _VERSION == "Lua 5.1" then
95 | local context = { __index = function(t, k)
96 | return t.context[k] or t.template[k] or _G[k]
97 | end }
98 | if jit then
99 | load_chunk = function(view)
100 | return assert(load(view, nil, "tb", setmetatable({ template = template }, context)))
101 | end
102 | else
103 | load_chunk = function(view)
104 | local func = assert(loadstring(view))
105 | setfenv(func, setmetatable({ template = template }, context))
106 | return func
107 | end
108 | end
109 | else
110 | local context = { __index = function(t, k)
111 | return t.context[k] or t.template[k] or _ENV[k]
112 | end }
113 | load_chunk = function(view)
114 | return assert(load(view, nil, "tb", setmetatable({ template = template }, context)))
115 | end
116 | end
117 |
118 | function template.caching(enable)
119 | if enable ~= nil then caching = enable == true end
120 | return caching
121 | end
122 |
123 | function template.output(s)
124 | if s == nil or s == ngx_null then return "" end
125 | if type(s) == "function" then return template.output(s()) end
126 | return tostring(s)
127 | end
128 |
129 | function template.escape(s, c)
130 | if type(s) == "string" then
131 | if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end
132 | return gsub(s, "[\">/<'&]", HTML_ENTITIES)
133 | end
134 | return template.output(s)
135 | end
136 |
137 | function template.new(view, layout)
138 | assert(view, "view was not provided for template.new(view, layout).")
139 | local render, compile = template.render, template.compile
140 | if layout then
141 | return setmetatable({ render = function(self, context)
142 | local context = context or self
143 | context.blocks = context.blocks or {}
144 | context.view = compile(view)(context)
145 | render(layout, context)
146 | end }, { __tostring = function(self)
147 | local context = context or self
148 | context.blocks = context.blocks or {}
149 | context.view = compile(view)(context)
150 | return compile(layout)(context)
151 | end })
152 | end
153 | return setmetatable({ render = function(self, context)
154 | render(view, context or self)
155 | end }, { __tostring = function(self)
156 | return compile(view)(context or self)
157 | end })
158 | end
159 |
160 | function template.precompile(view, path, strip)
161 | local chunk = dump(template.compile(view), strip ~= false)
162 | if path then
163 | local file = open(path, "wb")
164 | file:write(chunk)
165 | file:close()
166 | end
167 | return chunk
168 | end
169 |
170 | function template.compile(view, key, plain)
171 | assert(view, "view was not provided for template.compile(view, key, plain).")
172 | if key == "no-cache" then
173 | return load_chunk(template.parse(view, plain)), false
174 | end
175 | key = key or view
176 | local cache = template.cache
177 | if cache[key] then return cache[key], true end
178 | local func = load_chunk(template.parse(view, plain))
179 | if caching then cache[key] = func end
180 | return func, false
181 | end
182 |
183 | function template.parse(view, plain)
184 | assert(view, "view was not provided for template.parse(view, plain).")
185 | local concat, rpos, find, byte, sub = concat, rpos, find, byte, sub
186 | if not plain then
187 | view = template.load(view)
188 | if byte(sub(view, 1, 1)) == 27 then return view end
189 | end
190 | local c = {[[
191 | context=... or {}
192 | local function include(v, c)
193 | return template.compile(v)(c or context)
194 | end
195 | local ___,blocks,layout={},blocks or {}
196 | ]]}
197 | local i, s = 1, find(view, "{", 1, true)
198 | while s do
199 | local t, p, d, z, r = sub(view, s + 1, s + 1), s + 2
200 | if t == "{" then
201 | local e = find(view, "}}", p, true)
202 | if e then
203 | d = concat{"___[#___+1]=template.escape(", sub(view, p, e - 1), ")\n" }
204 | z = e + 1
205 | end
206 | elseif t == "*" then
207 | local e = (find(view, "*}", p, true))
208 | if e then
209 | d = concat{"___[#___+1]=template.output(", sub(view, p, e - 1), ")\n" }
210 | z = e + 1
211 | end
212 | elseif t == "%" then
213 | local e = find(view, "%}", p, true)
214 | if e then
215 | local n = e + 2
216 | if sub(view, n, n) == "\n" then
217 | n = n + 1
218 | end
219 | d = concat{sub(view, p, e - 1), "\n" }
220 | z, r = n - 1, true
221 | end
222 | elseif t == "(" then
223 | local e = find(view, ")}", p, true)
224 | if e then
225 | local f = sub(view, p, e - 1)
226 | local x = (find(f, ",", 2, true))
227 | if x then
228 | d = concat{"___[#___+1]=include([=[", sub(f, 1, x - 1), "]=],", sub(f, x + 1), ")\n"}
229 | else
230 | d = concat{"___[#___+1]=include([=[", f, "]=])\n" }
231 | end
232 | z = e + 1
233 | end
234 | elseif t == "[" then
235 | local e = find(view, "]}", p, true)
236 | if e then
237 | d = concat{"___[#___+1]=include(", sub(view, p, e - 1), ")\n" }
238 | z = e + 1
239 | end
240 | elseif t == "-" then
241 | local e = find(view, "-}", p, true)
242 | if e then
243 | local x, y = find(view, sub(view, s, e + 1), e + 2, true)
244 | if x then
245 | y = y + 1
246 | x = x - 1
247 | if sub(view, y, y) == "\n" then
248 | y = y + 1
249 | end
250 | if sub(view, x, x) == "\n" then
251 | x = x - 1
252 | end
253 | d = concat{'blocks["', sub(view, p, e - 1), '"]=include[=[', sub(view, e + 2, x), "]=]\n"}
254 | z, r = y - 1, true
255 | end
256 | end
257 | elseif t == "#" then
258 | local e = find(view, "#}", p, true)
259 | if e then
260 | e = e + 2
261 | if sub(view, e, e) == "\n" then
262 | e = e + 1
263 | end
264 | d = ""
265 | z, r = e - 1, true
266 | end
267 | end
268 | if d then
269 | c[#c+1] = concat{"___[#___+1]=[=[\n", sub(view, i, r and rpos(view, s - 1) or s - 1), "]=]\n" }
270 | if d ~= "" then
271 | c[#c+1] = d
272 | end
273 | s, i = z, z + 1
274 | end
275 | s = find(view, "{", s + 1, true)
276 | end
277 | c[#c+1] = concat{"___[#___+1]=[=[\n", sub(view, i), "]=]\n"}
278 | c[#c+1] = "return layout and include(layout,setmetatable({view=template.concat(___),blocks=blocks},{__index=context})) or template.concat(___)"
279 | return concat(c)
280 | end
281 |
282 | function template.render(view, context, key, plain)
283 | assert(view, "view was not provided for template.render(view, context, key, plain).")
284 | return template.print(template.compile(view, key, plain)(context))
285 | end
286 |
287 | return template
288 |
--------------------------------------------------------------------------------
/alpine/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.3
2 |
3 | ENV OPENRESTY_VERSION 1.9.7.3
4 | ENV OPENRESTY_PREFIX /opt/openresty
5 | ENV NGINX_PREFIX /opt/openresty/nginx
6 | ENV VAR_PREFIX /var/nginx
7 |
8 | # NginX prefix is automatically set by OpenResty to $OPENRESTY_PREFIX/nginx
9 | # look for $ngx_prefix in https://github.com/openresty/ngx_openresty/blob/master/util/configure
10 |
11 | RUN echo "==> Installing dependencies..." \
12 | && apk update \
13 | && apk add --virtual build-deps \
14 | make gcc musl-dev \
15 | pcre-dev openssl-dev zlib-dev ncurses-dev readline-dev \
16 | curl perl \
17 | && mkdir -p /root/ngx_openresty \
18 | && cd /root/ngx_openresty \
19 | && echo "==> Downloading OpenResty..." \
20 | && curl -sSL http://openresty.org/download/openresty-${OPENRESTY_VERSION}.tar.gz | tar -xvz \
21 | && cd openresty-* \
22 | && echo "==> Configuring OpenResty..." \
23 | && readonly NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) \
24 | && echo "using upto $NPROC threads" \
25 | && ./configure \
26 | --prefix=$OPENRESTY_PREFIX \
27 | --http-client-body-temp-path=$VAR_PREFIX/client_body_temp \
28 | --http-proxy-temp-path=$VAR_PREFIX/proxy_temp \
29 | --http-log-path=$VAR_PREFIX/access.log \
30 | --error-log-path=$VAR_PREFIX/error.log \
31 | --pid-path=$VAR_PREFIX/nginx.pid \
32 | --lock-path=$VAR_PREFIX/nginx.lock \
33 | --with-luajit \
34 | --with-pcre-jit \
35 | --with-ipv6 \
36 | --with-http_ssl_module \
37 | --without-http_ssi_module \
38 | --without-http_userid_module \
39 | --without-http_uwsgi_module \
40 | --without-http_scgi_module \
41 | -j${NPROC} \
42 | && echo "==> Building OpenResty..." \
43 | && make -j${NPROC} \
44 | && echo "==> Installing OpenResty..." \
45 | && make install \
46 | && echo "==> Finishing..." \
47 | && ln -sf $NGINX_PREFIX/sbin/nginx /usr/local/bin/nginx \
48 | && ln -sf $NGINX_PREFIX/sbin/nginx /usr/local/bin/openresty \
49 | && ln -sf $OPENRESTY_PREFIX/bin/resty /usr/local/bin/resty \
50 | && ln -sf $OPENRESTY_PREFIX/luajit/bin/luajit-* $OPENRESTY_PREFIX/luajit/bin/lua \
51 | && ln -sf $OPENRESTY_PREFIX/luajit/bin/luajit-* /usr/local/bin/lua \
52 | && apk del build-deps \
53 | && apk add \
54 | libpcrecpp libpcre16 libpcre32 openssl libssl1.0 pcre libgcc libstdc++ \
55 | && rm -rf /var/cache/apk/* \
56 | && rm -rf /root/ngx_openresty
57 |
58 | WORKDIR $NGINX_PREFIX/
59 |
60 | ONBUILD RUN rm -rf conf/* html/*
61 | ONBUILD COPY nginx $NGINX_PREFIX/
62 |
63 | CMD ["nginx", "-g", "daemon off; error_log /dev/stderr info;"]
64 |
--------------------------------------------------------------------------------
/debian/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:wheezy
2 |
3 | RUN apt-get update \
4 | && apt-get install -y --no-install-recommends \
5 | curl perl make build-essential procps \
6 | libreadline-dev libncurses5-dev libpcre3-dev libssl-dev \
7 | && rm -rf /var/lib/apt/lists/*
8 |
9 | ENV OPENRESTY_VERSION 1.9.7.3
10 | ENV OPENRESTY_PREFIX /opt/openresty
11 | ENV NGINX_PREFIX /opt/openresty/nginx
12 | ENV VAR_PREFIX /var/nginx
13 |
14 | # NginX prefix is automatically set by OpenResty to $OPENRESTY_PREFIX/nginx
15 | # look for $ngx_prefix in https://github.com/openresty/ngx_openresty/blob/master/util/configure
16 |
17 | RUN cd /root \
18 | && echo "==> Downloading OpenResty..." \
19 | && curl -sSL http://openresty.org/download/openresty-${OPENRESTY_VERSION}.tar.gz | tar -xvz \
20 | && echo "==> Configuring OpenResty..." \
21 | && cd openresty-* \
22 | && readonly NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) \
23 | && echo "using upto $NPROC threads" \
24 | && ./configure \
25 | --prefix=$OPENRESTY_PREFIX \
26 | --http-client-body-temp-path=$VAR_PREFIX/client_body_temp \
27 | --http-proxy-temp-path=$VAR_PREFIX/proxy_temp \
28 | --http-log-path=$VAR_PREFIX/access.log \
29 | --error-log-path=$VAR_PREFIX/error.log \
30 | --pid-path=$VAR_PREFIX/nginx.pid \
31 | --lock-path=$VAR_PREFIX/nginx.lock \
32 | --with-luajit \
33 | --with-pcre-jit \
34 | --with-ipv6 \
35 | --with-http_ssl_module \
36 | --without-http_ssi_module \
37 | --without-http_userid_module \
38 | --without-http_uwsgi_module \
39 | --without-http_scgi_module \
40 | -j${NPROC} \
41 | && echo "==> Building OpenResty..." \
42 | && make -j${NPROC} \
43 | && echo "==> Installing OpenResty..." \
44 | && make install \
45 | && echo "==> Finishing..." \
46 | && ln -sf $NGINX_PREFIX/sbin/nginx /usr/local/bin/nginx \
47 | && ln -sf $NGINX_PREFIX/sbin/nginx /usr/local/bin/openresty \
48 | && ln -sf $OPENRESTY_PREFIX/bin/resty /usr/local/bin/resty \
49 | && ln -sf $OPENRESTY_PREFIX/luajit/bin/luajit-* $OPENRESTY_PREFIX/luajit/bin/lua \
50 | && ln -sf $OPENRESTY_PREFIX/luajit/bin/luajit-* /usr/local/bin/lua \
51 | && rm -rf /root/ngx_openresty*
52 |
53 | WORKDIR $NGINX_PREFIX/
54 |
55 | ONBUILD RUN rm -rf conf/* html/*
56 | ONBUILD COPY nginx $NGINX_PREFIX/
57 |
58 | CMD ["nginx", "-g", "daemon off; error_log /dev/stderr info;"]
59 |
--------------------------------------------------------------------------------