├── utils
├── html
│ ├── beta1
│ │ └── index.html
│ ├── beta2
│ │ └── index.html
│ ├── beta3
│ │ └── index.html
│ ├── beta4
│ │ └── index.html
│ ├── index.html
│ └── stable
│ │ └── index.html
├── README
├── stop-all-nginx.sh
├── abtesting.sh
├── conf
│ ├── upstream.conf
│ ├── default.conf
│ ├── beta4.conf
│ ├── fastcgi_params
│ ├── fastcgi.conf
│ ├── stable.conf
│ ├── beta3.conf
│ ├── beta2.conf
│ ├── beta1.conf
│ ├── policy_sample
│ ├── vhost.conf
│ ├── nginx.conf
│ └── mime.types
└── pytool
│ ├── cmd.txt
│ └── pydygateway.py
├── doc
├── img
│ ├── manage.png
│ ├── 带cache性能.png
│ ├── 带cache柱子.png
│ ├── deployment.png
│ ├── load_data.png
│ ├── load_line.png
│ ├── div_flowchart.png
│ ├── runtime_policy.png
│ └── abtesting_architect.png
├── 灰度发布系统压测报告.pdf
├── 基于动态策略的灰度发布系统.pdf
├── redis_conf_配置过程.md
├── 注释文档.md
├── ab功能须知.md
├── ab分流策略.md
├── nginx_conf_配置过程.md
├── ABTestingGateway之添加新的分流方式.md
└── ABTestingGateway之系统配置参数说明.md
├── lib
├── lua-resty-core
│ ├── t
│ │ ├── cert
│ │ │ ├── test.crt.der
│ │ │ ├── test.key.der
│ │ │ ├── chain
│ │ │ │ ├── chain.der
│ │ │ │ ├── test-com.key.der
│ │ │ │ ├── test-com.key.pem
│ │ │ │ └── root-ca.crt
│ │ │ ├── ocsp
│ │ │ │ ├── ocsp-req.der
│ │ │ │ ├── ocsp-resp.der
│ │ │ │ ├── ocsp-resp-no-certs.der
│ │ │ │ ├── revoked-ocsp-resp.der
│ │ │ │ ├── ocsp-resp-signed-by-orphaned.der
│ │ │ │ ├── ocsp-resp-signed-by-orphaned-no-certs.der
│ │ │ │ └── test-com.crt
│ │ │ ├── test2.key
│ │ │ ├── test.key
│ │ │ ├── test2.crt
│ │ │ └── test.crt
│ │ ├── count.t
│ │ ├── exit.t
│ │ ├── ctx.t
│ │ ├── status.t
│ │ ├── worker.t
│ │ ├── time.t
│ │ ├── misc.t
│ │ ├── md5.t
│ │ ├── md5_bin.t
│ │ ├── sha1_bin.t
│ │ ├── decode-base64.t
│ │ ├── var.t
│ │ ├── encode-base64.t
│ │ └── response.t
│ ├── lib
│ │ ├── resty
│ │ │ ├── core.lua
│ │ │ └── core
│ │ │ │ ├── time.lua
│ │ │ │ ├── worker.lua
│ │ │ │ ├── exit.lua
│ │ │ │ ├── uri.lua
│ │ │ │ ├── hash.lua
│ │ │ │ ├── ctx.lua
│ │ │ │ ├── base64.lua
│ │ │ │ ├── var.lua
│ │ │ │ ├── misc.lua
│ │ │ │ ├── base.lua
│ │ │ │ └── response.lua
│ │ └── ngx
│ │ │ ├── balancer.lua
│ │ │ ├── semaphore.lua
│ │ │ ├── ocsp.lua
│ │ │ └── ssl.lua
│ ├── Makefile
│ └── valgrind.suppress
├── abtesting
│ ├── userinfo
│ │ ├── urlParser.lua
│ │ ├── cityParser.lua
│ │ ├── uidParser.lua
│ │ └── ipParser.lua
│ ├── utils
│ │ ├── sema.lua
│ │ ├── log.lua
│ │ ├── redis.lua
│ │ ├── utils.lua
│ │ ├── init.lua
│ │ └── cache.lua
│ ├── error
│ │ ├── handler.lua
│ │ └── errcode.lua
│ ├── diversion
│ │ ├── arg_city.lua
│ │ ├── url.lua
│ │ ├── uidsuffix.lua
│ │ ├── uidappoint.lua
│ │ ├── uidrange.lua
│ │ └── iprange.lua
│ └── adapter
│ │ ├── runtime.lua
│ │ └── policy.lua
└── resty
│ └── lock.lua
├── .gitignore
├── LICENSE
└── admin
├── action.lua
└── ab_action.lua
/utils/html/beta1/index.html:
--------------------------------------------------------------------------------
1 | this is beta1 server
2 |
--------------------------------------------------------------------------------
/utils/html/beta2/index.html:
--------------------------------------------------------------------------------
1 | this is beta2 server
2 |
--------------------------------------------------------------------------------
/utils/html/beta3/index.html:
--------------------------------------------------------------------------------
1 | this is beta3 server
2 |
--------------------------------------------------------------------------------
/utils/html/beta4/index.html:
--------------------------------------------------------------------------------
1 | this is beta4 server
2 |
--------------------------------------------------------------------------------
/utils/html/index.html:
--------------------------------------------------------------------------------
1 | this is abtesting server
2 |
--------------------------------------------------------------------------------
/utils/html/stable/index.html:
--------------------------------------------------------------------------------
1 | this is stable server
2 |
--------------------------------------------------------------------------------
/doc/img/manage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/manage.png
--------------------------------------------------------------------------------
/doc/灰度发布系统压测报告.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/灰度发布系统压测报告.pdf
--------------------------------------------------------------------------------
/doc/img/带cache性能.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/带cache性能.png
--------------------------------------------------------------------------------
/doc/img/带cache柱子.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/带cache柱子.png
--------------------------------------------------------------------------------
/doc/img/deployment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/deployment.png
--------------------------------------------------------------------------------
/doc/img/load_data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/load_data.png
--------------------------------------------------------------------------------
/doc/img/load_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/load_line.png
--------------------------------------------------------------------------------
/doc/基于动态策略的灰度发布系统.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/基于动态策略的灰度发布系统.pdf
--------------------------------------------------------------------------------
/doc/img/div_flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/div_flowchart.png
--------------------------------------------------------------------------------
/doc/img/runtime_policy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/runtime_policy.png
--------------------------------------------------------------------------------
/doc/img/abtesting_architect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/doc/img/abtesting_architect.png
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/test.crt.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/test.crt.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/test.key.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/test.key.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/chain/chain.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/chain/chain.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/ocsp-req.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/ocsp/ocsp-req.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/ocsp-resp.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/ocsp/ocsp-resp.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/chain/test-com.key.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/chain/test-com.key.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/ocsp-resp-no-certs.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/ocsp/ocsp-resp-no-certs.der
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/revoked-ocsp-resp.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/ocsp/revoked-ocsp-resp.der
--------------------------------------------------------------------------------
/utils/README:
--------------------------------------------------------------------------------
1 |
2 | * conf
3 | * demo conf of nginx and redis
4 |
5 | * html
6 | * some html file
7 |
8 | * pytool
9 | * a cmd line tool for dygateway
10 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/ocsp-resp-signed-by-orphaned.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/ocsp/ocsp-resp-signed-by-orphaned.der
--------------------------------------------------------------------------------
/lib/abtesting/userinfo/urlParser.lua:
--------------------------------------------------------------------------------
1 |
2 | local _M = {
3 | _VERSION = '0.01'
4 | }
5 |
6 | _M.get = function()
7 | local u = ngx.var.uri
8 | return u
9 | end
10 | return _M
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.o
3 | *.swp
4 | *_temp/
5 | *.log
6 | *.log
7 | *.pid
8 | *.out
9 | *.rdb
10 | t/servroot/
11 | *temp/
12 | *.pyc
13 |
14 | *stdout*
15 | stdout
16 |
17 | *.log
18 |
--------------------------------------------------------------------------------
/lib/abtesting/userinfo/cityParser.lua:
--------------------------------------------------------------------------------
1 |
2 | local _M = {
3 | _VERSION = '0.01'
4 | }
5 |
6 | _M.get = function()
7 | local u = ngx.var.arg_city
8 | return u
9 | end
10 | return _M
11 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/ocsp-resp-signed-by-orphaned-no-certs.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binghe001/ABTestingGateway/HEAD/lib/lua-resty-core/t/cert/ocsp/ocsp-resp-signed-by-orphaned-no-certs.der
--------------------------------------------------------------------------------
/lib/abtesting/userinfo/uidParser.lua:
--------------------------------------------------------------------------------
1 |
2 | local _M = {
3 | _VERSION = '0.01'
4 | }
5 |
6 | _M.get = function()
7 | local u = ngx.req.get_headers()["X-Uid"]
8 | return u
9 | end
10 | return _M
11 |
--------------------------------------------------------------------------------
/lib/abtesting/utils/sema.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingSema"
2 | local _M = {}
3 |
4 | local semaphore = require("lua-resty-core.lib.ngx.semaphore")
5 |
6 | _M.sema = semaphore.new(1)
7 | _M.upsSema = semaphore.new(1)
8 |
9 | return _M
10 |
--------------------------------------------------------------------------------
/doc/redis_conf_配置过程.md:
--------------------------------------------------------------------------------
1 | * redis的ip&port配置要与nginx.conf中一致,同时补充几点redis的优化配置
2 |
3 |
4 | ```bash
5 | port 6379
6 | unixsocket /tmp/redis.sock
7 | unixsocketperm 766
8 |
9 | timeout 0
10 | tcp-keepalive 120
11 | tcp-backlog 20000
12 |
13 | maxclients 262144
14 | ```
15 |
16 |
--------------------------------------------------------------------------------
/utils/stop-all-nginx.sh:
--------------------------------------------------------------------------------
1 | rm *temp -rf
2 |
3 | /usr/local/nginx/sbin/nginx -p `pwd` -c conf/nginx.conf -s stop
4 | /usr/local/nginx/sbin/nginx -p `pwd` -c conf/stable.conf -s stop
5 | /usr/local/nginx/sbin/nginx -p `pwd` -c conf/beta1.conf -s stop
6 | /usr/local/nginx/sbin/nginx -p `pwd` -c conf/beta2.conf -s stop
7 | /usr/local/nginx/sbin/nginx -p `pwd` -c conf/beta3.conf -s stop
8 | /usr/local/nginx/sbin/nginx -p `pwd` -c conf/beta4.conf -s stop
9 |
--------------------------------------------------------------------------------
/utils/abtesting.sh:
--------------------------------------------------------------------------------
1 | #killall nginx
2 |
3 | rm *temp -rf
4 |
5 | /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx.conf
6 | /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/stable.conf
7 | /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/beta1.conf
8 | /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/beta2.conf
9 | /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/beta3.conf
10 | /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/beta4.conf
11 |
--------------------------------------------------------------------------------
/lib/abtesting/error/handler.lua:
--------------------------------------------------------------------------------
1 | local modulename = 'abtestingErrorHandler'
2 |
3 | local _M = { _VERSION = '0.01' }
4 |
5 | local ERRORINFO = require('abtesting.error.errcode').info
6 |
7 | _M.handler = function(errinfo)
8 | local info
9 | if type(errinfo) == 'table' then
10 | info = errinfo
11 | elseif type(errinfo) == 'string' then
12 | info = {ERRORINFO.LUA_RUNTIME_ERROR, errinfo}
13 | else
14 | info = {ERRORINFO.BLANK_INFO_ERROR, }
15 | end
16 |
17 | local errstack = debug.traceback()
18 | return {info, errstack}
19 | end
20 |
21 | return _M
22 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 |
--------------------------------------------------------------------------------
/utils/conf/upstream.conf:
--------------------------------------------------------------------------------
1 | upstream beta2 {
2 | server 127.0.0.1:8021 weight=10 fail_timeout=1 max_fails=3;
3 | keepalive 256;
4 | }
5 |
6 | upstream beta3 {
7 | server 127.0.0.1:8022 weight=10 fail_timeout=1 max_fails=3;
8 | keepalive 256;
9 | }
10 |
11 | upstream beta1 {
12 | server 127.0.0.1:8020 weight=1 fail_timeout=10 max_fails=1;
13 | keepalive 1000;
14 | }
15 |
16 | upstream beta4 {
17 | server 127.0.0.1:8023 weight=1 fail_timeout=10 max_fails=1;
18 | keepalive 1000;
19 | }
20 |
21 | upstream stable {
22 | server 127.0.0.1:8040 weight=1 fail_timeout=10 max_fails=1;
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/Makefile:
--------------------------------------------------------------------------------
1 | OPENRESTY_PREFIX=/usr/local/openresty
2 |
3 | PREFIX ?= /usr/local
4 | LUA_INCLUDE_DIR ?= $(PREFIX)/include
5 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
6 | INSTALL ?= install
7 |
8 | .PHONY: all test install
9 |
10 | all: ;
11 |
12 | install: all
13 | $(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/resty/core/
14 | $(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/ngx/
15 | $(INSTALL) lib/resty/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/
16 | $(INSTALL) lib/resty/core/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/core/
17 | $(INSTALL) lib/ngx/*.lua $(DESTDIR)$(LUA_LIB_DIR)/ngx/
18 |
19 | test: all
20 | PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t
21 |
22 |
--------------------------------------------------------------------------------
/utils/conf/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 8080;
3 | server_name localhost 127.0.0.1;
4 | access_log logs/ip-access.log main;
5 | error_log logs/admin.log error;
6 | location / {
7 | root html;
8 | index index.html index.htm;
9 | }
10 |
11 | # location /nginx_status {
12 | # stub_status on;
13 | # access_log off;
14 | # allow 127.0.0.1;
15 | # }
16 |
17 | error_page 500 502 503 504 /50x.html;
18 | location = /50x.html {
19 | root html;
20 | }
21 |
22 | set $redis_host '127.0.0.1';
23 | set $redis_port '6379';
24 | set $redis_uds '/var/run/redis.sock';
25 | set $redis_connect_timeout 10000;
26 | set $redis_dbid 0;
27 |
28 | set $redis_pool_size 1000;
29 | set $redis_keepalive_timeout 90000;
30 |
31 | location /ab_admin {
32 | content_by_lua_file '../admin/ab_action.lua';
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/lib/abtesting/utils/log.lua:
--------------------------------------------------------------------------------
1 | local log = ngx.log
2 |
3 | local ERR = ngx.ERR
4 | local INFO = ngx.INFO
5 | local WARN = ngx.WARN
6 | local DEBUG = ngx.DEBUG
7 |
8 | local _M = {}
9 | local mt = {__index = _M}
10 | _M._VERSION = "0.01"
11 |
12 | _M.new = function (self, hostname)
13 | self.tag = hostname
14 | return setmetatable(self, mt)
15 | end
16 |
17 | function _M.info(self, ...)
18 | log(INFO, "ab_div host [", self.tag or 'ab_admin',"] ", ...)
19 | end
20 |
21 |
22 | function _M.warn(self, ...)
23 | log(WARN, "ab_div host [", self.tag or 'ab_admin',"] ", ...)
24 | end
25 |
26 |
27 | function _M.errlog(self, ...)
28 | log(ERR, "ab_div host [", self.tag or 'ab_admin',"] ", ...)
29 | end
30 |
31 |
32 | function _M.debug(self, ...)
33 | log(DEBUG, "ab_div host [", self.tag or 'ab_admin',"] ", ...)
34 | end
35 |
36 |
37 | return _M
38 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/test2.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQDy+OVI2u5NBOeB2CyzGnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4
3 | PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7fJrKFziJxMy4g4Kdn9G659vE7CWu/UAVj
4 | RUtc+mTBAEfjdbumizmHLG7DmnNhGl3RNGiVNLsUInSMGfUlJRzZJXhI4QIDAQAB
5 | AoGAEqBB83PVENJvbOTFiHVfUAjGtr3R/Wnwd4jOcjHHZB3fZ9sjVoxJntxfp3s1
6 | dwZir2rxlqVS6i3VAFiGiVTOGo2Vvzhw2J7f58twCECmnLb2f863AkGEYe4dAndD
7 | GHGD0WI0CBMD1sT18YCj561o0Wol5deWH0gM9pr2N3HkeIECQQD6hUKFlFhrpaHP
8 | WNJsl6BxgE6pB5kxLcMcpIQ7P+kHUvtyvCJl5QZJqPrpPGjRsAI5Ph92rpsp/zDp
9 | /IZNWGVjAkEA+Ele31Rt+XbV32MrLKZgBDBk+Pzss5LTn9fZ5v1k/7hrMk2VVWvk
10 | AD6n5QiGe/g59woANpPb1T9l956SBf0d6wJABTXOS17pc9uvANP1FGMW6CVl/Wf2
11 | DKrJ+weE5IKQwyE7r4gwIvRfbBrClSU3fNzvPueG2f4JphbzmnoxBNzIxwJAYivY
12 | mGNwzHehXx99/byXMHDWK+EN0n8WsBgP75Z3rekEcbJdfpYXY8Via1vwmOnwOW65
13 | 4NqbzHix37PSNw37GwJBALxaGNpREO2Tk+oWOvsD2QyviMVae3mXAJHc6nLVdKDM
14 | q0YvDT6VdeNYYFTkAuzJacsVXOpn6AnUMFj0OBedMhc=
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/test.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXgIBAAKBgQDo/XzNS+2ZopArbF4/LxlsXBLJMvD6Nbq315lqJN5zxfeJs31Z
3 | 6avEfORNGKTE/dwBCghNG5mJC8XhDsM7rAyTP9vTPB+Bfm550arTjsAk/qf+bCUM
4 | 6MZI2iYEuhdDOju1V7rSKQSbdnINSqck8ky3eEiY7ldKingw8y5lr1aEVwIDAQAB
5 | AoGBANgB66sKMga2SKN5nQdHS3LDCkevCutu1OWM5ZcbB4Kej5kC57xsf+tzPtab
6 | emeIVGhCPOAALqB4YcT+QtMX967oM1MjcFbtH7si5oq6UYyp3i0G9Si6jIoVHz3+
7 | 8yOUaqwKbK+bRX8VS0YsHZmBsPK5ryN50iUwsU08nemoA94BAkEA9GS9Q5OPeFkM
8 | tFxsIQ1f2FSsZAuN/1cpZgJqY+YaAN7MSPGTWyfd7nWG/Zgk3GO9/2ihh4gww+7B
9 | To09GkmW4QJBAPQOHC2V+t2TA98+6Lj6+TYwcGEkhOENfVpH25mQ+kXgF/1Bd6rA
10 | nosT1bdAY+SnmWXbSw6Kv5C20Em+bEX8WjcCQCSRRjhsRdVODbaW9Z7kb2jhEoJN
11 | sEt6cTlQNzcHYPCsZYisjM3g4zYg47fiIfHQAsfKkhDDcfh/KvFj9LaQOEECQQCH
12 | eBWYEDpSJ7rsfqT7mQQgWj7nDThdG/nK1TxGP71McBmg0Gg2dfkLRhVJRQqt74Is
13 | kc9V4Rp4n6F6baL4Lh19AkEA6pZZer0kg3Kv9hjhaITIKUYdfIp9vYnDRWbQlBmR
14 | atV8V9u9q2ETZvqfHpN+9Lu6NYR4yXIEIRf1bnIZ/mr9eQ==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/chain/test-com.key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQDYU6ysXD/5gKiWS7BY21qG3sowAtkZyPYUxUDJQeu7etHh+ZY7
3 | VNXov6xQWkkR2plgROAlaEA2fPbOtJy5WNbq50SYY+uicvjpabRKTWiGQcpnWGHm
4 | cOgI/q3CdVkkDvAvGnCDjKN3ZOhN1cUoYqlT0aEi9TZDp0YAqpdUctRyRwIDAQAB
5 | AoGBAIl/5elIWYGFPaMKSPSxuECxq2II7WVuTru1BRDnTabE0lMICW185tohuqz4
6 | NimbAJIoNTCRqv73Pwjz1AobZb6Nm7TDaahhstak6IlTYKcjXVBuM/UU4G13Kz/f
7 | hNVblv2cCn9CkeTNOvPZjYJXw/c4XlHasjDMMh8S83Q9095BAkEA+6oPzEiSsdo5
8 | RX9D0EV+Uv4ID08johKbcZdGbsp+mo+PQ9CYOlE67QcKf8J4Hp2SFmq7mpTvvS7F
9 | tA/a2WwJswJBANwNwsJre3QPJmJCBAGsIrPrw9rFKLiT0/ajyhT7kKfG4Rw9t55S
10 | lY9VPFOxAJF9lDo4QiFUHi/8Htvd0B78wx0CQFh5cRRgbzIXhgrosu6Ff+Otayf2
11 | qpBP+lX02M4aYmf0EGnG672U0SKDVy2TMKeSvckjvNCbi6z2xIqJCGdnlAECQFTh
12 | +f6E91oNfgDo9iKvA7PjfeklpE+OtnStOYZeg640SSFbrTilIovnlR2zaUS17DeI
13 | +/lfOUXJOx4UsfNCDQECQD7nndBJDJeSggFSJKcZ0RI59NVG8eGRSX7/3ycbq6+t
14 | guGI7WBvhDH4jNNL8jhuE+XuJuhhzOwP85872AFgIgw=
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/test2.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIChzCCAfACCQDjCkJpJUtZmjANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMC
3 | VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
4 | EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZI
5 | hvcNAQkBFhNvcGVucmVzdHlAZ21haWwuY29tMCAXDTE0MDkxMzAwMTgxMFoYDzIx
6 | MTQwODIwMDAxODEwWjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju
7 | aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5SZXN0eTES
8 | MBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZIhvcNAQkBFhNvcGVucmVzdHlAZ21h
9 | aWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDy+OVI2u5NBOeB2Cyz
10 | Gnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7f
11 | JrKFziJxMy4g4Kdn9G659vE7CWu/UAVjRUtc+mTBAEfjdbumizmHLG7DmnNhGl3R
12 | NGiVNLsUInSMGfUlJRzZJXhI4QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEMmRvyN
13 | N7uE24Tc6TR19JadNHK8g3YGktRoXWiqd/y0HY4NRPgvnK/nX7CY/wXa1j+uDO8K
14 | e6/Ldm5RZrjtvfHJmTSAu8zkqTJz8bqRDH7kzL5Ni2Ky2x8r9dtB0ImpOiSlwvZN
15 | snMvbrxEdwBiqlC9prV2f9aG+ACo1KnPL0j6
16 | -----END CERTIFICATE-----
17 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/chain/root-ca.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICkDCCAfmgAwIBAgIJAK3s1yAQ5tdfMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV
3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
4 | c2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEDAOBgNVBAMMB1Jvb3QgQ0EwIBcNMTQw
5 | OTIwMDM1NTU0WhgPMjExNDA4MjcwMzU1NTRaMGAxCzAJBgNVBAYTAlVTMRMwEQYD
6 | VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQK
7 | DAlPcGVuUmVzdHkxEDAOBgNVBAMMB1Jvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQAD
8 | gY0AMIGJAoGBAN7CcpCjiafBdl1KaExRcuutAF0/eq4/ht7L4/i0nPDzikscFJ/O
9 | aVyH3UpUF/KMq+72vom2bEbUeRROr1rL/JRe9raGlQtvdovHZt6f4c3/Coihtupp
10 | 9BXYrBCU4P+Bxai5gtTXGFvLC2a72qKcXDNeH+NxpIaemfPxSvemCYUXAgMBAAGj
11 | UDBOMB0GA1UdDgQWBBRWZcmLZVUnLqsU8CZGvbueoStBWDAfBgNVHSMEGDAWgBRW
12 | ZcmLZVUnLqsU8CZGvbueoStBWDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
13 | A4GBAGjMH6qkY+61311DERFhDuYzMSSZjH53qzFseq/chlIMGjrgJIMy6rl7T0AU
14 | 2hjvW+FOyhf5NqRrAQDTTuLbtXZ/ygiUformE8lR/SNRY/DVj1yarQkWUC5UpqOs
15 | GWG1VW9DHQAMFVkYwPO3XKeTXpEFOxPLHtXBYcVemCT4zo42
16 | -----END CERTIFICATE-----
17 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/test.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICqTCCAhICCQClDm1WkreW4jANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC
3 | VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
4 | EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQD
5 | DAh0ZXN0LmNvbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20wIBcN
6 | MTQwNzIxMDMyMzQ3WhgPMjE1MTA2MTMwMzIzNDdaMIGXMQswCQYDVQQGEwJVUzET
7 | MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG
8 | A1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRl
9 | c3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNvbTCBnzANBgkq
10 | hkiG9w0BAQEFAAOBjQAwgYkCgYEA6P18zUvtmaKQK2xePy8ZbFwSyTLw+jW6t9eZ
11 | aiTec8X3ibN9WemrxHzkTRikxP3cAQoITRuZiQvF4Q7DO6wMkz/b0zwfgX5uedGq
12 | 047AJP6n/mwlDOjGSNomBLoXQzo7tVe60ikEm3ZyDUqnJPJMt3hImO5XSop4MPMu
13 | Za9WhFcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA4OBb9bOyWB1//93nSXX1mdENZ
14 | IQeyTK0Dd6My76lnZxnZ4hTWrvvd0b17KLDU6JnS2N5ee3ATVkojPidRLWLIhnh5
15 | 0eXrcKalbO2Ce6nShoFvQCQKXN2Txmq2vO/Mud2bHAWwJALg+qi1Iih/gVYB9sct
16 | FLg8zFOzRlYiU+6Mmw==
17 | -----END CERTIFICATE-----
18 |
--------------------------------------------------------------------------------
/utils/conf/beta4.conf:
--------------------------------------------------------------------------------
1 | worker_processes auto;
2 |
3 | pid logs/nginx-beta4.pid;
4 |
5 | events {
6 | worker_connections 32768;
7 | }
8 |
9 | error_log logs/error_beta4.log;
10 | http {
11 | include mime.types;
12 | default_type application/octet-stream;
13 |
14 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
15 | '$status $body_bytes_sent "$http_referer" '
16 | '"$http_user_agent" "$http_x_forwarded_for"';
17 |
18 | sendfile on;
19 |
20 | keepalive_timeout 75;
21 | keepalive_requests 32768;
22 |
23 | access_log logs/access_beta4.log;
24 | server {
25 | listen 8023 backlog=16384;
26 | server_name localhost;
27 |
28 | location / {
29 | root html/beta4;
30 | index index.html index.htm;
31 | }
32 | error_page 500 502 503 504 /50x.html;
33 | location = /50x.html {
34 | root html;
35 | }
36 | location /abc {
37 | content_by_lua ' ngx.say("this is beta4 server @ location abc") ';
38 | }
39 |
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/utils/conf/fastcgi_params:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param QUERY_STRING $query_string;
3 | fastcgi_param REQUEST_METHOD $request_method;
4 | fastcgi_param CONTENT_TYPE $content_type;
5 | fastcgi_param CONTENT_LENGTH $content_length;
6 |
7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
8 | fastcgi_param REQUEST_URI $request_uri;
9 | fastcgi_param DOCUMENT_URI $document_uri;
10 | fastcgi_param DOCUMENT_ROOT $document_root;
11 | fastcgi_param SERVER_PROTOCOL $server_protocol;
12 | fastcgi_param HTTPS $https if_not_empty;
13 |
14 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
15 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
16 |
17 | fastcgi_param REMOTE_ADDR $remote_addr;
18 | fastcgi_param REMOTE_PORT $remote_port;
19 | fastcgi_param SERVER_ADDR $server_addr;
20 | fastcgi_param SERVER_PORT $server_port;
21 | fastcgi_param SERVER_NAME $server_name;
22 |
23 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
24 | fastcgi_param REDIRECT_STATUS 200;
25 |
--------------------------------------------------------------------------------
/utils/conf/fastcgi.conf:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
3 | fastcgi_param QUERY_STRING $query_string;
4 | fastcgi_param REQUEST_METHOD $request_method;
5 | fastcgi_param CONTENT_TYPE $content_type;
6 | fastcgi_param CONTENT_LENGTH $content_length;
7 |
8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
9 | fastcgi_param REQUEST_URI $request_uri;
10 | fastcgi_param DOCUMENT_URI $document_uri;
11 | fastcgi_param DOCUMENT_ROOT $document_root;
12 | fastcgi_param SERVER_PROTOCOL $server_protocol;
13 | fastcgi_param HTTPS $https if_not_empty;
14 |
15 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
16 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
17 |
18 | fastcgi_param REMOTE_ADDR $remote_addr;
19 | fastcgi_param REMOTE_PORT $remote_port;
20 | fastcgi_param SERVER_ADDR $server_addr;
21 | fastcgi_param SERVER_PORT $server_port;
22 | fastcgi_param SERVER_NAME $server_name;
23 |
24 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
25 | fastcgi_param REDIRECT_STATUS 200;
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 CNSRE 技术保障团队
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
24 |
--------------------------------------------------------------------------------
/utils/conf/stable.conf:
--------------------------------------------------------------------------------
1 |
2 | worker_processes auto;
3 |
4 | pid logs/stable.pid;
5 |
6 | error_log logs/error_stable.log ;
7 | events {
8 | worker_connections 32768;
9 | accept_mutex off;
10 | multi_accept on;
11 | use epoll;
12 |
13 | }
14 |
15 | http {
16 | include mime.types;
17 | default_type application/octet-stream;
18 |
19 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
20 | '$status $body_bytes_sent "$http_referer" '
21 | '"$http_user_agent" "$http_x_forwarded_for"';
22 |
23 | sendfile on;
24 |
25 | keepalive_timeout 75;
26 | keepalive_requests 32768;
27 |
28 | access_log logs/access_stable.log;
29 |
30 | server {
31 | listen 8040 backlog=16384;
32 | server_name localhost;
33 |
34 | location / {
35 | root html/stable;
36 | index index.html index.htm;
37 | }
38 |
39 | error_page 500 502 503 504 /50x.html;
40 | location = /50x.html {
41 | root html;
42 | }
43 |
44 | location /abc {
45 | content_by_lua ' ngx.say("this is stable server @ location abc") ';
46 | }
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/utils/conf/beta3.conf:
--------------------------------------------------------------------------------
1 | worker_processes auto;
2 |
3 | pid logs/nginx-beta3.pid;
4 |
5 | events {
6 | worker_connections 32768;
7 | accept_mutex off;
8 | multi_accept on;
9 |
10 | }
11 |
12 | error_log logs/error_beta3.log;
13 | http {
14 | include mime.types;
15 | default_type application/octet-stream;
16 |
17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
18 | '$status $body_bytes_sent "$http_referer" '
19 | '"$http_user_agent" "$http_x_forwarded_for"';
20 |
21 | sendfile on;
22 |
23 | keepalive_timeout 75;
24 | keepalive_requests 32768;
25 |
26 | access_log logs/access_beta3.log main;
27 |
28 | server {
29 | listen 8022 backlog=16384;
30 | server_name api.weibo.cn;
31 |
32 | location / {
33 | root html/beta3;
34 | index index.html index.htm;
35 | }
36 | error_page 500 502 503 504 /50x.html;
37 | location = /50x.html {
38 | root html;
39 | }
40 | location /abc {
41 | content_by_lua ' ngx.say("this is beta3 server @ location abc") ';
42 | }
43 |
44 |
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/utils/conf/beta2.conf:
--------------------------------------------------------------------------------
1 | worker_processes auto;
2 |
3 | pid logs/nginx-beta2.pid;
4 |
5 | events {
6 | worker_connections 32768;
7 | accept_mutex off;
8 | multi_accept on;
9 | }
10 |
11 | error_log logs/error_beta2.log;
12 | #error_log /var/log/nginx/error.log;
13 | http {
14 | include mime.types;
15 | default_type application/octet-stream;
16 |
17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
18 | '$status $body_bytes_sent "$http_referer" '
19 | '"$http_user_agent" "$http_x_forwarded_for"';
20 |
21 | sendfile on;
22 |
23 | keepalive_timeout 75;
24 | keepalive_requests 32768;
25 |
26 | access_log logs/access_beta2.log;
27 |
28 | server {
29 | listen 8021 backlog=16384;
30 | server_name localhost;
31 |
32 | location / {
33 | root html/beta2;
34 | index index.html index.htm;
35 | }
36 |
37 | error_page 500 502 503 504 /50x.html;
38 | location = /50x.html {
39 | root html;
40 | }
41 | location /abc {
42 | content_by_lua ' ngx.say("this is beta2 server @ location abc") ';
43 | }
44 |
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/utils/conf/beta1.conf:
--------------------------------------------------------------------------------
1 | worker_processes auto;
2 | #worker_cpu_affinity auto;
3 |
4 | pid logs/nginx-beta1.pid;
5 |
6 | events {
7 | worker_connections 32768;
8 | accept_mutex off;
9 | multi_accept on;
10 | use epoll;
11 | }
12 |
13 | error_log logs/error_beta1.log;
14 |
15 | http {
16 | include mime.types;
17 | default_type application/octet-stream;
18 |
19 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
20 | '$status $body_bytes_sent "$http_referer" '
21 | '"$http_user_agent" "$http_x_forwarded_for"';
22 |
23 | sendfile on;
24 |
25 | keepalive_timeout 75;
26 | keepalive_requests 32768;
27 |
28 | access_log logs/access_beta1.log;
29 | server {
30 | listen 8020 backlog=16384;
31 | server_name localhost;
32 |
33 | location / {
34 | root html/beta1;
35 | index index.html index.htm;
36 | }
37 |
38 | error_page 500 502 503 504 /50x.html;
39 | location = /50x.html {
40 | root html;
41 | }
42 | location /abc {
43 | content_by_lua ' ngx.say("this is beta1 server @ location abc") ';
44 | }
45 |
46 |
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/valgrind.suppress:
--------------------------------------------------------------------------------
1 | # Valgrind suppression file for LuaJIT 2.0.
2 | {
3 |
4 | Memcheck:Leak
5 | fun:malloc
6 | fun:ngx_alloc
7 | fun:ngx_event_process_init
8 | }
9 | {
10 |
11 | Memcheck:Param
12 | epoll_ctl(event)
13 | fun:epoll_ctl
14 | fun:ngx_epoll_add_event
15 | }
16 | {
17 |
18 | Memcheck:Cond
19 | fun:index
20 | fun:expand_dynamic_string_token
21 | fun:_dl_map_object
22 | fun:map_doit
23 | fun:_dl_catch_error
24 | fun:do_preload
25 | fun:dl_main
26 | fun:_dl_sysdep_start
27 | fun:_dl_start
28 | }
29 | {
30 |
31 | Memcheck:Param
32 | epoll_ctl(event)
33 | fun:epoll_ctl
34 | fun:ngx_epoll_init
35 | fun:ngx_event_process_init
36 | }
37 | {
38 |
39 | Memcheck:Param
40 | epoll_ctl(event)
41 | fun:epoll_ctl
42 | fun:ngx_epoll_notify_init
43 | fun:ngx_epoll_init
44 | fun:ngx_event_process_init
45 | }
46 | {
47 |
48 | Memcheck:Param
49 | epoll_ctl(event)
50 | fun:epoll_ctl
51 | fun:ngx_epoll_add_connection
52 | fun:ngx_event_connect_peer
53 | }
54 |
--------------------------------------------------------------------------------
/utils/conf/policy_sample:
--------------------------------------------------------------------------------
1 |
2 | iprange分流方式的策略样例:
3 |
4 | {
5 | "divtype":"iprange",
6 | "divdata":[
7 | {"range":{"start":1111, "end":2222}, "upstream":"beta1"},
8 | {"range":{"start":3333, "end":4444}, "upstream":"beta2"},
9 | {"range":{"start":5555, "end":6666}, "upstream":"beta1"},
10 | {"range":{"start":7777, "end":2130706439}, "upstream":"beta3"}]
11 | }
12 |
13 |
14 | uidrange分流方式的策略样例:
15 |
16 | {
17 | "divtype":"uidrange",
18 | "divdata":[
19 | {"range":{"start":1111, "end":2222}, "upstream":"beta1"},
20 | {"range":{"start":3333, "end":4444}, "upstream":"beta2"},
21 | {"range":{"start":5555, "end":6666}, "upstream":"beta1"},
22 | {"range":{"start":7777, "end":1661638660}, "upstream":"beta3"}]
23 | }
24 |
25 | uidsuffix分流方式的策略样例:
26 |
27 | {
28 | "divtype":"uidsuffix",
29 | "divdata":[
30 | {"suffix":"1", "upstream":"beta1"},
31 | {"suffix":"3", "upstream":"beta2"},
32 | {"suffix":"5", "upstream":"beta1"},
33 | {"suffix":"0", "upstream":"beta3"}]
34 | }
35 |
36 |
37 | uidappoint分流方式的策略样例:
38 |
39 | {
40 | "divtype":"uidappoint",
41 | "divdata":[
42 | {"uidset":["1143321","43214321","324124"], "upstream":"beta1"},
43 | {"uidset":["34321","324213", "234321"], "upstream":"beta2"},
44 | {"uidset":["245245","4325","5324","542"], "upstream":"beta1"},
45 | {"uidset":["546","563","656", "1661638660"], "upstream":"beta3"}]
46 | }
47 |
48 |
49 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 error = error
9 | local base = require "resty.core.base"
10 | local get_string_buf = base.get_string_buf
11 | local get_size_ptr = base.get_size_ptr
12 | local base = require "resty.core.base"
13 | local getfenv = getfenv
14 | local co_yield = coroutine._yield
15 |
16 |
17 | ffi.cdef[[
18 | int ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status,
19 | unsigned char *err, size_t *errlen);
20 | ]]
21 |
22 |
23 | local ERR_BUF_SIZE = 128
24 | local FFI_DONE = base.FFI_DONE
25 |
26 |
27 | ngx.exit = function (rc)
28 | local err = get_string_buf(ERR_BUF_SIZE)
29 | local errlen = get_size_ptr()
30 | local r = getfenv(0).__ngx_req
31 | if r == nil then
32 | return error("no request found")
33 | end
34 | errlen[0] = ERR_BUF_SIZE
35 | local rc = C.ngx_http_lua_ffi_exit(r, rc, err, errlen)
36 | if rc == 0 then
37 | -- print("yielding...")
38 | return co_yield()
39 | end
40 | if rc == FFI_DONE then
41 | return
42 | end
43 | return error(ffi_string(err, errlen[0]))
44 | end
45 |
46 |
47 | return {
48 | version = base.version
49 | }
50 |
--------------------------------------------------------------------------------
/utils/conf/vhost.conf:
--------------------------------------------------------------------------------
1 | lua_shared_dict api_root_sysConfig 1m;
2 | lua_shared_dict kv_api_root_upstream 100m;
3 |
4 | lua_shared_dict api_abc_sysConfig 1m;
5 | lua_shared_dict kv_api_abc_upstream 100m;
6 |
7 | server {
8 | listen 8030;
9 | server_name api.weibo.cn mapi.weibo.com;
10 |
11 | access_log logs/vhost_access.log main;
12 | error_log logs/vhost_error.log;
13 |
14 | proxy_redirect off;
15 | proxy_set_header Host $host;
16 | proxy_set_header X-Forwarded-For $remote_addr;
17 |
18 | set $redis_host '127.0.0.1';
19 | set $redis_port '6379';
20 | set $redis_uds '/var/run/redis.sock';
21 | set $redis_connect_timeout 10000;
22 | set $redis_dbid 0;
23 | set $redis_pool_size 1000;
24 | set $redis_keepalive_timeout 90000;
25 |
26 | location ~* /abc/(i|f)/ {
27 | set $hostkey $server_name.abc;
28 | set $sysConfig api_abc_sysConfig;
29 | set $kv_upstream kv_api_abc_upstream;
30 | set $backend 'stable';
31 | rewrite_by_lua_file '../diversion/diversion.lua';
32 | proxy_pass http://$backend;
33 | }
34 |
35 | location / {
36 |
37 | error_log logs/vhost_error.log debug;
38 |
39 | set $hostkey $server_name;
40 | set $sysConfig api_root_sysConfig;
41 | set $kv_upstream kv_api_root_upstream;
42 | set $backend 'stable';
43 | rewrite_by_lua_file '../diversion/diversion.lua';
44 | proxy_pass http://$backend;
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/lib/abtesting/userinfo/ipParser.lua:
--------------------------------------------------------------------------------
1 |
2 | local _M = {
3 | _VERSION = '0.01'
4 | }
5 |
6 | local ffi = require("ffi")
7 |
8 | ffi.cdef[[
9 | struct in_addr {
10 | uint32_t s_addr;
11 | };
12 |
13 | int inet_aton(const char *cp, struct in_addr *inp);
14 | uint32_t ntohl(uint32_t netlong);
15 |
16 | char *inet_ntoa(struct in_addr in);
17 | uint32_t htonl(uint32_t hostlong);
18 | ]]
19 |
20 | local C = ffi.C
21 |
22 | local ip2long = function(ip)
23 | local inp = ffi.new("struct in_addr[1]")
24 | if C.inet_aton(ip, inp) ~= 0 then
25 | return tonumber(C.ntohl(inp[0].s_addr))
26 | end
27 | return nil
28 | end
29 |
30 | local long2ip = function(long)
31 | if type(long) ~= "number" then
32 | return nil
33 | end
34 | local addr = ffi.new("struct in_addr")
35 | addr.s_addr = C.htonl(long)
36 | return ffi.string(C.inet_ntoa(addr))
37 | end
38 |
39 |
40 |
41 | _M.get = function()
42 | local ClientIP = ngx.req.get_headers()["X-Real-IP"]
43 | if ClientIP == nil then
44 | ClientIP = ngx.req.get_headers()["X-Forwarded-For"]
45 | if ClientIP then
46 | local colonPos = string.find(ClientIP, ' ')
47 | if colonPos then
48 | ClientIP = string.sub(ClientIP, 1, colonPos - 1)
49 | end
50 | end
51 | end
52 | if ClientIP == nil then
53 | ClientIP = ngx.var.remote_addr
54 | end
55 | if ClientIP then
56 | ClientIP = ip2long(ClientIP)
57 | end
58 | return ClientIP
59 | end
60 |
61 |
62 | return _M
63 |
64 |
65 |
--------------------------------------------------------------------------------
/utils/conf/nginx.conf:
--------------------------------------------------------------------------------
1 | #user www www;
2 | worker_processes auto;
3 | # worker_cpu_affinity auto; # openresty-1.9.15
4 | worker_rlimit_nofile 102400;
5 |
6 | error_log logs/error.log;
7 | pid logs/nginx.pid;
8 |
9 | events {
10 | use epoll;
11 | worker_connections 10240;
12 | }
13 |
14 | http {
15 |
16 | server_tokens off;
17 | sendfile on;
18 | tcp_nodelay on;
19 | tcp_nopush on;
20 | keepalive_timeout 0;
21 | charset utf-8;
22 |
23 | include mime.types;
24 | default_type application/json;
25 |
26 | log_format main '[$time_local]`$http_x_up_calling_line_id`"$request"`"$http_user_agent"`$staTus`[$remote_addr]`$http_x_log_uid`"$http_referer"`$request_time`$body_bytes_sent`$http_x_forwarded_proto`$http_x_forwarded_for`$http_host`$http_cookie`$upstream_response_time`xd';
27 | client_header_buffer_size 4k;
28 | large_client_header_buffers 8 4k;
29 | server_names_hash_bucket_size 128;
30 | client_max_body_size 8m;
31 |
32 | client_header_timeout 30s;
33 | client_body_timeout 30s;
34 | send_timeout 30s;
35 | lingering_close off;
36 |
37 | gzip on;
38 | gzip_vary on;
39 | gzip_min_length 1000;
40 | gzip_comp_level 6;
41 | gzip_types text/plain text/xml text/css application/javascript application/json;
42 | gzip_http_version 1.0;
43 |
44 | #index index.html index.shtml index.php;
45 |
46 |
47 | # include upstream.conf;
48 | include upstream.conf;
49 | include default.conf;
50 | include vhost.conf;
51 |
52 | # lua_code_cache on;
53 | lua_code_cache off;
54 | lua_package_path "../?.lua;../lib/?.lua;../lib/lua-resty-core/lib/?.lua;;";
55 | lua_need_request_body on;
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/utils/pytool/cmd.txt:
--------------------------------------------------------------------------------
1 | * dygateway命令脚本说明文档,* 号开头的行是注释,文档前面是接口使用说明
2 | * 所有命令的参数都不应该为空,以一个 | 号隔开
3 |
4 | host: 127.0.0.1:8080
5 |
6 | * del_upstream: stable
7 | * get_upstream:
8 |
9 | * add_upstream: stable| server 10.13.112.54:8020; server 10.13.112.54:8021 backup; keepalive 1000;
10 | * get_upstream:
11 | *
12 | * add_member: stable| 10.13.112.54| 8040| 1| 1| 10
13 | * get_member: stable
14 | *
15 | * del_member: stable| 10.13.112.54| 8040
16 | * get_member: stable
17 | *
18 | * setup_member: stable| 1| weight=9| fail_timeout=9| max_fails=9
19 | * get_member: stable
20 | *
21 | add_policy: {"1":{"divtype":"uidappoint","divdata":[{"uidset":[1234,5124,653],"upstream":"beta1"},{"uidset":[3214,652,145],"upstream":"beta2"}]},"2":{"divtype":"iprange","divdata":[{"range":{"start":1111,"end":2222},"upstream":"beta1"},{"range":{"start":3333,"end":4444},"upstream":"beta2"},{"range":{"start":7777,"end":8888},"upstream":"beta3"}]}} | {"1":{"divtype":"uidappoint","divdata":[{"uidset":[1234,5124,653],"upstream":"beta1"},{"uidset":[3214,652,145],"upstream":"beta2"}]},"2":{"divtype":"iprange","divdata":[{"range":{"start":1111,"end":2222},"upstream":"beta1"},{"range":{"start":3333,"end":4444},"upstream":"beta2"},{"range":{"start":7777,"end":8888},"upstream":"beta3"}]}}
22 |
23 | * del_policy: 1
24 | get_policy: 1| 2| 3
25 |
26 |
27 | del_runtime: api.weibo.cn
28 | get_runtime: api.weibo.cn
29 |
30 | set_runtime: api.weibo.cn = 2
31 | get_runtime: api.weibo.cn
32 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/count.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 3);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | -- local verbose = true
20 | local verbose = false
21 | local outfile = "$Test::Nginx::Util::ErrLogFile"
22 | -- local outfile = "/tmp/v.log"
23 | if verbose then
24 | local dump = require "jit.dump"
25 | dump.on(nil, outfile)
26 | else
27 | local v = require "jit.v"
28 | v.on(outfile)
29 | end
30 |
31 | require "resty.core"
32 | -- jit.opt.start("hotloop=1")
33 | -- jit.opt.start("loopunroll=1000000")
34 | -- jit.off()
35 | ';
36 | _EOC_
37 |
38 | #no_diff();
39 | no_long_string();
40 | run_tests();
41 |
42 | __DATA__
43 |
44 | === TEST 1: module size
45 | --- http_config eval: $::HttpConfig
46 | --- config
47 | location = /re {
48 | access_log off;
49 | content_by_lua '
50 | local base = require "resty.core.base"
51 | local n = 0
52 | for _, _ in pairs(base) do
53 | n = n + 1
54 | end
55 | ngx.say("base size: ", n)
56 | ';
57 | }
58 | --- request
59 | GET /re
60 |
61 | --- stap2
62 | global c
63 | probe process("$LIBLUA_PATH").function("rehashtab") {
64 | c++
65 | printf("rehash: %d\n", c)
66 | }
67 |
68 | --- response_body
69 | base size: 16
70 | --- no_error_log
71 | [error]
72 |
73 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 C = ffi.C
7 | local ngx = ngx
8 | local type = type
9 | local tostring = tostring
10 | local base = require "resty.core.base"
11 | local get_string_buf = base.get_string_buf
12 |
13 |
14 | ffi.cdef[[
15 | size_t ngx_http_lua_ffi_uri_escaped_length(const unsigned char *src,
16 | size_t len);
17 |
18 | void ngx_http_lua_ffi_escape_uri(const unsigned char *src, size_t len,
19 | unsigned char *dst);
20 |
21 | size_t ngx_http_lua_ffi_unescape_uri(const unsigned char *src,
22 | size_t len, unsigned char *dst);
23 | ]]
24 |
25 |
26 | ngx.escape_uri = function (s)
27 | if type(s) ~= 'string' then
28 | if not s then
29 | s = ''
30 | else
31 | s = tostring(s)
32 | end
33 | end
34 | local slen = #s
35 | local dlen = C.ngx_http_lua_ffi_uri_escaped_length(s, slen)
36 | -- print("dlen: ", tonumber(dlen))
37 | if dlen == slen then
38 | return s
39 | end
40 | local dst = get_string_buf(dlen)
41 | C.ngx_http_lua_ffi_escape_uri(s, slen, dst)
42 | return ffi_string(dst, dlen)
43 | end
44 |
45 |
46 | ngx.unescape_uri = function (s)
47 | if type(s) ~= 'string' then
48 | if not s then
49 | s = ''
50 | else
51 | s = tostring(s)
52 | end
53 | end
54 | local slen = #s
55 | local dlen = slen
56 | local dst = get_string_buf(dlen)
57 | dlen = C.ngx_http_lua_ffi_unescape_uri(s, slen, dst)
58 | return ffi_string(dst, dlen)
59 | end
60 |
61 |
62 | return {
63 | version = base.version,
64 | }
65 |
--------------------------------------------------------------------------------
/lib/abtesting/utils/redis.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingRedis"
2 | local _M = {}
3 |
4 | _M._VERSION = '0.0.1'
5 |
6 | local redis = require('resty.redis')
7 |
8 | _M.new = function(self, conf)
9 | self.host = conf.host
10 | self.port = conf.port
11 | self.uds = conf.uds
12 | self.timeout = conf.timeout
13 | self.dbid = conf.dbid
14 | self.poolsize = conf.poolsize
15 | self.idletime = conf.idletime
16 |
17 | local red = redis:new()
18 | return setmetatable({redis = red}, { __index = _M } )
19 | end
20 |
21 | _M.connectdb = function(self)
22 |
23 | local uds = self.uds
24 | local host = self.host
25 | local port = self.port
26 | local dbid = self.dbid
27 | local red = self.redis
28 |
29 | if not uds and not (host and port) then
30 | return nil, 'no uds or tcp avaliable provided'
31 | end
32 | if not dbid then dbid = 0 end
33 |
34 | local timeout = self.timeout
35 | if not timeout then
36 | timeout = 1000 -- 10s
37 | end
38 | red:set_timeout(timeout)
39 |
40 | local ok, err
41 | if uds then
42 | ok, err = red:connect('unix:'..uds)
43 | if ok then return red:select(dbid) end
44 | end
45 |
46 | if host and port then
47 | ok, err = red:connect(host, port)
48 | if ok then return red:select(dbid) end
49 | end
50 |
51 | return ok, err
52 | end
53 |
54 | _M.keepalivedb = function(self)
55 | local pool_max_idle_time = self.idletime --毫秒
56 | local pool_size = self.poolsize --连接池大小
57 |
58 | if not pool_size then pool_size = 1000 end
59 | if not pool_max_idle_time then pool_max_idle_time = 90000 end
60 |
61 | return self.redis:set_keepalive(pool_max_idle_time, pool_size)
62 | end
63 |
64 | return _M
65 |
--------------------------------------------------------------------------------
/lib/abtesting/utils/utils.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingUtils"
2 | local _M = {}
3 | _M._VERSION = '0.0.1'
4 |
5 | local cjson = require('cjson.safe')
6 | local log = require("abtesting.utils.log")
7 | --将doresp和dolog,与handler统一起来。
8 | --handler将返回一个table,结构为:
9 | --[[
10 | handler———errinfo————errcode————code
11 | | | |
12 | | | |————info
13 | | |
14 | | |————errdesc
15 | |
16 | |
17 | |
18 | |———errstack
19 | ]]--
20 |
21 | _M.dolog = function(info, desc, data, errstack)
22 | -- local errlog = 'ab_admin '
23 | local errlog = ''
24 | local code, err = info[1], info[2]
25 | local errcode = code
26 | local errinfo = desc and err..desc or err
27 |
28 | errlog = errlog .. 'code : '..errcode
29 | errlog = errlog .. ', desc : '..errinfo
30 | if data then
31 | errlog = errlog .. ', extrainfo : '..data
32 | end
33 | if errstack then
34 | errlog = errlog .. ', errstack : '..errstack
35 | end
36 | return errlog
37 | end
38 |
39 | _M.doresp = function(info, desc, data)
40 | local response = {}
41 |
42 | local code = info[1]
43 | local err = info[2]
44 | response.code = code
45 | response.desc = desc and err..desc or err
46 | if data then
47 | response.data = data
48 | end
49 |
50 | return cjson.encode(response)
51 | end
52 |
53 | _M.doerror = function(info, extrainfo)
54 | local errinfo = info[1]
55 | local errstack = info[2]
56 | local err, desc = errinfo[1], errinfo[2]
57 |
58 | local dolog, doresp = _M.dolog, _M.doresp
59 | local errlog = dolog(err, desc, extrainfo, errstack)
60 | log:errlog(errlog)
61 |
62 | local response = doresp(err, desc)
63 | return response
64 | end
65 |
66 | return _M
67 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/exit.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | log_level('warn');
9 |
10 | repeat_each(120);
11 | #repeat_each(2);
12 |
13 | plan tests => repeat_each() * (blocks() * 5);
14 |
15 | my $pwd = cwd();
16 |
17 | our $HttpConfig = <<_EOC_;
18 | lua_package_path "$pwd/lib/?.lua;\$prefix/html/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | run_tests();
37 |
38 | __DATA__
39 |
40 | === TEST 1: sanity
41 | --- http_config eval: $::HttpConfig
42 | --- config
43 | location = /t {
44 | content_by_lua '
45 | ngx.exit(403)
46 | ';
47 | }
48 | --- request
49 | GET /t
50 | --- response_body_like: 403 Forbidden
51 | --- error_code: 403
52 | --- no_error_log eval
53 | ["[error]",
54 | qr/ -- NYI: (?!FastFunc coroutine.yield)/,
55 | " bad argument"]
56 |
57 |
58 |
59 | === TEST 2: call ngx.exit() from a custom lua module
60 | --- http_config eval: $::HttpConfig
61 | --- config
62 | location = /t {
63 | content_by_lua '
64 | local foo = require "foo"
65 | foo.go()
66 | ';
67 | }
68 | --- user_files
69 | >>> foo.lua
70 | local exit = ngx.exit
71 |
72 | local function go()
73 | exit(403)
74 | return
75 | end
76 |
77 | return { go = go }
78 | --- request
79 | GET /t
80 | --- response_body_like: 403 Forbidden
81 | --- error_code: 403
82 | --- no_error_log eval
83 | ["[error]",
84 | qr/ -- NYI: (?!FastFunc coroutine.yield)/,
85 | " bad argument"]
86 |
87 |
--------------------------------------------------------------------------------
/lib/abtesting/error/errcode.lua:
--------------------------------------------------------------------------------
1 | local modulename = 'abtestingErrorInfo'
2 | local _M = {}
3 |
4 | _M._VERSION = '0.0.1'
5 |
6 | _M.info = {
7 | -- index code desc
8 | -- SUCCESS
9 | ["SUCCESS"] = { 200, 'success '},
10 |
11 | -- System Level ERROR
12 | ['REDIS_ERROR'] = { 40101, 'redis error for '},
13 | ['POLICY_DB_ERROR'] = { 40102, 'policy in db error '},
14 | ['RUNTIME_DB_ERROR'] = { 40103, 'runtime info in db error '},
15 |
16 | ['LUA_RUNTIME_ERROR'] = { 40201, 'lua runtime error '},
17 | ['BLANK_INFO_ERROR'] = { 40202, 'errinfo blank in handler '},
18 |
19 | -- Service Level ERROR
20 | -- input or parameter error
21 | ['PARAMETER_NONE'] = { 50101, 'expected parameter for '},
22 | ['PARAMETER_ERROR'] = { 50102, 'parameter error for '},
23 | ['PARAMETER_NEEDED'] = { 50103, 'need parameter for '},
24 | ['PARAMETER_TYPE_ERROR'] = { 50104, 'parameter type error for '},
25 |
26 | -- input policy error
27 | ['POLICY_INVALID_ERROR'] = { 50201, 'policies invalid for ' },
28 |
29 | ['POLICY_BUSY_ERROR'] = { 50202, 'policy is busy and policyID is ' },
30 |
31 | -- redis connect error
32 | ['REDIS_CONNECT_ERROR'] = { 50301, 'redis connect error for '},
33 | ['REDIS_KEEPALIVE_ERROR'] = { 50302, 'redis keepalive error for '},
34 |
35 | -- runtime error
36 | ['POLICY_BLANK_ERROR'] = { 50401, 'policy contains no data '},
37 | ['RUNTIME_BLANK_ERROR'] = { 50402, 'expect runtime info for '},
38 | ['MODULE_BLANK_ERROR'] = { 50403, 'no required module for '},
39 | ['USERINFO_BLANK_ERROR'] = { 50404, 'no userinfo fetched from '},
40 |
41 | ['ARG_BLANK_ERROR'] = { 50405, 'no arg fetched from req '},
42 | ['ACTION_BLANK_ERROR'] = { 50406, 'no action fetched from '},
43 |
44 | ['DOACTION_ERROR'] = { 50501, 'error during action of '},
45 |
46 | -- unknown reason
47 | ['UNKNOWN_ERROR'] = { 50601, 'unknown reason '},
48 | }
49 |
50 | return _M
51 |
--------------------------------------------------------------------------------
/lib/abtesting/utils/init.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingInit"
2 | local _M = {}
3 |
4 | _M._VERSION = '0.0.1'
5 |
6 | _M.redisConf = {
7 | ["uds"] = ngx.var.redis_uds ,
8 | ["host"] = ngx.var.redis_host,
9 | ["port"] = ngx.var.redis_port,
10 | ["poolsize"] = ngx.var.redis_pool_size,
11 | ["idletime"] = ngx.var.redis_keepalive_timeout ,
12 | ["timeout"] = ngx.var.redis_connect_timeout,
13 | ["dbid"] = ngx.var.redis_dbid,
14 | }
15 |
16 | _M.divtypes = {
17 | ["iprange"] = 'ipParser',
18 | ["uidrange"] = 'uidParser',
19 | ["uidsuffix"] = 'uidParser',
20 | ["uidappoint"] = 'uidParser',
21 | ["arg_city"] = 'cityParser',
22 |
23 | ["url"] = 'urlParser'
24 | }
25 |
26 | _M.prefixConf = {
27 | ["policyLibPrefix"] = 'ab:policies',
28 | ["policyGroupPrefix"] = 'ab:policygroups',
29 | ["runtimeInfoPrefix"] = 'ab:runtimeInfo',
30 | ["domainname"] = ngx.var.domain_name,
31 | }
32 |
33 | _M.divConf = {
34 | ["default_backend"] = ngx.var.default_backend,
35 | ["shdict_expire"] = 60, -- in s
36 | -- ["shdict_expire"] = ngx.var.shdict_expire,
37 | }
38 |
39 | _M.cacheConf = {
40 | ['timeout'] = ngx.var.lock_expire,
41 | ['runtimeInfoLock'] = ngx.var.rt_cache_lock,
42 | ['upstreamLock'] = ngx.var.up_cache_lock,
43 | }
44 |
45 | _M.indices = {
46 | 'first', 'second', 'third',
47 | 'forth', 'fifth', 'sixth',
48 | 'seventh', 'eighth', 'ninth'
49 | }
50 |
51 | _M.fields = {
52 | ['divModulename'] = 'divModulename',
53 | ['divDataKey'] = 'divDataKey',
54 | ['userInfoModulename'] = 'userInfoModulename',
55 | ['divtype'] = 'divtype',
56 | ['divdata'] = 'divdata',
57 | ['idCount'] = 'idCount',
58 | ['divsteps'] = 'divsteps'
59 | }
60 |
61 | _M.loglv = {
62 |
63 | ['err'] = ngx.ERR,
64 | ['info'] = ngx.INFO, ['warn'] = ngx.WARN,
65 | ['debug'] = ngx.DEBUG,
66 | }
67 |
68 | return _M
69 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/ctx.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | log_level('warn');
9 |
10 | #repeat_each(120);
11 | repeat_each(2);
12 |
13 | plan tests => repeat_each() * (blocks() * 6);
14 |
15 | my $pwd = cwd();
16 |
17 | our $HttpConfig = <<_EOC_;
18 | lua_package_path "$pwd/lib/?.lua;\$prefix/html/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | check_accum_error_log();
37 | run_tests();
38 |
39 | __DATA__
40 |
41 | === TEST 1: get ngx.ctx
42 | --- http_config eval: $::HttpConfig
43 | --- config
44 | location = /t {
45 | content_by_lua '
46 | for i = 1, 100 do
47 | ngx.ctx.foo = i
48 | end
49 | ngx.say("ctx.foo = ", ngx.ctx.foo)
50 | ';
51 | }
52 | --- request
53 | GET /t
54 | --- response_body
55 | ctx.foo = 100
56 | --- no_error_log
57 | [error]
58 | -- NYI:
59 | bad argument
60 | --- error_log eval
61 | qr/\[TRACE\s+\d+\s+content_by_lua\(nginx\.conf:\d+\):2 loop\]/
62 |
63 |
64 |
65 | === TEST 2: set ngx.ctx
66 | --- http_config eval: $::HttpConfig
67 | --- config
68 | location = /t {
69 | content_by_lua '
70 | for i = 1, 100 do
71 | ngx.ctx = {foo = i}
72 | end
73 | ngx.say("ctx.foo = ", ngx.ctx.foo)
74 | ';
75 | }
76 | --- request
77 | GET /t
78 | --- response_body
79 | ctx.foo = 100
80 | --- no_error_log
81 | [error]
82 | -- NYI:
83 | bad argument
84 | --- error_log eval
85 | qr/\[TRACE\s+\d+\s+content_by_lua\(nginx\.conf:\d+\):2 loop\]/
86 |
87 |
--------------------------------------------------------------------------------
/doc/注释文档.md:
--------------------------------------------------------------------------------
1 |
46 |
47 |
51 |
52 |
63 |
64 | 系统介绍
65 | =========================
66 | **dygateway**的功能由**nginx.conf**和**luacode**实现。
67 |
68 | * **luacode**中包括**ab**和**dyupsc**两部分,前者是灰度系统的lua代码,后者是动态upstream系统的lua代码
69 | * ab文件夹是灰度系统源码,包括策略管理、运行时信息管理以及分流功能
70 | * dyupsc文件夹是动态upstream系统源码,包括动态upstream和member功能
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 ngx = ngx
9 | local type = type
10 | local tostring = tostring
11 | local error = error
12 | local base = require "resty.core.base"
13 |
14 |
15 | ffi.cdef[[
16 | void ngx_http_lua_ffi_md5_bin(const unsigned char *src, size_t len,
17 | unsigned char *dst);
18 |
19 | void ngx_http_lua_ffi_md5(const unsigned char *src, size_t len,
20 | unsigned char *dst);
21 |
22 | int ngx_http_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
23 | unsigned char *dst);
24 | ]]
25 |
26 |
27 | local MD5_DIGEST_LEN = 16
28 | local md5_buf = ffi_new("unsigned char[?]", MD5_DIGEST_LEN)
29 |
30 | ngx.md5_bin = function (s)
31 | if type(s) ~= 'string' then
32 | if not s then
33 | s = ''
34 | else
35 | s = tostring(s)
36 | end
37 | end
38 | C.ngx_http_lua_ffi_md5_bin(s, #s, md5_buf)
39 | return ffi_string(md5_buf, MD5_DIGEST_LEN)
40 | end
41 |
42 |
43 | local MD5_HEX_DIGEST_LEN = MD5_DIGEST_LEN * 2
44 | local md5_hex_buf = ffi_new("unsigned char[?]", MD5_HEX_DIGEST_LEN)
45 |
46 | ngx.md5 = function (s)
47 | if type(s) ~= 'string' then
48 | if not s then
49 | s = ''
50 | else
51 | s = tostring(s)
52 | end
53 | end
54 | C.ngx_http_lua_ffi_md5(s, #s, md5_hex_buf)
55 | return ffi_string(md5_hex_buf, MD5_HEX_DIGEST_LEN)
56 | end
57 |
58 |
59 | local SHA_DIGEST_LEN = 20
60 | local sha_buf = ffi_new("unsigned char[?]", SHA_DIGEST_LEN)
61 |
62 | ngx.sha1_bin = function (s)
63 | if type(s) ~= 'string' then
64 | if not s then
65 | s = ''
66 | else
67 | s = tostring(s)
68 | end
69 | end
70 | local ok = C.ngx_http_lua_ffi_sha1_bin(s, #s, sha_buf)
71 | if ok == 0 then
72 | return error("SHA-1 support missing in Nginx")
73 | end
74 | return ffi_string(sha_buf, SHA_DIGEST_LEN)
75 | end
76 |
77 |
78 | return {
79 | version = base.version
80 | }
81 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/status.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | log_level('warn');
9 |
10 | #repeat_each(120);
11 | repeat_each(2);
12 |
13 | plan tests => repeat_each() * (blocks() * 7);
14 |
15 | my $pwd = cwd();
16 |
17 | our $HttpConfig = <<_EOC_;
18 | lua_package_path "$pwd/lib/?.lua;\$prefix/html/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | check_accum_error_log();
37 | run_tests();
38 |
39 | __DATA__
40 |
41 | === TEST 1: get ngx.status
42 | --- http_config eval: $::HttpConfig
43 | --- config
44 | location = /t {
45 | return 201;
46 | header_filter_by_lua '
47 | local sum = 0
48 | for i = 1, 100 do
49 | sum = sum + ngx.status
50 | end
51 | ngx.log(ngx.WARN, "sum: ", sum)
52 | ';
53 | }
54 | --- request
55 | GET /t
56 | --- response_body
57 | --- error_code: 201
58 | --- no_error_log
59 | [error]
60 | -- NYI:
61 | bad argument
62 | --- error_log eval
63 | ["sum: 20100,",
64 | qr/\[TRACE\s+\d+\s+header_filter_by_lua:3 loop\]/
65 | ]
66 |
67 |
68 |
69 | === TEST 2: set ngx.status
70 | --- http_config eval: $::HttpConfig
71 | --- config
72 | location = /t {
73 | return 201;
74 | header_filter_by_lua '
75 | for i = 100, 200 do
76 | ngx.status = i
77 | end
78 | ngx.log(ngx.WARN, "status: ", ngx.status)
79 | ';
80 | }
81 | --- request
82 | GET /t
83 | --- response_body
84 | --- no_error_log
85 | [error]
86 | -- NYI:
87 | bad argument
88 | --- error_log eval
89 | ["status: 200,",
90 | qr/\[TRACE\s+\d+\s+header_filter_by_lua:2 loop\]/
91 | ]
92 |
93 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/worker.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 6);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_shared_dict dogs 1m;
18 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on("b", "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | check_accum_error_log();
37 | run_tests();
38 |
39 | __DATA__
40 |
41 | === TEST 1: ngx.worker.exiting
42 | --- http_config eval: $::HttpConfig
43 | --- config
44 | location = /t {
45 | content_by_lua '
46 | local v
47 | local exiting = ngx.worker.exiting
48 | for i = 1, 400 do
49 | v = exiting()
50 | end
51 | ngx.say(v)
52 | ';
53 | }
54 | --- request
55 | GET /t
56 | --- response_body
57 | false
58 | --- error_log eval
59 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
60 | --- no_error_log
61 | [error]
62 | -- NYI:
63 | stitch
64 |
65 |
66 |
67 | === TEST 2: ngx.worker.pid
68 | --- http_config eval: $::HttpConfig
69 | --- config
70 | location = /t {
71 | content_by_lua '
72 | local v
73 | local pid = ngx.worker.pid
74 | for i = 1, 400 do
75 | v = pid()
76 | end
77 | ngx.say(v == tonumber(ngx.var.pid))
78 | ngx.say(v)
79 | ';
80 | }
81 | --- request
82 | GET /t
83 | --- response_body_like chop
84 | ^true
85 | \d+$
86 | --- error_log eval
87 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
88 | --- no_error_log
89 | [error]
90 | -- NYI:
91 | stitch
92 |
93 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 | local error = error
20 |
21 |
22 | ffi.cdef[[
23 | int ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r);
24 | int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref);
25 | ]]
26 |
27 |
28 | local _M = {
29 | _VERSION = base.version
30 | }
31 |
32 |
33 | local function get_ctx_table()
34 | local r = getfenv(0).__ngx_req
35 |
36 | if not r then
37 | return error("no request found")
38 | end
39 |
40 | local ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref(r)
41 | if ctx_ref == FFI_NO_REQ_CTX then
42 | return error("no request ctx found")
43 | end
44 |
45 | local ctxs = registry.ngx_lua_ctx_tables
46 | if ctx_ref < 0 then
47 | local ctx = new_tab(0, 4)
48 | ctx_ref = ref_in_table(ctxs, ctx)
49 | if C.ngx_http_lua_ffi_set_ctx_ref(r, ctx_ref) ~= FFI_OK then
50 | return nil
51 | end
52 | return ctx
53 | end
54 | return ctxs[ctx_ref]
55 | end
56 | register_getter("ctx", get_ctx_table)
57 |
58 |
59 | local function set_ctx_table(ctx)
60 | local r = getfenv(0).__ngx_req
61 |
62 | if not r then
63 | return error("no request found")
64 | end
65 |
66 | local ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref(r)
67 | if ctx_ref == FFI_NO_REQ_CTX then
68 | return error("no request ctx found")
69 | end
70 |
71 | local ctxs = registry.ngx_lua_ctx_tables
72 | if ctx_ref < 0 then
73 | ctx_ref = ref_in_table(ctxs, ctx)
74 | C.ngx_http_lua_ffi_set_ctx_ref(r, ctx_ref)
75 | return
76 | end
77 | ctxs[ctx_ref] = ctx
78 | end
79 | register_setter("ctx", set_ctx_table)
80 |
81 |
82 | return _M
83 |
--------------------------------------------------------------------------------
/lib/abtesting/diversion/arg_city.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingDiversionArgCity"
2 |
3 | local _M = {}
4 | local mt = { __index = _M }
5 | _M._VERSION = "0.0.1"
6 |
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 |
9 | local k_city = 'city'
10 | local k_upstream = 'upstream'
11 |
12 | _M.new = function(self, database, policyLib)
13 | if not database then
14 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
15 | end if not policyLib then
16 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
17 | end
18 |
19 | self.database = database
20 | self.policyLib = policyLib
21 | return setmetatable(self, mt)
22 | end
23 |
24 | -- policy is in format as {{city = 'BJ0101', upstream = '192.132.23.125'}}
25 | _M.check = function(self, policy)
26 | for _, v in pairs(policy) do
27 | local city = v[k_city]
28 | local upstream = v[k_upstream]
29 |
30 | if not city or not upstream then
31 | local info = ERRORINFO.POLICY_INVALID_ERROR
32 | local desc = ' need '..k_city..' and '..k_upstream
33 | return {false, info, desc}
34 | end
35 |
36 | end
37 |
38 | return {true}
39 | end
40 |
41 | _M.set = function(self, policy)
42 | local database = self.database
43 | local policyLib = self.policyLib
44 |
45 | database:init_pipeline()
46 | for _, v in pairs(policy) do
47 | database:hset(policyLib, v[k_city], v[k_upstream])
48 | end
49 | local ok, err = database:commit_pipeline()
50 | if not ok then
51 | error{ERRORINFO.REDIS_ERROR, err}
52 | end
53 |
54 | end
55 |
56 | _M.get = function(self)
57 | local database = self.database
58 | local policyLib = self.policyLib
59 |
60 | local data, err = database:hgetall(policyLib)
61 | if not data then
62 | error{ERRORINFO.REDIS_ERROR, err}
63 | end
64 |
65 | return data
66 | end
67 |
68 | _M.getUpstream = function(self, city)
69 |
70 | local database = self.database
71 | local policyLib = self.policyLib
72 |
73 | local upstream, err = database:hget(policyLib , city)
74 | if not upstream then error{ERRORINFO.REDIS_ERROR, err} end
75 |
76 | if upstream == ngx.null then
77 | return nil
78 | else
79 | return upstream
80 | end
81 |
82 | end
83 |
84 |
85 | return _M
86 |
--------------------------------------------------------------------------------
/lib/abtesting/diversion/url.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingDiversionUrl"
2 |
3 | local _M = {}
4 | local mt = { __index = _M }
5 | _M._VERSION = "0.0.1"
6 |
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 |
9 | local k_url = 'url'
10 | local k_upstream = 'upstream'
11 |
12 | _M.new = function(self, database, policyLib)
13 | if not database then
14 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
15 | end if not policyLib then
16 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
17 | end
18 |
19 | self.database = database
20 | self.policyLib = policyLib
21 | return setmetatable(self, mt)
22 | end
23 |
24 | -- policy is in format as {{url = '/a/b/c/d', upstream = '192.132.23.125'}}
25 | _M.check = function(self, policy)
26 | for _, v in pairs(policy) do
27 | local url = v[k_url]
28 | local upstream = v[k_upstream]
29 |
30 | if not url or not upstream then
31 | local info = ERRORINFO.POLICY_INVALID_ERROR
32 | local desc = ' need '..k_url..' and '..k_upstream
33 | return {false, info, desc}
34 | end
35 |
36 | -- if suffix < 0 or suffix > 9 then
37 | -- local info = ERRORINFO.POLICY_INVALID_ERROR
38 | -- local desc = 'suffix is not between [0 and 10]'
39 | -- return {false, info, desc}
40 | -- end
41 | end
42 |
43 | return {true}
44 | end
45 |
46 | _M.set = function(self, policy)
47 | local database = self.database
48 | local policyLib = self.policyLib
49 |
50 | database:init_pipeline()
51 | for _, v in pairs(policy) do
52 | database:hset(policyLib, v[k_url], v[k_upstream])
53 | end
54 | local ok, err = database:commit_pipeline()
55 | if not ok then
56 | error{ERRORINFO.REDIS_ERROR, err}
57 | end
58 |
59 | end
60 |
61 | _M.get = function(self)
62 | local database = self.database
63 | local policyLib = self.policyLib
64 |
65 | local data, err = database:hgetall(policyLib)
66 | if not data then
67 | error{ERRORINFO.REDIS_ERROR, err}
68 | end
69 |
70 | return data
71 | end
72 |
73 | _M.getUpstream = function(self, url)
74 |
75 | local url = url;
76 | local database = self.database
77 | local policyLib = self.policyLib
78 |
79 | local upstream, err = database:hget(policyLib , url)
80 | if not upstream then error{ERRORINFO.REDIS_ERROR, err} end
81 |
82 | if upstream == ngx.null then
83 | return nil
84 | else
85 | return upstream
86 | end
87 |
88 | end
89 |
90 |
91 | return _M
92 |
--------------------------------------------------------------------------------
/admin/action.lua:
--------------------------------------------------------------------------------
1 | local policyModule = require('abtesting.adapter.policy')
2 | local redisModule = require('abtesting.utils.redis')
3 | local systemConf = require('abtesting.utils.init')
4 | local handler = require('abtesting.error.handler').handler
5 | local utils = require('abtesting.utils.utils')
6 | local ERRORINFO = require('abtesting.error.errcode').info
7 |
8 | local cjson = require('cjson.safe')
9 | local doresp = utils.doresp
10 | local dolog = utils.dolog
11 |
12 | local redisConf = systemConf.redisConf
13 | local divtypes = systemConf.divtypes
14 | local prefixConf = systemConf.prefixConf
15 | local policyLib = prefixConf.policyLibPrefix
16 | local runtimeLib = prefixConf.runtimeInfoPrefix
17 | local domain_name = prefixConf.domainname
18 |
19 |
20 | local policy = require("admin/policy")
21 | local runtime = require('admin/runtime')
22 | local ab_action = {}
23 |
24 | ab_action.policy_check = policy.check
25 | ab_action.policy_set = policy.set
26 | ab_action.policy_get = policy.get
27 | ab_action.policy_del = policy.del
28 |
29 | ab_action.runtime_set = runtime.set
30 | ab_action.runtime_del = runtime.del
31 | ab_action.runtime_get = runtime.get
32 |
33 | local get_uriargs_error = function()
34 | local info = ERRORINFO.ACTION_BLANK_ERROR
35 | local response = doresp(info, 'user req')
36 | dolog(info, desc)
37 | ngx.say(response)
38 | return
39 | end
40 |
41 | local get_action_error = function()
42 | local info = ERRORINFO.ACTION_BLANK_ERROR
43 | local response = doresp(info, 'user req')
44 | dolog(info, desc)
45 | ngx.say(response)
46 | return
47 | end
48 |
49 | local do_action_error = function()
50 | local info = ERRORINFO.DOACTION_ERROR
51 | local desc = action
52 | local response = doresp(info, desc)
53 | dolog(info, desc)
54 | ngx.say(response)
55 | return
56 | end
57 |
58 | local red = redisModule:new(redisConf)
59 | local ok, err = red:connectdb()
60 | if not ok then
61 | local info = ERRORINFO.REDIS_CONNECT_ERROR
62 | local response = doresp(info, err)
63 | dolog(info, desc)
64 | ngx.say(response)
65 | return
66 | end
67 |
68 | local args = ngx.req.get_uri_args()
69 | if args then
70 | local action = args.action
71 | local do_action = ab_action[action]
72 | if do_action then
73 | do_action({['db']=red})
74 | -- local ok, info = do_action(policy, {['db']=red})
75 | -- if not ok then
76 | -- do_action_error()
77 | -- end
78 | else
79 | doaction_error()
80 | end
81 | else
82 | get_uriargs_error()
83 | end
84 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 C = ffi.C
9 | local ngx = ngx
10 | local type = type
11 | local tostring = tostring
12 | local error = error
13 | local get_string_buf = base.get_string_buf
14 | local get_size_ptr = base.get_size_ptr
15 | local floor = math.floor
16 |
17 |
18 | ffi.cdef[[
19 | size_t ngx_http_lua_ffi_encode_base64(const unsigned char *src,
20 | size_t len, unsigned char *dst,
21 | int no_padding);
22 |
23 | int ngx_http_lua_ffi_decode_base64(const unsigned char *src,
24 | size_t len, unsigned char *dst,
25 | size_t *dlen);
26 | ]]
27 |
28 |
29 | local function base64_encoded_length(len, no_padding)
30 | return no_padding and floor((len * 8 + 5) / 6) or
31 | floor((len + 2) / 3) * 4
32 | end
33 |
34 |
35 | ngx.encode_base64 = function (s, no_padding)
36 | if type(s) ~= 'string' then
37 | if not s then
38 | s = ''
39 | else
40 | s = tostring(s)
41 | end
42 | end
43 |
44 | local slen = #s
45 | local no_padding_bool = false;
46 | local no_padding_int = 0;
47 |
48 | if no_padding then
49 | if no_padding ~= true then
50 | return error("boolean argument only")
51 | end
52 |
53 | no_padding_bool = true
54 | no_padding_int = 1;
55 | end
56 |
57 | local dlen = base64_encoded_length(slen, no_padding_bool)
58 | local dst = get_string_buf(dlen)
59 | local r_dlen = C.ngx_http_lua_ffi_encode_base64(s, slen, dst,
60 | no_padding_int)
61 | -- if dlen ~= r_dlen then error("discrepancy in len") end
62 | return ffi_string(dst, r_dlen)
63 | end
64 |
65 |
66 | local function base64_decoded_length(len)
67 | return floor((len + 3) / 4) * 3
68 | end
69 |
70 |
71 | ngx.decode_base64 = function (s)
72 | if type(s) ~= 'string' then
73 | return error("string argument only")
74 | end
75 | local slen = #s
76 | local dlen = base64_decoded_length(slen)
77 | -- print("dlen: ", tonumber(dlen))
78 | local dst = get_string_buf(dlen)
79 | local pdlen = get_size_ptr()
80 | local ok = C.ngx_http_lua_ffi_decode_base64(s, slen, dst, pdlen)
81 | if ok == 0 then
82 | return nil
83 | end
84 | return ffi_string(dst, pdlen[0])
85 | end
86 |
87 |
88 | return {
89 | version = base.version
90 | }
91 |
--------------------------------------------------------------------------------
/lib/abtesting/diversion/uidsuffix.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingDiversionUidsuffix"
2 |
3 | local _M = {}
4 | local mt = { __index = _M }
5 | _M._VERSION = "0.0.1"
6 |
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 |
9 | local k_suffix = 'suffix'
10 | local k_upstream = 'upstream'
11 |
12 | _M.new = function(self, database, policyLib)
13 | if not database then
14 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
15 | end if not policyLib then
16 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
17 | end
18 |
19 | self.database = database
20 | self.policyLib = policyLib
21 | return setmetatable(self, mt)
22 | end
23 |
24 | -- policy is in format as {{suffix = '4', upstream = '192.132.23.125'}}
25 | _M.check = function(self, policy)
26 | for _, v in pairs(policy) do
27 | local suffix = tonumber(v[k_suffix])
28 | local upstream = v[k_upstream]
29 |
30 | if not suffix or not upstream then
31 | local info = ERRORINFO.POLICY_INVALID_ERROR
32 | local desc = ' need '..k_suffix..' and '..k_upstream
33 | return {false, info, desc}
34 | end
35 |
36 | if suffix < 0 or suffix > 9 then
37 | local info = ERRORINFO.POLICY_INVALID_ERROR
38 | local desc = 'suffix is not between [0 and 10]'
39 | return {false, info, desc}
40 | end
41 | end
42 |
43 | return {true}
44 | end
45 |
46 | _M.set = function(self, policy)
47 | local database = self.database
48 | local policyLib = self.policyLib
49 |
50 | database:init_pipeline()
51 | for _, v in pairs(policy) do
52 | database:hset(policyLib, v[k_suffix], v[k_upstream])
53 | end
54 | local ok, err = database:commit_pipeline()
55 | if not ok then
56 | error{ERRORINFO.REDIS_ERROR, err}
57 | end
58 |
59 | end
60 |
61 | _M.get = function(self)
62 | local database = self.database
63 | local policyLib = self.policyLib
64 |
65 | local data, err = database:hgetall(policyLib)
66 | if not data then
67 | error{ERRORINFO.REDIS_ERROR, err}
68 | end
69 |
70 | return data
71 | end
72 |
73 | _M.getUpstream = function(self, uid)
74 | if not tonumber(uid) then
75 | return nil
76 | end
77 |
78 | local suffix = uid % 10;
79 | local database = self.database
80 | local policyLib = self.policyLib
81 |
82 | local upstream, err = database:hget(policyLib , suffix)
83 | if not upstream then error{ERRORINFO.REDIS_ERROR, err} end
84 |
85 | if upstream == ngx.null then
86 | return nil
87 | else
88 | return upstream
89 | end
90 |
91 | end
92 |
93 |
94 | return _M
95 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/time.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 6);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | -- local verbose = true
20 | local verbose = false
21 | local outfile = "$Test::Nginx::Util::ErrLogFile"
22 | -- local outfile = "/tmp/v.log"
23 | if verbose then
24 | local dump = require "jit.dump"
25 | dump.on(nil, outfile)
26 | else
27 | local v = require "jit.v"
28 | v.on(outfile)
29 | end
30 |
31 | require "resty.core"
32 | -- jit.opt.start("hotloop=1")
33 | -- jit.opt.start("loopunroll=1000000")
34 | -- jit.off()
35 | ';
36 | _EOC_
37 |
38 | #no_diff();
39 | no_long_string();
40 | check_accum_error_log();
41 | run_tests();
42 |
43 | __DATA__
44 |
45 | === TEST 1: ngx.now()
46 | --- http_config eval: $::HttpConfig
47 | --- config
48 | location = /t {
49 | access_log off;
50 | content_by_lua '
51 | local t
52 | for i = 1, 500 do
53 | t = ngx.now()
54 | end
55 | ngx.sleep(0.10)
56 | local elapsed = ngx.now() - t
57 | ngx.say(t > 1399867351)
58 | ngx.say(">= 0.099: ", elapsed >= 0.099)
59 | ngx.say("< 0.11: ", elapsed < 0.11)
60 | -- ngx.say(t, " ", elapsed)
61 | ';
62 | }
63 | --- request
64 | GET /t
65 | --- response_body
66 | true
67 | >= 0.099: true
68 | < 0.11: true
69 |
70 | --- error_log eval
71 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
72 | --- no_error_log
73 | [error]
74 | bad argument type
75 | stitch
76 |
77 |
78 |
79 | === TEST 2: ngx.time()
80 | --- http_config eval: $::HttpConfig
81 | --- config
82 | location = /t {
83 | access_log off;
84 | content_by_lua '
85 | local t
86 | for i = 1, 500 do
87 | t = ngx.time()
88 | end
89 | ngx.say(t > 1400960598)
90 | local diff = os.time() - t
91 | ngx.say(diff <= 1)
92 | ';
93 | }
94 | --- request
95 | GET /t
96 | --- response_body
97 | true
98 | true
99 |
100 | --- error_log eval
101 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
102 | --- no_error_log
103 | [error]
104 | bad argument type
105 | stitch
106 |
107 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/ngx/balancer.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_str = ffi.string
10 | local errmsg = base.get_errmsg_ptr()
11 | local FFI_OK = base.FFI_OK
12 | local FFI_ERROR = base.FFI_ERROR
13 | local int_out = ffi.new("int[1]")
14 | local getfenv = getfenv
15 | local error = error
16 | local type = type
17 | local tonumber = tonumber
18 |
19 |
20 | ffi.cdef[[
21 | int ngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r,
22 | const unsigned char *addr, size_t addr_len, int port, char **err);
23 |
24 | int ngx_http_lua_ffi_balancer_set_more_tries(ngx_http_request_t *r,
25 | int count, char **err);
26 |
27 | int ngx_http_lua_ffi_balancer_get_last_failure(ngx_http_request_t *r,
28 | int *status, char **err);
29 | ]]
30 |
31 |
32 | local peer_state_names = {
33 | [1] = "keepalive",
34 | [2] = "next",
35 | [4] = "failed",
36 | }
37 |
38 |
39 | local _M = { version = base.version }
40 |
41 |
42 | function _M.set_current_peer(addr, port)
43 | local r = getfenv(0).__ngx_req
44 | if not r then
45 | return error("no request found")
46 | end
47 |
48 | if not port then
49 | port = 0
50 | elseif type(port) ~= "number" then
51 | port = tonumber(port)
52 | end
53 |
54 | local rc = C.ngx_http_lua_ffi_balancer_set_current_peer(r, addr, #addr,
55 | port, errmsg)
56 | if rc == FFI_OK then
57 | return true
58 | end
59 |
60 | return nil, ffi_str(errmsg[0])
61 | end
62 |
63 |
64 | function _M.set_more_tries(count)
65 | local r = getfenv(0).__ngx_req
66 | if not r then
67 | return error("no request found")
68 | end
69 |
70 | local rc = C.ngx_http_lua_ffi_balancer_set_more_tries(r, count, errmsg)
71 | if rc == FFI_OK then
72 | if errmsg[0] == nil then
73 | return true
74 | end
75 | return true, ffi_str(errmsg[0]) -- return the warning
76 | end
77 |
78 | return nil, ffi_str(errmsg[0])
79 | end
80 |
81 |
82 | function _M.get_last_failure()
83 | local r = getfenv(0).__ngx_req
84 | if not r then
85 | return error("no request found")
86 | end
87 |
88 | local state = C.ngx_http_lua_ffi_balancer_get_last_failure(r,
89 | int_out,
90 | errmsg)
91 |
92 | if state == 0 then
93 | return nil
94 | end
95 |
96 | if state == FFI_ERROR then
97 | return nil, nil, ffi_str(errmsg[0])
98 | end
99 |
100 | return peer_state_names[state] or "unknown", int_out[0]
101 | end
102 |
103 |
104 | return _M
105 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/misc.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | log_level('warn');
9 |
10 | #repeat_each(120);
11 | repeat_each(2);
12 |
13 | plan tests => repeat_each() * (blocks() * 6 + 1);
14 |
15 | my $pwd = cwd();
16 |
17 | our $HttpConfig = <<_EOC_;
18 | lua_package_path "$pwd/lib/?.lua;\$prefix/html/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | check_accum_error_log();
37 | run_tests();
38 |
39 | __DATA__
40 |
41 | === TEST 1: ngx.is_subrequest
42 | --- http_config eval: $::HttpConfig
43 | --- config
44 | location = /t {
45 | return 201;
46 | header_filter_by_lua '
47 | local rc
48 | for i = 1, 100 do
49 | rc = ngx.is_subrequest
50 | end
51 | ngx.log(ngx.WARN, "is subrequest: ", rc)
52 | ';
53 | }
54 | --- request
55 | GET /t
56 | --- response_body
57 | --- error_code: 201
58 | --- no_error_log
59 | [error]
60 | -- NYI:
61 | bad argument
62 | --- error_log eval
63 | ["is subrequest: false,",
64 | qr/\[TRACE\s+\d+\s+header_filter_by_lua:3 loop\]/
65 | ]
66 |
67 |
68 |
69 | === TEST 2: ngx.headers_sent (false)
70 | --- http_config eval: $::HttpConfig
71 | --- config
72 | location = /t {
73 | content_by_lua '
74 | local rc
75 | for i = 1, 100 do
76 | rc = ngx.headers_sent
77 | end
78 | ngx.say("headers sent: ", rc)
79 | ';
80 | }
81 | --- request
82 | GET /t
83 | --- response_body
84 | headers sent: false
85 | --- no_error_log
86 | [error]
87 | -- NYI:
88 | bad argument
89 | --- error_log eval
90 | qr/\[TRACE\s+\d+\s+content_by_lua\(nginx\.conf:\d+\):3 loop\]/
91 |
92 |
93 |
94 | === TEST 3: ngx.headers_sent (true)
95 | --- http_config eval: $::HttpConfig
96 | --- config
97 | location = /t {
98 | content_by_lua '
99 | ngx.send_headers()
100 | local rc
101 | for i = 1, 100 do
102 | rc = ngx.headers_sent
103 | end
104 | ngx.say("headers sent: ", rc)
105 | ';
106 | }
107 | --- request
108 | GET /t
109 | --- response_body
110 | headers sent: true
111 | --- no_error_log
112 | [error]
113 | -- NYI:
114 | bad argument
115 | --- error_log eval
116 | qr/\[TRACE\s+\d+\s+content_by_lua\(nginx\.conf:\d+\):4 loop\]/
117 |
118 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/md5.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 4);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | local v = require "jit.v"
20 | v.on("$Test::Nginx::Util::ErrLogFile")
21 | require "resty.core"
22 | ';
23 | _EOC_
24 |
25 | #no_diff();
26 | #no_long_string();
27 | check_accum_error_log();
28 | run_tests();
29 |
30 | __DATA__
31 |
32 | === TEST 1: set md5 hello
33 | --- http_config eval: $::HttpConfig
34 | --- config
35 | location = /md5 {
36 | content_by_lua '
37 | local s
38 | for i = 1, 100 do
39 | s = ngx.md5("hello")
40 | end
41 | ngx.say(s)
42 | ';
43 | }
44 | --- request
45 | GET /md5
46 | --- response_body
47 | 5d41402abc4b2a76b9719d911017c592
48 | --- error_log eval
49 | qr/\[TRACE 1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
50 | --- no_error_log
51 | [error]
52 |
53 |
54 |
55 | === TEST 2: nil string to ngx.md5
56 | --- http_config eval: $::HttpConfig
57 | --- config
58 | location = /md5 {
59 | content_by_lua '
60 | local s
61 | for i = 1, 100 do
62 | s = ngx.md5(nil)
63 | end
64 | ngx.say(s)
65 | ';
66 | }
67 | --- request
68 | GET /md5
69 | --- response_body
70 | d41d8cd98f00b204e9800998ecf8427e
71 | --- error_log eval
72 | qr/\[TRACE 1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
73 | --- no_error_log
74 | [error]
75 |
76 |
77 |
78 | === TEST 3: empty string to ngx.md5
79 | --- http_config eval: $::HttpConfig
80 | --- config
81 | location /md5 {
82 | content_by_lua '
83 | local s
84 | for i = 1, 100 do
85 | s = ngx.md5("")
86 | end
87 | ngx.say(s)
88 | ';
89 | }
90 | --- request
91 | GET /md5
92 | --- response_body
93 | d41d8cd98f00b204e9800998ecf8427e
94 | --- error_log eval
95 | qr/\[TRACE 1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
96 | --- no_error_log
97 | [error]
98 |
99 |
100 |
101 | === TEST 4: number to ngx.md5
102 | --- http_config eval: $::HttpConfig
103 | --- config
104 | location /md5 {
105 | content_by_lua '
106 | local s
107 | for i = 1, 100 do
108 | s = ngx.md5(3.14)
109 | end
110 | ngx.say(s)
111 | ';
112 | }
113 | --- request
114 | GET /md5
115 | --- response_body
116 | 4beed3b9c4a886067de0e3a094246f78
117 | --- error_log eval
118 | qr/\[TRACE 1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
119 | --- no_error_log
120 | [error]
121 |
122 |
--------------------------------------------------------------------------------
/admin/ab_action.lua:
--------------------------------------------------------------------------------
1 | local policyModule = require('abtesting.adapter.policy')
2 | local redisModule = require('abtesting.utils.redis')
3 | local systemConf = require('abtesting.utils.init')
4 | local handler = require('abtesting.error.handler').handler
5 | local utils = require('abtesting.utils.utils')
6 | local log = require('abtesting.utils.log')
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 | local policy = require("admin.policy")
9 | local runtime = require('admin.runtime')
10 | local policygroup = require("admin.policygroup")
11 |
12 | local cjson = require('cjson.safe')
13 | local doresp = utils.doresp
14 | local dolog = utils.dolog
15 |
16 | local redisConf = systemConf.redisConf
17 | local divtypes = systemConf.divtypes
18 | local prefixConf = systemConf.prefixConf
19 | local policyLib = prefixConf.policyLibPrefix
20 | local runtimeLib = prefixConf.runtimeInfoPrefix
21 | local domain_name = prefixConf.domainname
22 |
23 | local ab_action = {}
24 |
25 | ab_action.policy_check = policy.check
26 | ab_action.policy_set = policy.set
27 | ab_action.policy_get = policy.get
28 | ab_action.policy_del = policy.del
29 |
30 | ab_action.runtime_set = runtime.set
31 | ab_action.runtime_del = runtime.del
32 | ab_action.runtime_get = runtime.get
33 |
34 |
35 | ab_action.policygroup_check = policygroup.check
36 | ab_action.policygroup_set = policygroup.set
37 | ab_action.policygroup_get = policygroup.get
38 | ab_action.policygroup_del = policygroup.del
39 |
40 |
41 | local get_uriargs_error = function()
42 | local info = ERRORINFO.ACTION_BLANK_ERROR
43 | local response = doresp(info, 'user req')
44 | log:errlog(dolog(info, desc))
45 | ngx.say(response)
46 | return
47 | end
48 |
49 | local get_action_error = function()
50 | local info = ERRORINFO.ACTION_BLANK_ERROR
51 | local response = doresp(info, 'user req')
52 | log:errlog(dolog(info, desc))
53 | ngx.say(response)
54 | return
55 | end
56 |
57 | local do_action_error = function(action)
58 | local info = ERRORINFO.DOACTION_ERROR
59 | local desc = action
60 | local response = doresp(info, desc)
61 | local errlog = dolog(info, desc)
62 | log:errlog(errlog)
63 | ngx.say(response)
64 | return
65 | end
66 |
67 | local red = redisModule:new(redisConf)
68 | local ok, err = red:connectdb()
69 | if not ok then
70 | local info = ERRORINFO.REDIS_CONNECT_ERROR
71 | local response = doresp(info, err)
72 | log:errlog(dolog(info, desc))
73 | ngx.say(response)
74 | return
75 | end
76 |
77 | local args = ngx.req.get_uri_args()
78 | if args then
79 | local action = args.action
80 | local do_action = ab_action[action]
81 | if do_action then
82 | do_action({['db']=red})
83 | -- local ok, info = do_action(policy, {['db']=red})
84 | -- if not ok then
85 | -- do_action_error()
86 | -- end
87 | else
88 | do_action_error(action)
89 | end
90 | else
91 | get_uriargs_error()
92 | end
93 |
--------------------------------------------------------------------------------
/doc/ab功能须知.md:
--------------------------------------------------------------------------------
1 | ab分流功能须知
2 | -------------------
3 |
4 | 转发分流是灰度系统的主要功能,目前 ABTestingGateway 支持 `ip段分流(iprange)`、`uid用户段分流(uidrange)`、`uid尾数分流(uidsuffix)` 和 `指定特殊uid分流(uidappoint)` 四种方式。
5 |
6 | ABTestingGateway 依据系统中配置的 `运行时信息runtimeInfo` 进行分流工作;通过将 runtimeInfo 设置为不同的分流策略,实现运行时分流策略的动态更新,达到动态调度的目的。
7 |
8 | 对于ab功能而言,步骤是以下三步:
9 |
10 | 1. 向系统添加策略,将策略写入策略数据库中
11 | 1. 为具体的server设置运行时信息,将某个分流策略设置为运行时策略
12 | 1. 之后可以进行分流操作
13 |
14 | ### 1. 向系统添加策略 ###
15 | 向系统中添加分流策略的过程,本质上是将分流策略写入到系统的redis中,这个过程与具体哪个server没有关系,只是将分流策略以id为key存入redis中。本系统采用***ab:test:policies***为策略前缀,比如某个策略写入redis成功后,返回policyId=1, 那么在redis中,key为***ab:test:policyies:1:divtype***和***ab:test:policyies:1:divdata***将用来存储策略1的类型type和规则。
16 | 同样的,向系统添加分流策略组,首先将策略组中的单个策略写入系统中,然后返回策略组的policyGroupId。
17 |
18 | ### 2. 为具体的server设置运行时信息 ###
19 | 将某个策略设置成server的运行时策略,最终将这个运行时信息写入到redis中。运行时信息包括三个元素,分流模块名divModuleName,分流策略名divDataKey和用户信息提取模块名userInfoModuleName;运行时信息以***ab:test:runtimeInfo***为前缀;server name为redis中key的关键部分,用于甄别不同server的运行时信息。最终redis中存储的运行时信息是
20 |
21 | ```bash
22 | ab:test:runtimeInfo:xxx.weibo.cn:first:divModulename
23 | ab:test:runtimeInfo:xxx.weibo.cn:first:divDataKey
24 | ab:test:runtimeInfo:xxx.weibo.cn:first:userInfoModulename
25 |
26 | ab:test:runtimeInfo:xxx.weibo.cn:second:divModulename
27 | ab:test:runtimeInfo:xxx.weibo.cn:second:divDataKey
28 | ab:test:runtimeInfo:xxx.weibo.cn:second:userInfoModulename
29 |
30 | # ab:test:runtimeInfo 为前缀
31 | # xxx.weibo.cn 是server name
32 | # first和second分别表示第一级分流和第二级分流
33 | # divModulename等是运行时信息的元素
34 | ```
35 | 因此设置运行时信息时,server name是关键。
36 | 运行时信息设置的接口是
37 |
38 | /ab_admin?action=runtime_set&policyid=0&hostname=xxx.weibo.cn
39 |
40 | 向hostname=xxx.weibo.cn设置运行时信息,完全是在访问runtime_set接口时的hostname参数中指定的。
41 |
42 | 因此,如果引入location级别的运行时信息,我们只需要在调用runtime_set接口时指定hostname就可以。这个hostname同时在location中指定,比如对location /abc设置分流信息时,hostname指定为xxx.weibo.cn.abc,这样在对访问/abc接口的请求进行分流时,以xxx.weibo.cn.abc为key的分流信息生效(例如 ab:test:runtiemInfo:xxx.weibo.cn.abc:divModulename)。具体配置方法见下文
43 |
44 | ### 3. 对用户请求进行分流 ###
45 | 对用户请求进行分流的过程:
46 |
47 | 1. 提取用户请求的HOST字段,拿到以***ab:test:runtimeInfo***和***HOST***为前缀的运行时信息的三要素
48 | 2. 用户信息提取模块userInfoModule提取用户信息userInfo
49 | 3. 分流模块divmodule根据分流策略divDataKey和userInfo计算得到对应的upstream,转发;如果没有对应的upstream,则转向默认upstream。
50 |
51 | * 分流三要素的用途
52 |
53 |
54 | * 分流过程流程图
55 | 
分流过程流程图
56 |
57 | 在引入location级别的分流设置后,需要对访问同一个servername的不同location进行甄别,这里将第一步中从用户请求中获取HOST字段,改为在location配置块中预先设置HOST字段,其效果是一样的。
58 |
59 | ```bash
60 | curl http://ip:port/abc?city=BJ -H 'X-Uid:30' -H 'Host:whatever'
61 |
62 | 该请求访问/abc接口,在location接口中已经指定了server name是xxx.weibo.cn.abc,那么分流工作就按步骤进行。配置如下所示:
63 | location ~* /abc/(i|f)/ {
64 | set $hostkey $server_name.abc;
65 |
66 | rewrite_by_lua_file '/usr/local/dygateway/diversion/diversion.lua';
67 | proxy_pass http://$backend;
68 | }
69 | ```
70 |
71 |
72 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/md5_bin.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 4);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | local verbose = false
20 | if verbose then
21 | local dump = require "jit.dump"
22 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
23 | else
24 | local v = require "jit.v"
25 | v.on("$Test::Nginx::Util::ErrLogFile")
26 | end
27 |
28 | require "resty.core"
29 | -- jit.off()
30 | ';
31 | _EOC_
32 |
33 | #no_diff();
34 | #no_long_string();
35 | check_accum_error_log();
36 | run_tests();
37 |
38 | __DATA__
39 |
40 | === TEST 1: set md5_bin (string)
41 | --- http_config eval: $::HttpConfig
42 | --- config
43 | location = /md5_bin {
44 | content_by_lua '
45 | local s
46 | for i = 1, 100 do
47 | s = ngx.md5_bin("hello")
48 | end
49 | ngx.say(string.len(s))
50 | ';
51 | }
52 | --- request
53 | GET /md5_bin
54 | --- response_body
55 | 16
56 | --- error_log eval
57 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
58 | --- no_error_log
59 | [error]
60 |
61 |
62 |
63 | === TEST 2: set md5_bin (nil)
64 | --- http_config eval: $::HttpConfig
65 | --- config
66 | location = /md5_bin {
67 | content_by_lua '
68 | local s
69 | for i = 1, 100 do
70 | s = ngx.md5_bin(nil)
71 | end
72 | ngx.say(string.len(s))
73 | ';
74 | }
75 | --- request
76 | GET /md5_bin
77 | --- response_body
78 | 16
79 | --- error_log eval
80 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
81 | --- no_error_log
82 | [error]
83 |
84 |
85 |
86 | === TEST 3: set md5_bin (number)
87 | --- http_config eval: $::HttpConfig
88 | --- config
89 | location = /md5_bin {
90 | content_by_lua '
91 | local s
92 | for i = 1, 100 do
93 | s = ngx.md5_bin(3.14)
94 | end
95 | ngx.say(string.len(s))
96 | ';
97 | }
98 | --- request
99 | GET /md5_bin
100 | --- response_body
101 | 16
102 | --- error_log eval
103 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
104 | --- no_error_log
105 | [error]
106 |
107 |
108 |
109 | === TEST 4: set md5_bin (boolean)
110 | --- http_config eval: $::HttpConfig
111 | --- config
112 | location = /md5_bin {
113 | content_by_lua '
114 | local s
115 | for i = 1, 100 do
116 | s = ngx.md5_bin(true)
117 | end
118 | ngx.say(string.len(s))
119 | ';
120 | }
121 | --- request
122 | GET /md5_bin
123 | --- response_body
124 | 16
125 | --- error_log eval
126 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
127 | --- no_error_log
128 | [error]
129 |
130 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/sha1_bin.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 4);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | local verbose = false
20 | if verbose then
21 | local dump = require "jit.dump"
22 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
23 | else
24 | local v = require "jit.v"
25 | v.on("$Test::Nginx::Util::ErrLogFile")
26 | end
27 |
28 | require "resty.core"
29 | -- jit.off()
30 | ';
31 | _EOC_
32 |
33 | #no_diff();
34 | #no_long_string();
35 | check_accum_error_log();
36 | run_tests();
37 |
38 | __DATA__
39 |
40 | === TEST 1: set sha1_bin (string)
41 | --- http_config eval: $::HttpConfig
42 | --- config
43 | location = /sha1_bin {
44 | content_by_lua '
45 | local s
46 | for i = 1, 100 do
47 | s = ngx.sha1_bin("hello")
48 | end
49 | ngx.say(string.len(s))
50 | ';
51 | }
52 | --- request
53 | GET /sha1_bin
54 | --- response_body
55 | 20
56 | --- error_log eval
57 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
58 | --- no_error_log
59 | [error]
60 |
61 |
62 |
63 | === TEST 2: set sha1_bin (nil)
64 | --- http_config eval: $::HttpConfig
65 | --- config
66 | location = /sha1_bin {
67 | content_by_lua '
68 | local s
69 | for i = 1, 100 do
70 | s = ngx.sha1_bin(nil)
71 | end
72 | ngx.say(string.len(s))
73 | ';
74 | }
75 | --- request
76 | GET /sha1_bin
77 | --- response_body
78 | 20
79 | --- error_log eval
80 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
81 | --- no_error_log
82 | [error]
83 |
84 |
85 |
86 | === TEST 3: set sha1_bin (number)
87 | --- http_config eval: $::HttpConfig
88 | --- config
89 | location = /sha1_bin {
90 | content_by_lua '
91 | local s
92 | for i = 1, 100 do
93 | s = ngx.sha1_bin(3.14)
94 | end
95 | ngx.say(string.len(s))
96 | ';
97 | }
98 | --- request
99 | GET /sha1_bin
100 | --- response_body
101 | 20
102 | --- error_log eval
103 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
104 | --- no_error_log
105 | [error]
106 |
107 |
108 |
109 | === TEST 4: set sha1_bin (boolean)
110 | --- http_config eval: $::HttpConfig
111 | --- config
112 | location = /sha1_bin {
113 | content_by_lua '
114 | local s
115 | for i = 1, 100 do
116 | s = ngx.sha1_bin(true)
117 | end
118 | ngx.say(string.len(s))
119 | ';
120 | }
121 | --- request
122 | GET /sha1_bin
123 | --- response_body
124 | 20
125 | --- error_log eval
126 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
127 | --- no_error_log
128 | [error]
129 |
130 |
--------------------------------------------------------------------------------
/lib/abtesting/diversion/uidappoint.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingDiversionUidappoint"
2 |
3 | local _M = {}
4 | local mt = { __index = _M }
5 | _M._VERSION = "0.0.1"
6 |
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 |
9 | local k_uid = 'uid'
10 | local k_uidset = 'uidset'
11 | local k_upstream= 'upstream'
12 |
13 | _M.new = function(self, database, policyLib)
14 | if not database then
15 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
16 | end if not policyLib then
17 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
18 | end
19 |
20 | self.database = database
21 | self.policyLib = policyLib
22 | return setmetatable(self, mt)
23 | end
24 |
25 | local isNULL = function(v)
26 | return v and v ~= ngx.null
27 | end
28 |
29 | -- policy is in format as {{upstream = '192.132.23.125', uidset ={ 214214, 23421,12421} }, {}}
30 | _M.check = function(self, policy)
31 | for _, v in pairs(policy) do
32 | local uidset = v[k_uidset]
33 | local upstream = v[k_upstream]
34 |
35 | local v_uidset = uidset and (type(uidset) == 'table')
36 | local v_upstream = upstream and upstream ~= ngx.null
37 |
38 | if not v_uidset or not v_upstream then
39 | local info = ERRORINFO.POLICY_INVALID_ERROR
40 | local desc = ' k_uidset or k_upstream error'
41 | return {false, info, desc}
42 | end
43 |
44 | for _, uid in pairs(uidset) do
45 | if not tonumber(uid) then
46 | local info = ERRORINFO.POLICY_INVALID_ERROR
47 | local desc = 'uid invalid '
48 | return {false, info, desc}
49 | end
50 | end
51 | --TODO: need to check upstream alive
52 | end
53 |
54 | return {true}
55 | end
56 |
57 | -- policyData will be in hash table uid:upstream
58 | _M.set = function(self, policy)
59 | local database = self.database
60 | local policyLib = self.policyLib
61 |
62 | database:init_pipeline()
63 | for _, v in pairs(policy) do
64 | local uidset = v[k_uidset]
65 | local upstream = v[k_upstream]
66 | for _, uid in pairs(uidset) do
67 | database:hset(policyLib, uid, upstream)
68 | end
69 | end
70 | local ok, err = database:commit_pipeline()
71 | if not ok then
72 | error{ERRORINFO.REDIS_ERROR, err}
73 | end
74 | end
75 |
76 | _M.get = function(self)
77 | local database = self.database
78 | local policyLib = self.policyLib
79 |
80 | local data, err = database:hgetall(policyLib)
81 | if not data then
82 | error{ERRORINFO.REDIS_ERROR, err}
83 | end
84 |
85 | return data
86 | end
87 |
88 | _M.getUpstream = function(self, uid)
89 | if not tonumber(uid) then
90 | return nil
91 | end
92 |
93 | local database, key = self.database, self.policyLib
94 |
95 | local backend, err = database:hget(key, uid)
96 | if not backend then error{ERRORINFO.REDIS_ERROR, err} end
97 |
98 | if backend == ngx.null then backend = nil end
99 |
100 | return backend
101 | end
102 |
103 | return _M
104 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 |
--------------------------------------------------------------------------------
/doc/ab分流策略.md:
--------------------------------------------------------------------------------
1 | ab分流策略格式
2 | ======================
3 |
4 | * 分流策略 policy
5 | * 分流策略组 policygroup
6 |
7 | 分流策略policy
8 | --------------------------
9 |
10 | ```bash
11 | {
12 | "divtype":"分流类型",
13 | "divdata":[
14 | {规则一},
15 | {规则二},
16 | {规则三},
17 | ...
18 | ]
19 | }
20 | ```
21 |
22 | * ab 灰度系统目前支持的策略有ip段分流、用户uid段分流、uid尾数分流、uid白名单分流
23 | * 可以灵活添加新的分流方式
24 |
25 | ```bash
26 | #iprange分流,其中start和end为ip的整型表示。
27 | {
28 | "divtype":"iprange",
29 | "divdata":[
30 | {"range":{"start":1111, "end":2222}, "upstream":"beta1"},
31 | {"range":{"start":3333, "end":4444}, "upstream":"beta2"},
32 | {"range":{"start":7777, "end":8888}, "upstream":"beta3"}
33 | ]
34 | }
35 | ```
36 |
37 | ```bash
38 | #uid段分流
39 | {
40 | "divtype": "uidrange",
41 | "divdata": [
42 | {"range":{"start":1111, "end":2222}, "upstream":"beta1"},
43 | {"range":{"start":3333, "end":4444}, "upstream":"beta2"}
44 | ]
45 | }
46 | ```
47 |
48 | ```bash
49 | #uid尾数分流
50 | {
51 | "divtype": "uidsuffix",
52 | "divdata": [
53 | {"suffix":1, "upstream":"beta1"},
54 | {"suffix":3, "upstream":"beta2"},
55 | {"suffix":5, "upstream":"beta1"},
56 | {"suffix":0, "upstream":"beta3"}
57 | ]
58 | }
59 | ```
60 |
61 | ```bash
62 | #uid白名单分流
63 | {
64 | "divtype": "uidappoint",
65 | "divdata": [
66 | {"uidset":[1234,5124,653], "upstream":"beta1"},
67 | {"uidset":[3214,652,145], "upstream":"beta2"}
68 | ]
69 | }
70 | ```
71 | * 当向系统添加分流策略时,需要将策略数据转为json类型字符串,以POST方式访问添加策略接口
72 | * http://www.bejson.com/jsoneditoronline/ 可以将上述策略转换为字符串,用以通过post方式向系统添加策略
73 | * 以上四条策略的字符串格式分别为:
74 | * {"divtype":"iprange","divdata":[{"range":{"start":1111,"end":2222},"upstream":"beta1"},{"range":{"start":3333,"end":4444},"upstream":"beta2"},{"range":{"start":7777,"end":8888},"upstream":"beta3"}]}
75 | * {"divtype":"uidrange","divdata":[{"range":{"start":1111,"end":2222},"upstream":"beta1"},{"range":{"start":3333,"end":4444},"upstream":"beta2"}]}
76 | * {"divtype":"uidsuffix","divdata":[{"suffix":1,"upstream":"beta1"},{"suffix":3,"upstream":"beta2"},{"suffix":5,"upstream":"beta1"},{"suffix":0,"upstream":"beta3"}]}
77 | * {"divtype":"uidappoint","divdata":[{"uidset":[1234,5124,653],"upstream":"beta1"},{"uidset":[3214,652,145],"upstream":"beta2"}]}
78 |
79 |
80 | 分流策略组policygroup
81 | --------------------------
82 |
83 | * 分流策略组中会有多个策略
84 | * 优先级由数字表示,从1开始,级别为1的策略优先级最高
85 | * 分流策略组格式为:
86 |
87 | ```bash
88 | {
89 | "1":{
90 | "divtype":"分流类型",
91 | "divdata":[
92 | {规则一},
93 | {规则二},
94 | ...
95 | ]
96 | },
97 | "2":{
98 |
99 | },
100 | ...
101 | }
102 | ```
103 | * 以下是一个包含两级分流策略,第一级为uid白名单分流策略,第二级为ip段分流
104 |
105 | ```bash
106 | {
107 |
108 | "1":{
109 | "divtype": "uidappoint",
110 | "divdata": [
111 | {"uidset":[1234,5124,653], "upstream":"beta1"},
112 | {"uidset":[3214,652,145], "upstream":"beta2"}
113 | ]
114 | },
115 | "2":{
116 | "divtype":"iprange",
117 | "divdata":[
118 | {"range":{"start":1111, "end":2222}, "upstream":"beta1"},
119 | {"range":{"start":3333, "end":4444}, "upstream":"beta2"},
120 | {"range":{"start":7777, "end":8888}, "upstream":"beta3"}
121 | ]
122 | }
123 | }
124 | ```
125 | * 该分流策略组的字符串形式为:
126 | * {"1":{"divtype":"uidappoint","divdata":[{"uidset":[1234,5124,653],"upstream":"beta1"},{"uidset":[3214,652,145],"upstream":"beta2"}]},"2":{"divtype":"iprange","divdata":[{"range":{"start":1111,"end":2222},"upstream":"beta1"},{"range":{"start":3333,"end":4444},"upstream":"beta2"},{"range":{"start":7777,"end":8888},"upstream":"beta3"}]}}
127 |
128 |
129 |
--------------------------------------------------------------------------------
/lib/abtesting/adapter/runtime.lua:
--------------------------------------------------------------------------------
1 | ---
2 | -- @classmod abtesting.adapter.runtime
3 | -- @release 0.0.1
4 | local modulename = "abtestingAdapterRuntime"
5 |
6 | local _M = {}
7 | local metatable = {__index = _M}
8 |
9 | _M._VERSION = "0.0.1"
10 |
11 | local ERRORINFO = require('abtesting.error.errcode').info
12 | local fields = require('abtesting.utils.init').fields
13 |
14 | local separator = ':'
15 |
16 | ---
17 | -- runtimeInfoIO new function
18 | -- @param database opened redis
19 | -- @param baseLibrary a library(prefix of redis key) of runtime info
20 | -- @return runtimeInfoIO object
21 | _M.new = function(self, database, baseLibrary)
22 | if not database then
23 | error{ERRORINFO.PARAMETER_NONE, 'need a object of redis'}
24 | end if not baseLibrary then
25 | error{ERRORINFO.PARAMETER_NONE, 'need a library of runtime info'}
26 | end
27 |
28 | self.database = database
29 | self.baseLibrary = baseLibrary
30 |
31 | return setmetatable(self, metatable)
32 | end
33 |
34 | ---
35 | -- set runtime info(diversion modulename and diversion metadata key)
36 | -- @param domain is a domain name to search runtime info
37 | -- @param ... now is diversion modulename and diversion data key
38 | -- @return if returned, the return value always SUCCESS
39 | _M.set = function(self, domain, ...)
40 | local info = {...}
41 | local divModulename = info[1]
42 | local divDataKey = info[2]
43 | local userInfoModulename = info[3]
44 |
45 | local database = self.database
46 | local divModulenamekey = table.concat({self.baseLibrary, domain, fields.divModulename}, separator)
47 | local divDataKeyOfKey = table.concat({self.baseLibrary, domain, fields.divDataKey}, separator)
48 | local userInfoModulenameKey = table.concat({self.baseLibrary, domain, fields.userInfoModulename}, separator)
49 | local ok, err = database:mset(divModulenamekey, divModulename,
50 | divDataKeyOfKey, divDataKey,
51 | userInfoModulenameKey, userInfoModulename)
52 |
53 | if not ok then error{ERRORINFO.REDIS_ERROR, err} end
54 |
55 | return ERRORINFO.SUCCESS
56 | end
57 |
58 | ---
59 | -- delete runtime info(diversion modulename and diversion metadata key)
60 | -- @param domain a domain of delete
61 | -- @return if returned, the return value always SUCCESS
62 | _M.del = function(self, domain)
63 | local database = self.database
64 | local divModulenamekey = table.concat({self.baseLibrary, domain, fields.divModulename}, separator)
65 | local divDataKeyOfKey = table.concat({self.baseLibrary, domain, fields.divDataKey}, separator)
66 | local userInfoModulenameKey = table.concat({self.baseLibrary, domain, fields.userInfoModulename}, separator)
67 |
68 | local ok, err = database:del(divModulenamekey, divDataKeyOfKey, userInfoModulenameKey)
69 |
70 | if not ok then error{ERRORINFO.REDIS_ERROR, err} end
71 |
72 | return ERRORINFO.SUCCESS
73 | end
74 |
75 | ---
76 | -- get runtime info(diversion modulename and diversion metadata key)
77 | -- @param domain is a domain name to search runtime info
78 | -- @return a table of diversion modulename and diversion metadata key
79 | _M.get = function(self, domain)
80 | local database = self.database
81 | local divModulenameKey = table.concat({self.baseLibrary, domain, fields.divModulename}, separator)
82 | local divDataKeyOfKey = table.concat({self.baseLibrary, domain, fields.divDataKey}, separator)
83 | local userInfoModulenameKey = table.concat({self.baseLibrary, domain, fields.userInfoModulename}, separator)
84 |
85 | local response, err = database:mget(divModulenameKey, divDataKeyOfKey, userInfoModulenameKey)
86 | if not response then
87 | error{ERRORINFO.REDIS_ERROR, err}
88 | end
89 |
90 | return response
91 | end
92 |
93 | return _M
94 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/cert/ocsp/test-com.crt:
--------------------------------------------------------------------------------
1 | Certificate:
2 | Data:
3 | Version: 3 (0x2)
4 | Serial Number: 4 (0x4)
5 | Signature Algorithm: sha1WithRSAEncryption
6 | Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
7 | Validity
8 | Not Before: Oct 16 03:27:09 2014 GMT
9 | Not After : Sep 22 03:27:09 2114 GMT
10 | Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=test.com
11 | Subject Public Key Info:
12 | Public Key Algorithm: rsaEncryption
13 | Public-Key: (1024 bit)
14 | Modulus:
15 | 00:c7:bd:50:99:71:46:af:93:22:85:ab:74:8b:5b:
16 | 19:74:af:3e:ad:d2:e1:17:3e:cb:5b:36:9c:8a:38:
17 | bd:1b:47:2d:8b:92:55:1d:fe:a6:72:92:78:00:de:
18 | 30:cb:a3:10:b5:92:aa:b8:e0:7b:44:9a:f5:99:89:
19 | 36:f4:84:20:81:e3:5c:76:00:9d:76:e7:b9:41:ab:
20 | 74:b6:14:9f:b2:94:b3:b6:48:a8:92:dc:09:e3:3d:
21 | 04:e3:5f:0f:5b:50:ad:0c:59:3a:88:06:39:2d:34:
22 | a6:52:2f:58:6f:53:1b:df:9f:98:ea:82:8d:52:60:
23 | b1:ef:6b:e9:f5:ad:29:87:45
24 | Exponent: 65537 (0x10001)
25 | X509v3 extensions:
26 | X509v3 Basic Constraints:
27 | CA:FALSE
28 | Netscape Comment:
29 | OpenSSL Generated Certificate
30 | X509v3 Subject Key Identifier:
31 | 67:DF:28:25:D1:F8:83:36:28:EE:DB:41:63:E4:E0:3A:32:0D:EA:30
32 | X509v3 Authority Key Identifier:
33 | keyid:B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
34 | DirName:/C=US/ST=California/L=Default City/O=OpenResty/CN=signing-ca-1
35 | serial:03
36 |
37 | Authority Information Access:
38 | OCSP - URI:http://127.0.0.1:8888/ocsp?foo=1
39 |
40 | Signature Algorithm: sha1WithRSAEncryption
41 | 37:29:3f:ed:d9:47:9a:51:36:a3:5b:00:85:66:de:51:4d:48:
42 | 2d:f8:bc:f1:5e:b4:fd:30:48:f0:25:ee:77:57:9c:f1:4b:0a:
43 | 4f:7e:96:1a:f8:48:76:23:46:8d:d6:f2:5e:1e:08:52:12:53:
44 | 08:07:9f:75:db:77:22:2e:7e:89:c2:2c:66:85:6b:df:e9:77:
45 | ca:23:6d:9a:af:87:8a:8c:27:37:1e:9e:55:92:8e:8a:a9:93:
46 | 24:41:a8:96:01:c0:65:93:8e:3d:7a:6c:bf:ed:c8:2a:f8:26:
47 | cc:00:17:b7:27:ca:85:6c:2e:d5:2a:0a:8d:f3:88:e8:26:48:
48 | e3:e8
49 | -----BEGIN CERTIFICATE-----
50 | MIIDaTCCAtKgAwIBAgIBBDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
51 | MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
52 | VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMjAgFw0xNDEwMTYw
53 | MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
54 | CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
55 | blJlc3R5MREwDwYDVQQDEwh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
56 | gYkCgYEAx71QmXFGr5Mihat0i1sZdK8+rdLhFz7LWzaciji9G0cti5JVHf6mcpJ4
57 | AN4wy6MQtZKquOB7RJr1mYk29IQggeNcdgCddue5Qat0thSfspSztkioktwJ4z0E
58 | 418PW1CtDFk6iAY5LTSmUi9Yb1Mb35+Y6oKNUmCx72vp9a0ph0UCAwEAAaOCASsw
59 | ggEnMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVk
60 | IENlcnRpZmljYXRlMB0GA1UdDgQWBBRn3ygl0fiDNiju20Fj5OA6Mg3qMDCBjgYD
61 | VR0jBIGGMIGDgBSzC/V9URZRfig3w6IPHS8QwFGjs6FopGYwZDELMAkGA1UEBhMC
62 | VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTES
63 | MBAGA1UEChMJT3BlblJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTGCAQMwPAYI
64 | KwYBBQUHAQEEMDAuMCwGCCsGAQUFBzABhiBodHRwOi8vMTI3LjAuMC4xOjg4ODgv
65 | b2NzcD9mb289MTANBgkqhkiG9w0BAQUFAAOBgQA3KT/t2UeaUTajWwCFZt5RTUgt
66 | +LzxXrT9MEjwJe53V5zxSwpPfpYa+Eh2I0aN1vJeHghSElMIB59123ciLn6Jwixm
67 | hWvf6XfKI22ar4eKjCc3Hp5Vko6KqZMkQaiWAcBlk449emy/7cgq+CbMABe3J8qF
68 | bC7VKgqN84joJkjj6A==
69 | -----END CERTIFICATE-----
70 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 | local error = error
19 | local tonumber = tonumber
20 |
21 |
22 | local _M = new_tab(0, 3)
23 | _M._VERSION = base.version
24 |
25 |
26 | local function register_getter(key, func)
27 | ngx_magic_key_getters[key] = func
28 | end
29 | _M.register_ngx_magic_key_getter = register_getter
30 |
31 |
32 | local function register_setter(key, func)
33 | ngx_magic_key_setters[key] = func
34 | end
35 | _M.register_ngx_magic_key_setter = register_setter
36 |
37 |
38 | local mt = getmetatable(ngx)
39 |
40 |
41 | local old_index = mt.__index
42 | mt.__index = function (tb, key)
43 | local f = ngx_magic_key_getters[key]
44 | if f then
45 | return f()
46 | end
47 | return old_index(tb, key)
48 | end
49 |
50 |
51 | local old_newindex = mt.__newindex
52 | mt.__newindex = function (tb, key, ctx)
53 | local f = ngx_magic_key_setters[key]
54 | if f then
55 | return f(ctx)
56 | end
57 | return old_newindex(tb, key, ctx)
58 | end
59 |
60 |
61 | ffi.cdef[[
62 | int ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r);
63 | int ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int r);
64 | int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r);
65 | int ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r);
66 | ]]
67 |
68 |
69 | -- ngx.status
70 |
71 | local function get_status()
72 | local r = getfenv(0).__ngx_req
73 |
74 | if not r then
75 | return error("no request found")
76 | end
77 |
78 | local rc = C.ngx_http_lua_ffi_get_resp_status(r)
79 |
80 | if rc == FFI_BAD_CONTEXT then
81 | return error("API disabled in the current context")
82 | end
83 |
84 | return rc
85 | end
86 | register_getter("status", get_status)
87 |
88 |
89 | local function set_status(status)
90 | local r = getfenv(0).__ngx_req
91 |
92 | if not r then
93 | return error("no request found")
94 | end
95 |
96 | if type(status) ~= 'number' then
97 | status = tonumber(status)
98 | end
99 |
100 | local rc = C.ngx_http_lua_ffi_set_resp_status(r, status)
101 |
102 | if rc == FFI_BAD_CONTEXT then
103 | return error("API disabled in the current context")
104 | end
105 |
106 | return
107 | end
108 | register_setter("status", set_status)
109 |
110 |
111 | -- ngx.is_subrequest
112 |
113 | local function is_subreq()
114 | local r = getfenv(0).__ngx_req
115 |
116 | if not r then
117 | return error("no request found")
118 | end
119 |
120 | local rc = C.ngx_http_lua_ffi_is_subrequest(r)
121 |
122 | if rc == FFI_BAD_CONTEXT then
123 | return error("API disabled in the current context")
124 | end
125 |
126 | return rc == 1 and true or false
127 | end
128 | register_getter("is_subrequest", is_subreq)
129 |
130 |
131 | -- ngx.headers_sent
132 |
133 | local function headers_sent()
134 | local r = getfenv(0).__ngx_req
135 |
136 | if not r then
137 | return error("no request found")
138 | end
139 |
140 | local rc = C.ngx_http_lua_ffi_headers_sent(r)
141 |
142 | if rc == FFI_NO_REQ_CTX then
143 | return error("no request ctx found")
144 | end
145 |
146 | if rc == FFI_BAD_CONTEXT then
147 | return error("API disabled in the current context")
148 | end
149 |
150 | return rc == 1
151 | end
152 | register_getter("headers_sent", headers_sent)
153 |
154 |
155 | return _M
156 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/ngx/semaphore.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 | -- Copyright (C) cuiweixie
3 | -- I hereby assign copyright in this code to the lua-resty-core project,
4 | -- to be licensed under the same terms as the rest of the code.
5 |
6 |
7 | local ffi = require 'ffi'
8 | local base = require "resty.core.base"
9 |
10 |
11 | local FFI_OK = base.FFI_OK
12 | local FFI_ERROR = base.FFI_ERROR
13 | local FFI_DECLINED = base.FFI_DECLINED
14 | local ffi_new = ffi.new
15 | local ffi_str = ffi.string
16 | local ffi_gc = ffi.gc
17 | local C = ffi.C
18 | local type = type
19 | local error = error
20 | local tonumber = tonumber
21 | local getfenv = getfenv
22 | local get_string_buf = base.get_string_buf
23 | local get_size_ptr = base.get_size_ptr
24 | local setmetatable = setmetatable
25 | local co_yield = coroutine._yield
26 | local ERR_BUF_SIZE = 128
27 |
28 |
29 | local errmsg = base.get_errmsg_ptr()
30 |
31 |
32 | ffi.cdef[[
33 | struct ngx_http_lua_semaphore_s;
34 | typedef struct ngx_http_lua_semaphore_s ngx_http_lua_semaphore_t;
35 |
36 | int ngx_http_lua_ffi_semaphore_new(ngx_http_lua_semaphore_t **psem,
37 | int n, char **errmsg);
38 |
39 | int ngx_http_lua_ffi_semaphore_post(ngx_http_lua_semaphore_t *sem, int n);
40 |
41 | int ngx_http_lua_ffi_semaphore_count(ngx_http_lua_semaphore_t *sem);
42 |
43 | int ngx_http_lua_ffi_semaphore_wait(ngx_http_request_t *r,
44 | ngx_http_lua_semaphore_t *sem, int wait_ms,
45 | unsigned char *errstr, size_t *errlen);
46 |
47 | void ngx_http_lua_ffi_semaphore_gc(ngx_http_lua_semaphore_t *sem);
48 | ]]
49 |
50 |
51 | local psem = ffi_new("ngx_http_lua_semaphore_t *[1]")
52 |
53 |
54 | local _M = { version = base.version }
55 | local mt = { __index = _M }
56 |
57 |
58 | function _M.new(n)
59 | n = tonumber(n) or 0
60 | if n < 0 then
61 | return error("no negative number")
62 | end
63 |
64 | local ret = C.ngx_http_lua_ffi_semaphore_new(psem, n, errmsg)
65 | if ret == FFI_ERROR then
66 | return nil, ffi_str(errmsg[0])
67 | end
68 |
69 | local sem = psem[0]
70 |
71 | ffi_gc(sem, C.ngx_http_lua_ffi_semaphore_gc)
72 |
73 | return setmetatable({ sem = sem }, mt)
74 | end
75 |
76 |
77 | function _M.wait(self, seconds)
78 | if type(self) ~= "table" or type(self.sem) ~= "cdata" then
79 | return error("not a semaphore instance")
80 | end
81 |
82 | local r = getfenv(0).__ngx_req
83 | if not r then
84 | return error("no request found")
85 | end
86 |
87 | local milliseconds = tonumber(seconds) * 1000
88 | if milliseconds < 0 then
89 | return error("no negative number")
90 | end
91 |
92 | local cdata_sem = self.sem
93 |
94 | local err = get_string_buf(ERR_BUF_SIZE)
95 | local errlen = get_size_ptr()
96 | errlen[0] = ERR_BUF_SIZE
97 |
98 | local ret = C.ngx_http_lua_ffi_semaphore_wait(r, cdata_sem,
99 | milliseconds, err, errlen)
100 |
101 | if ret == FFI_ERROR then
102 | return nil, ffi_str(err, errlen[0])
103 | end
104 |
105 | if ret == FFI_OK then
106 | return true
107 | end
108 |
109 | if ret == FFI_DECLINED then
110 | return nil, "timeout"
111 | end
112 |
113 | return co_yield()
114 | end
115 |
116 |
117 | function _M.post(self, n)
118 | if type(self) ~= "table" or type(self.sem) ~= "cdata" then
119 | return error("not a semaphore instance")
120 | end
121 |
122 | local cdata_sem = self.sem
123 |
124 | local num = n and tonumber(n) or 1
125 | if num < 1 then
126 | return error("no negative number")
127 | end
128 |
129 | -- always return NGX_OK
130 | C.ngx_http_lua_ffi_semaphore_post(cdata_sem, num)
131 |
132 | return true
133 | end
134 |
135 |
136 | function _M.count(self)
137 | if type(self) ~= "table" or type(self.sem) ~= "cdata" then
138 | return error("not a semaphore instance")
139 | end
140 |
141 | return C.ngx_http_lua_ffi_semaphore_count(self.sem)
142 | end
143 |
144 |
145 | return _M
146 |
--------------------------------------------------------------------------------
/utils/conf/mime.types:
--------------------------------------------------------------------------------
1 |
2 | types {
3 | text/html html htm shtml;
4 | text/css css;
5 | text/xml xml;
6 | image/gif gif;
7 | image/jpeg jpeg jpg;
8 | application/javascript js;
9 | application/atom+xml atom;
10 | application/rss+xml rss;
11 |
12 | text/mathml mml;
13 | text/plain txt;
14 | text/vnd.sun.j2me.app-descriptor jad;
15 | text/vnd.wap.wml wml;
16 | text/x-component htc;
17 |
18 | image/png png;
19 | image/tiff tif tiff;
20 | image/vnd.wap.wbmp wbmp;
21 | image/x-icon ico;
22 | image/x-jng jng;
23 | image/x-ms-bmp bmp;
24 | image/svg+xml svg svgz;
25 | image/webp webp;
26 |
27 | application/font-woff woff;
28 | application/java-archive jar war ear;
29 | application/json json;
30 | application/mac-binhex40 hqx;
31 | application/msword doc;
32 | application/pdf pdf;
33 | application/postscript ps eps ai;
34 | application/rtf rtf;
35 | application/vnd.apple.mpegurl m3u8;
36 | application/vnd.ms-excel xls;
37 | application/vnd.ms-fontobject eot;
38 | application/vnd.ms-powerpoint ppt;
39 | application/vnd.wap.wmlc wmlc;
40 | application/vnd.google-earth.kml+xml kml;
41 | application/vnd.google-earth.kmz kmz;
42 | application/x-7z-compressed 7z;
43 | application/x-cocoa cco;
44 | application/x-java-archive-diff jardiff;
45 | application/x-java-jnlp-file jnlp;
46 | application/x-makeself run;
47 | application/x-perl pl pm;
48 | application/x-pilot prc pdb;
49 | application/x-rar-compressed rar;
50 | application/x-redhat-package-manager rpm;
51 | application/x-sea sea;
52 | application/x-shockwave-flash swf;
53 | application/x-stuffit sit;
54 | application/x-tcl tcl tk;
55 | application/x-x509-ca-cert der pem crt;
56 | application/x-xpinstall xpi;
57 | application/xhtml+xml xhtml;
58 | application/xspf+xml xspf;
59 | application/zip zip;
60 | application/x-nokia-widget wgz;
61 | application/vnd.android.package-archive apk;
62 |
63 | application/octet-stream bin exe dll;
64 | application/octet-stream deb;
65 | application/octet-stream dmg;
66 | application/octet-stream iso img;
67 | application/octet-stream msi msp msm;
68 |
69 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
70 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
71 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
72 |
73 | audio/midi mid midi kar;
74 | audio/mpeg mp3;
75 | audio/ogg ogg;
76 | audio/x-m4a m4a;
77 | audio/x-realaudio ra;
78 |
79 | video/3gpp 3gpp 3gp;
80 | video/mp2t ts;
81 | video/mp4 mp4;
82 | video/mpeg mpeg mpg;
83 | video/quicktime mov;
84 | video/webm webm;
85 | video/x-flv flv;
86 | video/x-m4v m4v;
87 | video/x-mng mng;
88 | video/x-ms-asf asx asf;
89 | video/x-ms-wmv wmv;
90 | video/x-msvideo avi;
91 | }
92 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/ngx/ocsp.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_str = ffi.string
10 | local getfenv = getfenv
11 | local error = error
12 | local tonumber = tonumber
13 | local errmsg = base.get_errmsg_ptr()
14 | local get_string_buf = base.get_string_buf
15 | local get_string_buf_size = base.get_string_buf_size
16 | local get_size_ptr = base.get_size_ptr
17 | local FFI_DECLINED = base.FFI_DECLINED
18 | local FFI_OK = base.FFI_OK
19 | local FFI_BUSY = base.FFI_BUSY
20 |
21 |
22 | ffi.cdef[[
23 | int ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain(
24 | const char *chain_data, size_t chain_len, char *out, size_t *out_size,
25 | char **err);
26 |
27 | int ngx_http_lua_ffi_ssl_create_ocsp_request(const char *chain_data,
28 | size_t chain_len, unsigned char *out, size_t *out_size, char **err);
29 |
30 | int ngx_http_lua_ffi_ssl_validate_ocsp_response(const unsigned char *resp,
31 | size_t resp_len, const char *chain_data, size_t chain_len,
32 | unsigned char *errbuf, size_t *errbuf_size);
33 |
34 | int ngx_http_lua_ffi_ssl_set_ocsp_status_resp(ngx_http_request_t *r,
35 | const unsigned char *resp, size_t resp_len, char **err);
36 | ]]
37 |
38 |
39 | local _M = { version = base.version }
40 |
41 |
42 | function _M.get_ocsp_responder_from_der_chain(certs, maxlen)
43 |
44 | local buf_size = maxlen
45 | if not buf_size then
46 | buf_size = get_string_buf_size()
47 | end
48 | local buf = get_string_buf(buf_size)
49 |
50 | local sizep = get_size_ptr()
51 | sizep[0] = buf_size
52 |
53 | local rc = C.ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain(certs,
54 | #certs, buf, sizep, errmsg)
55 |
56 | if rc == FFI_DECLINED then
57 | return nil
58 | end
59 |
60 | if rc == FFI_OK then
61 | return ffi_str(buf, sizep[0])
62 | end
63 |
64 | if rc == FFI_BUSY then
65 | return ffi_str(buf, sizep[0]), "truncated"
66 | end
67 |
68 | return nil, ffi_str(errmsg[0])
69 | end
70 |
71 |
72 | function _M.create_ocsp_request(certs, maxlen)
73 |
74 | local buf_size = maxlen
75 | if not buf_size then
76 | buf_size = get_string_buf_size()
77 | end
78 | local buf = get_string_buf(buf_size)
79 |
80 | local sizep = get_size_ptr()
81 | sizep[0] = buf_size
82 |
83 | local rc = C.ngx_http_lua_ffi_ssl_create_ocsp_request(certs,
84 | #certs, buf, sizep,
85 | errmsg)
86 |
87 | if rc == FFI_OK then
88 | return ffi_str(buf, sizep[0])
89 | end
90 |
91 | if rc == FFI_BUSY then
92 | return nil, ffi_str(errmsg[0]) .. ": " .. tonumber(sizep[0])
93 | .. " > " .. buf_size
94 | end
95 |
96 | return nil, ffi_str(errmsg[0])
97 | end
98 |
99 |
100 | function _M.validate_ocsp_response(resp, chain, max_errmsg_len)
101 |
102 | local errbuf_size = max_errmsg_len
103 | if not errbuf_size then
104 | errbuf_size = get_string_buf_size()
105 | end
106 | local errbuf = get_string_buf(errbuf_size)
107 |
108 | local sizep = get_size_ptr()
109 | sizep[0] = errbuf_size
110 |
111 | local rc = C.ngx_http_lua_ffi_ssl_validate_ocsp_response(
112 | resp, #resp, chain, #chain, errbuf, sizep)
113 |
114 | if rc == FFI_OK then
115 | return true
116 | end
117 |
118 | -- rc == FFI_ERROR
119 |
120 | return nil, ffi_str(errbuf, sizep[0])
121 | end
122 |
123 |
124 | function _M.set_ocsp_status_resp(ocsp_resp)
125 | local r = getfenv(0).__ngx_req
126 | if not r then
127 | return error("no request found")
128 | end
129 |
130 | local rc = C.ngx_http_lua_ffi_ssl_set_ocsp_status_resp(r, ocsp_resp,
131 | #ocsp_resp,
132 | errmsg)
133 |
134 | if rc == FFI_DECLINED then
135 | -- no client status req
136 | return true, "no status req"
137 | end
138 |
139 | if rc == FFI_OK then
140 | return true
141 | end
142 |
143 | -- rc == FFI_ERROR
144 |
145 | return nil, ffi_str(errmsg[0])
146 | end
147 |
148 |
149 | return _M
150 |
--------------------------------------------------------------------------------
/lib/abtesting/utils/cache.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingCache"
2 |
3 | local _M = {}
4 | _M._VERSION = '0.0.1'
5 |
6 | local ERRORINFO = require('abtesting.error.errcode').info
7 | local systemConf = require('abtesting.utils.init')
8 |
9 | local prefixConf = systemConf.prefixConf
10 | local runtimeLib = prefixConf.runtimeInfoPrefix
11 |
12 | local indices = systemConf.indices
13 | local fields = systemConf.fields
14 |
15 | local divConf = systemConf.divConf
16 | local shdict_expire = divConf.shdict_expire or 60
17 |
18 | _M.new = function(self, sharedDict)
19 | if not sharedDict then
20 | error{ERRORINFO.ARG_BLANK_ERROR, 'cache name valid from nginx.conf'}
21 | end
22 |
23 | self.cache = ngx.shared[sharedDict]
24 | if not self.cache then
25 | error{ERRORINFO.PARAMETER_ERROR, 'cache name [' .. sharedDict .. '] valid from nginx.conf'}
26 | end
27 |
28 | return setmetatable(self, { __index = _M } )
29 | end
30 |
31 | local isNULL = function(v)
32 | return not v or v == ngx.null
33 | end
34 |
35 | local areNULL = function(v1, v2, v3)
36 | if isNULL(v1) or isNULL(v2) or isNULL(v3) then
37 | return true
38 | end
39 | return false
40 | end
41 |
42 | _M.getSteps = function(self, hostname)
43 | local cache = self.cache
44 | local k_divsteps = runtimeLib..':'..hostname..':'..fields.divsteps
45 | local divsteps = cache:get(k_divsteps)
46 | return tonumber(divsteps)
47 | end
48 |
49 | _M.getRuntime = function(self, hostname, divsteps)
50 | local cache = self.cache
51 | local runtimegroup = {}
52 | local prefix = runtimeLib .. ':' .. hostname
53 | for i = 1, divsteps do
54 | local idx = indices[i]
55 | local k_divModname = prefix .. ':'..idx..':'..fields.divModulename
56 | local k_divDataKey = prefix .. ':'..idx..':'..fields.divDataKey
57 | local k_userInfoModname = prefix .. ':'..idx..':'..fields.userInfoModulename
58 |
59 | local divMod, err1 = cache:get(k_divModname)
60 | local divPolicy, err2 = cache:get(k_divDataKey)
61 | local userInfoMod, err3 = cache:get(k_userInfoModname)
62 |
63 | if areNULL(divMod, divPolicy, userInfoMod) then
64 | return false
65 | end
66 |
67 | local runtime = {}
68 | runtime[fields.divModulename ] = divMod
69 | runtime[fields.divDataKey ] = divPolicy
70 | runtime[fields.userInfoModulename] = userInfoMod
71 | runtimegroup[idx] = runtime
72 | end
73 |
74 | return true, runtimegroup
75 |
76 | end
77 |
78 | _M.setRuntime = function(self, hostname, divsteps, runtimegroup)
79 | local cache = self.cache
80 | local prefix = runtimeLib .. ':' .. hostname
81 | local expire = shdict_expire
82 |
83 | for i = 1, divsteps do
84 | local idx = indices[i]
85 |
86 | local k_divModname = prefix .. ':'..idx..':'..fields.divModulename
87 | local k_divDataKey = prefix .. ':'..idx..':'..fields.divDataKey
88 | local k_userInfoModname = prefix .. ':'..idx..':'..fields.userInfoModulename
89 |
90 | local runtime = runtimegroup[idx]
91 | local ok1, err = cache:set(k_divModname, runtime[fields.divModulename], expire)
92 | local ok2, err = cache:set(k_divDataKey, runtime[fields.divDataKey], expire)
93 | local ok3, err = cache:set(k_userInfoModname, runtime[fields.userInfoModulename], expire)
94 | if areNULL(ok1, ok2, ok3) then return false end
95 |
96 | end
97 |
98 | local k_divsteps = prefix ..':'..fields.divsteps
99 | local ok, err = cache:set(k_divsteps, divsteps, shdict_expire)
100 | if not ok then return false end
101 |
102 | return true
103 | end
104 |
105 | _M.getUpstream = function(self, divsteps, usertable)
106 | local upstable = {}
107 | local cache = self.cache
108 | for i = 1, divsteps do
109 | local idx = indices[i]
110 | local info = usertable[idx]
111 | -- ups will be an actually value or nil
112 | if info then
113 | local ups = cache:get(info)
114 | upstable[idx] = ups
115 | end
116 | end
117 | return upstable
118 | end
119 |
120 | _M.setUpstream = function(self, info, upstream)
121 | local cache = self.cache
122 | local expire = shdict_expire
123 | cache:set(info, upstream, expire)
124 | end
125 |
126 | return _M
127 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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 < 10000
21 | then
22 | error("ngx_lua 0.10.1+ 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.4"
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 |
--------------------------------------------------------------------------------
/lib/abtesting/diversion/uidrange.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingDiversionUidrange"
2 |
3 | local _M = {}
4 | local mt = { __index = _M }
5 | _M._VERSION = "0.0.1"
6 |
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 |
9 | local offset = 0.3
10 | local k_start = 'start'
11 | local k_end = 'end'
12 | local k_range = 'range'
13 | local k_upstream= 'upstream'
14 | local k_index = 'index'
15 |
16 | _M.new = function(self, database, policyLib)
17 | if not database then
18 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
19 | end if not policyLib then
20 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
21 | end
22 |
23 | self.database = database
24 | self.policyLib = policyLib
25 | return setmetatable(self, mt)
26 | end
27 |
28 | -- policy is in format {range = { start = 13411, end = 435435}, upstream = 'upstream1'},
29 | _M.check = function(self, policy)
30 | if not next(policy) then
31 | local info = ERRORINFO.POLICY_INVALID_ERROR
32 | local desc = 'policy is blank'
33 | return {false, info, desc}
34 | end
35 |
36 | table.sort(policy, function(n1, n2) return n1['range']['start'] < n2['range']['start'] end)
37 |
38 | local range, upstream
39 | local stuid, eduid
40 | local last_eduid
41 | for i, v in pairs(policy) do
42 | range, upstream = v[k_range], v[k_upstream]
43 | stuid = range['start']
44 | eduid = range['end']
45 |
46 | if type(upstream) ~= 'string' then
47 | local info = ERRORINFO.POLICY_INVALID_ERROR
48 | local desc = 'upstream invalid'
49 | return {false, info, desc}
50 | end
51 |
52 | if stuid > eduid then
53 | local info = ERRORINFO.POLICY_INVALID_ERROR
54 | local desc = 'range error for start < end'
55 | return {false, info, desc}
56 | end
57 |
58 | if i > 1 then
59 | if stuid <= last_eduid then
60 | local info = ERRORINFO.POLICY_INVALID_ERROR
61 | local desc = 'uidrange overlapped'
62 | return {false, info, desc}
63 | end
64 | end
65 |
66 | last_eduid = eduid
67 | end
68 |
69 | return {true}
70 | end
71 |
72 | _M.set = function(self, policy)
73 | local database = self.database
74 | local policyLib = self.policyLib
75 |
76 | local policyidx = 0
77 | database:init_pipeline()
78 | for i, v in pairs(policy) do
79 | local range, upstream = v[k_range], v[k_upstream]
80 | local stuid = range['start'] - offset
81 | local eduid = range['end'] + offset
82 | local left = policyidx * 2
83 | local right = policyidx * 2 + 1
84 | local leftBorder = left.. ':'..upstream
85 | local rightBorder = right..':'..upstream
86 | database:zadd(policyLib, stuid, leftBorder, eduid, rightBorder)
87 |
88 | policyidx = policyidx + 1
89 | end
90 |
91 | local ok, err = database:commit_pipeline()
92 | if not ok then
93 | error{ERRORINFO.REDIS_ERROR, err}
94 | end
95 | end
96 |
97 | _M.get = function(self)
98 | local database = self.database
99 | local policyLib = self.policyLib
100 |
101 | local data, err = database:zrange(policyLib, 0, -1, 'withscores')
102 | if not data then
103 | error{ERRORINFO.REDIS_ERROR, err}
104 | end
105 | local n = #data
106 | local policy = {}
107 | for i = 1, n, 2 do
108 | policy[data[i]] = data[i+1]
109 | end
110 |
111 | return policy
112 | end
113 |
114 | _M.getUpstream = function(self, uid)
115 | if not tonumber(uid) then
116 | return nil
117 | end
118 |
119 | local database, policyLib = self.database, self.policyLib
120 |
121 | local val, err = database:zrangebyscore(policyLib, uid, '+inf', 'limit','0', '1', 'withscores')
122 | if not val then error{ERRORINFO.REDIS_ERROR, err} end
123 |
124 | if not next(val) then return nil end
125 |
126 | local index_upstream = val[1]
127 |
128 | local colonPosition = string.find(index_upstream, ':')
129 | if colonPosition == nil then error{ERRORINFO.POLICY_DB_ERROR} end
130 |
131 | local index = string.sub(index_upstream, 1, colonPosition - 1)
132 | local upstream = string.sub(index_upstream, colonPosition + 1)
133 |
134 | if string.len(index) < 1 or string.len(upstream) < 1 then
135 | error{ERRORINFO.POLICY_DB_ERROR}
136 | end
137 |
138 | if index % 2 == 0 then upstream = nil end
139 |
140 | return upstream
141 |
142 | end
143 |
144 | return _M
145 |
146 |
--------------------------------------------------------------------------------
/lib/abtesting/diversion/iprange.lua:
--------------------------------------------------------------------------------
1 | local modulename = "abtestingDiversionIprange"
2 |
3 | local _M = {}
4 | local mt = { __index = _M }
5 | _M._VERSION = "0.0.1"
6 |
7 | local ERRORINFO = require('abtesting.error.errcode').info
8 |
9 | local offset = 0.3
10 | local k_start = 'start'
11 | local k_end = 'end'
12 | local k_range = 'range'
13 | local k_upstream= 'upstream'
14 | local k_index = 'index'
15 |
16 | _M.new = function(self, database, policyLib)
17 | if not database then
18 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
19 | end if not policyLib then
20 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
21 | end
22 |
23 | self.database = database
24 | self.policyLib = policyLib
25 | return setmetatable(self, mt)
26 | end
27 |
28 | -- policy is in format {range = { start = 13411, end = 435435}, upstream = 'upstream1'},
29 | _M.check = function(self, policy)
30 | if not next(policy) then
31 | local info = ERRORINFO.POLICY_INVALID_ERROR
32 | local desc = 'policy is blank'
33 | return {false, info, desc}
34 | end
35 | table.sort(policy, function(n1, n2) return n1['range']['start'] < n2['range']['start'] end)
36 |
37 | local range, upstream
38 | local stip, edip
39 | local last_edip
40 | for i, v in pairs(policy) do
41 | range, upstream = v[k_range], v[k_upstream]
42 | stip = range['start']
43 | edip = range['end']
44 |
45 | if type(upstream) ~= 'string' then
46 | local info = ERRORINFO.POLICY_INVALID_ERROR
47 | local desc = 'upstream invalid'
48 | return {false, info, desc}
49 | end
50 |
51 | if stip > edip then
52 | local info = ERRORINFO.POLICY_INVALID_ERROR
53 | local desc = 'range error for start < end'
54 | return {false, info, desc}
55 | end
56 |
57 | if i > 1 then
58 | if stip <= last_edip then
59 | local info = ERRORINFO.POLICY_INVALID_ERROR
60 | local desc = 'iprange overlapped'
61 | return {false, info, desc}
62 | end
63 | end
64 |
65 | last_edip = edip
66 | end
67 |
68 | return {true}
69 | end
70 |
71 | _M.set = function(self, policy)
72 | local database = self.database
73 | local policyLib = self.policyLib
74 |
75 | local policyidx = 0
76 | database:init_pipeline()
77 | for i, v in pairs(policy) do
78 | local range, upstream = v[k_range], v[k_upstream]
79 | local stip = range['start'] - offset
80 | local edip = range['end'] + offset
81 | local left = policyidx * 2
82 | local right = policyidx * 2 + 1
83 | local leftBorder = left.. ':'..upstream
84 | local rightBorder = right..':'..upstream
85 |
86 | database:zadd(policyLib, stip, leftBorder, edip, rightBorder)
87 |
88 | policyidx = policyidx + 1
89 | end
90 |
91 | local ok, err = database:commit_pipeline()
92 | if not ok then
93 | error{ERRORINFO.REDIS_ERROR, err}
94 | end
95 | end
96 |
97 | _M.get = function(self)
98 | local database = self.database
99 | local policyLib = self.policyLib
100 |
101 | local data, err = database:zrange(policyLib, 0, -1, 'withscores')
102 | if not data then
103 | error{ERRORINFO.REDIS_ERROR, err}
104 | end
105 | local n = #data
106 | local policy = {}
107 | for i = 1, n, 2 do
108 | policy[data[i]] = data[i+1]
109 | end
110 |
111 | return policy
112 | end
113 |
114 | _M.getUpstream = function(self, ip)
115 | if not tonumber(ip) then
116 | return nil
117 | end
118 |
119 | local database, policyLib = self.database, self.policyLib
120 |
121 | local val, err = database:zrangebyscore(policyLib, ip, '+inf', 'limit','0', '1', 'withscores')
122 | if not val then error{ERRORINFO.REDIS_ERROR, err} end
123 |
124 | if not next(val) then return nil end
125 |
126 | local index_upstream = val[1]
127 |
128 | local colonPosition = string.find(index_upstream, ':')
129 | if colonPosition == nil then error{ERRORINFO.POLICY_DB_ERROR} end
130 |
131 | local index = string.sub(index_upstream, 1, colonPosition - 1)
132 | local upstream = string.sub(index_upstream, colonPosition + 1)
133 |
134 | if string.len(index) < 1 or string.len(upstream) < 1 then
135 | error{ERRORINFO.POLICY_DB_ERROR}
136 | end
137 |
138 | if index % 2 == 0 then upstream = nil end
139 |
140 | return upstream
141 |
142 | end
143 |
144 | return _M
145 |
146 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/decode-base64.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 5);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | local verbose = false
20 | if verbose then
21 | local dump = require "jit.dump"
22 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
23 | else
24 | local v = require "jit.v"
25 | v.on("$Test::Nginx::Util::ErrLogFile")
26 | end
27 |
28 | require "resty.core"
29 | -- jit.off()
30 | ';
31 | _EOC_
32 |
33 | #no_diff();
34 | #no_long_string();
35 | check_accum_error_log();
36 | run_tests();
37 |
38 | __DATA__
39 |
40 | === TEST 1: string
41 | --- http_config eval: $::HttpConfig
42 | --- config
43 | location = /base64 {
44 | content_by_lua '
45 | local s
46 | for i = 1, 100 do
47 | s = ngx.decode_base64("aGVsbG8=")
48 | end
49 | ngx.say(s)
50 | ';
51 | }
52 | --- request
53 | GET /base64
54 | --- response_body
55 | hello
56 | --- error_log eval
57 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
58 | --- no_error_log
59 | [error]
60 | -- NYI:
61 |
62 |
63 |
64 | === TEST 2: set base64 (nil)
65 | --- http_config eval: $::HttpConfig
66 | --- config
67 | location = /base64 {
68 | content_by_lua '
69 | local s
70 | for i = 1, 100 do
71 | s = ngx.decode_base64("")
72 | end
73 | ngx.say(s)
74 | ';
75 | }
76 | --- request
77 | GET /base64
78 | --- response_body eval: "\n"
79 | --- error_log eval
80 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
81 | --- no_error_log
82 | [error]
83 | -- NYI:
84 |
85 |
86 |
87 | === TEST 3: set base64 (number)
88 | --- http_config eval: $::HttpConfig
89 | --- config
90 | location = /base64 {
91 | content_by_lua '
92 | local s
93 | for i = 1, 100 do
94 | s = ngx.decode_base64("My4xNA==")
95 | end
96 | ngx.say(s)
97 | ';
98 | }
99 | --- request
100 | GET /base64
101 | --- response_body
102 | 3.14
103 | --- error_log eval
104 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
105 | --- no_error_log
106 | [error]
107 | -- NYI:
108 |
109 |
110 |
111 | === TEST 4: set base64 (boolean)
112 | --- http_config eval: $::HttpConfig
113 | --- config
114 | location = /base64 {
115 | content_by_lua '
116 | local s
117 | for i = 1, 100 do
118 | s = ngx.decode_base64("dHJ1ZQ==")
119 | end
120 | ngx.say(s)
121 | ';
122 | }
123 | --- request
124 | GET /base64
125 | --- response_body
126 | true
127 | --- error_log eval
128 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
129 | --- no_error_log
130 | [error]
131 | -- NYI:
132 |
133 |
134 |
135 | === TEST 5: string (buf size just smaller than 4096)
136 | --- http_config eval: $::HttpConfig
137 | --- config
138 | location = /base64 {
139 | content_by_lua '
140 | local s
141 | for i = 1, 100 do
142 | s = ngx.decode_base64(string.rep("a", 5460))
143 | end
144 | if not s then
145 | ngx.say("bad base64 string")
146 | else
147 | ngx.say(string.len(s))
148 | end
149 | ';
150 | }
151 | --- request
152 | GET /base64
153 | --- response_body
154 | 4095
155 | --- error_log eval
156 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
157 | --- no_error_log
158 | [error]
159 | -- NYI:
160 |
161 |
162 |
163 | === TEST 6: string (buf size just a bit bigger than 4096)
164 | --- http_config eval: $::HttpConfig
165 | --- config
166 | location = /base64 {
167 | content_by_lua '
168 | local s
169 | for i = 1, 100 do
170 | s = ngx.decode_base64(string.rep("a", 5462))
171 | end
172 | if not s then
173 | ngx.say("bad base64 string")
174 | else
175 | ngx.say(string.len(s))
176 | end
177 | ';
178 | }
179 | --- request
180 | GET /base64
181 | --- response_body
182 | 4096
183 | --- error_log eval
184 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
185 | --- no_error_log
186 | [error]
187 | -- NYI:
188 |
189 |
--------------------------------------------------------------------------------
/utils/pytool/pydygateway.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | #!/usr/bin/env python
3 |
4 | import argparse, requests
5 | import sys, string, time
6 | from xml.parsers.expat import ParserCreate
7 | try:
8 | import json
9 | except:
10 | import simplejson as json
11 |
12 | cmdFileMode = 'cmd file mode'
13 | cmdInteractMode = 'cmd interaction mode'
14 |
15 |
16 | specialArgs = ['host']
17 | commonArgs = ['add_upstream']
18 | HOST = None
19 |
20 | def doSpecArg(kvCmd):
21 | key = kvCmd[0].strip()
22 | val = kvCmd[1].strip()
23 |
24 | if str.upper(key) == 'HOST':
25 | global HOST
26 | HOST = val
27 |
28 | def parseCmdFile(filename):
29 | f = open(filename)
30 | # error handle
31 |
32 | cmds = []
33 | lineN = 0
34 | for line in f.readlines():
35 | lineN = lineN + 1
36 | line = line.strip()
37 | if line == '' or line[0] == '*':
38 | continue
39 |
40 | kvCmd = line.split(':', 1)
41 | if len(kvCmd) < 2:
42 | print 'cmd error for invalid number of args in line NO. ' + str(lineN)
43 | sys.exit()
44 |
45 | if kvCmd[0] in specialArgs:
46 | doSpecArg(kvCmd)
47 | else:
48 | if not HOST:
49 | print 'HOST is not assigned'
50 | sys.exit()
51 |
52 | cmdname = kvCmd[0]
53 | cmdmod = __import__('command')
54 |
55 | cmdclass = getattr(cmdmod, cmdname)
56 | cmdobj = cmdclass(HOST, kvCmd[1])
57 | valid, err = cmdobj.check()
58 | if not valid:
59 | hint = 'cmd [' + line + '] invalid in line No. ' + str(lineN)
60 | if err:
61 | hint += ' : reason is ' + err
62 | print hint
63 | sys.exit()
64 |
65 | cmds.append(cmdobj)
66 | return cmds
67 |
68 | def doCmdFile(filename):
69 |
70 | cmds = parseCmdFile(filename)
71 |
72 | if not cmds or type(cmds) != list or len(cmds) < 1:
73 | print 'parse cmd file' , filename , 'error, please check the file'
74 | sys.exit()
75 | print '\033[1;34;40m'
76 | print 'Your cmds are listed below:'
77 | for cmdobj in cmds:
78 | print '\033[1;32;40m'
79 | print 'No.', cmds.index(cmdobj), 'cmd:', cmdobj.cmd['action']
80 | print '\033[1;34;40m'
81 | cmdobj.echo()
82 | print '\033[0m'
83 |
84 | print '\n************************************************'
85 | while(True):
86 | c = raw_input('Are cmds correct? If correct they will be executed immediately[Y/N]')
87 | if c == '' or c == 'y' or c == 'Y':
88 | break
89 | else:
90 | print 'terminate this program and edit your cmd file again'
91 | sys.exit()
92 |
93 | print '\n************************************************'
94 | for cmdobj in cmds:
95 | print '\033[1;32;40m'
96 | print '------------------cmd------------------'
97 | print '\033[1;33;40m'
98 | cmdobj.echo()
99 |
100 | ret = cmdobj.run()
101 | print '\033[1;34;40m'
102 | print '-----------------result-----------------\n'
103 | print json.dumps(ret, indent = 1)
104 |
105 | print '\033[1;31;40m'
106 | print '------------------end------------------'
107 |
108 | c = raw_input('\nIs the result correct? If correct it will be continued[Y/N]')
109 | if c == '' or c == 'y' or c == 'Y':
110 | pass
111 | else:
112 | print 'terminate this program and edit your cmd file again'
113 | sys.exit()
114 | print '\033[0m'
115 |
116 |
117 | def doCmdInteract():
118 | pass
119 |
120 | if __name__ == '__main__':
121 |
122 | parser = argparse.ArgumentParser(description='dygateway cmd utility')
123 | parser.add_argument('-s', action="store", dest="filename")
124 | result = parser.parse_args(sys.argv[1:])
125 |
126 | if result.filename:
127 | print cmdFileMode
128 | print '\n************************************************'
129 |
130 | doCmdFile(result.filename)
131 |
132 | else:
133 | print cmdInteractMode
134 |
135 |
136 | ### todo list
137 | # checkPort() exception handle
138 | # file.readlines() 会有中间为空的元素吗
139 |
140 | ### acknowledge
141 | # python 参数处理
142 | # python 异常处理 & 主动异常
143 | # python list
144 | # python dict
145 | # python file.readline
146 | # python str is or not null
147 | # python input and rawinput
148 | # python 控制终端颜色
149 | # python upper case and lower case convert
150 |
151 | ### know issues
152 | # dyupsc模块需要有一定时间让各个work能够同步upstream信息
153 | # ab模块的runtime_set无法获得policy_set(添加策略)返回的policyid,需要进一步的商量
154 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/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_cast = ffi.cast
10 | local ffi_str = ffi.string
11 | local new_tab = base.new_tab
12 | local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
13 | local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
14 | local FFI_DECLINED = base.FFI_DECLINED
15 | local get_string_buf = base.get_string_buf
16 | local getmetatable = getmetatable
17 | local type = type
18 | local tostring = tostring
19 | local getfenv = getfenv
20 | local error = error
21 | local ngx = ngx
22 |
23 |
24 | local MAX_HEADER_VALUES = 100
25 | local errmsg = base.get_errmsg_ptr()
26 | local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
27 | local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
28 |
29 |
30 | ffi.cdef[[
31 | int ngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r,
32 | const char *key_data, size_t key_len, int is_nil,
33 | const char *sval, size_t sval_len, ngx_http_lua_ffi_str_t *mvals,
34 | size_t mvals_len, char **errmsg);
35 |
36 | int ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r,
37 | const unsigned char *key, size_t key_len,
38 | unsigned char *key_buf, ngx_http_lua_ffi_str_t *values,
39 | int max_nvalues);
40 | ]]
41 |
42 |
43 | local function set_resp_header(tb, key, value)
44 | local r = getfenv(0).__ngx_req
45 | if not r then
46 | return error("no request found")
47 | end
48 |
49 | if type(key) ~= "string" then
50 | key = tostring(key)
51 | end
52 |
53 | local rc
54 | if value == nil then
55 | rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, true, nil, 0,
56 | nil, 0, errmsg)
57 | else
58 | local sval, sval_len, mvals, mvals_len, buf
59 |
60 | if type(value) == "table" then
61 | mvals_len = #value
62 | buf = get_string_buf(ffi_str_size * mvals_len)
63 | mvals = ffi_cast(ffi_str_type, buf)
64 | for i = 1, mvals_len do
65 | local s = value[i]
66 | if type(s) ~= "string" then
67 | s = tostring(s)
68 | value[i] = s
69 | end
70 | local str = mvals[i - 1]
71 | str.data = s
72 | str.len = #s
73 | end
74 |
75 | sval_len = 0
76 |
77 | else
78 | if type(value) ~= "string" then
79 | sval = tostring(value)
80 | else
81 | sval = value
82 | end
83 | sval_len = #sval
84 |
85 | mvals_len = 0
86 | end
87 |
88 | rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, false, sval,
89 | sval_len, mvals, mvals_len,
90 | errmsg)
91 | end
92 |
93 | if rc == 0 or rc == FFI_DECLINED then
94 | return
95 | end
96 |
97 | if rc == FFI_NO_REQ_CTX then
98 | return error("no request ctx found")
99 | end
100 |
101 | if rc == FFI_BAD_CONTEXT then
102 | return error("API disabled in the current context")
103 | end
104 |
105 | -- rc == FFI_ERROR
106 | return error(ffi_str(errmsg[0]))
107 | end
108 |
109 |
110 | local function get_resp_header(tb, key)
111 | local r = getfenv(0).__ngx_req
112 | if not r then
113 | return error("no request found")
114 | end
115 |
116 | if type(key) ~= "string" then
117 | key = tostring(key)
118 | end
119 |
120 | local key_len = #key
121 |
122 | local key_buf = get_string_buf(key_len + ffi_str_size * MAX_HEADER_VALUES)
123 | local values = ffi_cast(ffi_str_type, key_buf + key_len)
124 | local n = C.ngx_http_lua_ffi_get_resp_header(r, key, key_len, key_buf,
125 | values, MAX_HEADER_VALUES)
126 |
127 | -- print("retval: ", n)
128 |
129 | if n == FFI_BAD_CONTEXT then
130 | return error("API disabled in the current context")
131 | end
132 |
133 | if n == 0 then
134 | return nil
135 | end
136 |
137 | if n == 1 then
138 | local v = values[0]
139 | return ffi_str(v.data, v.len)
140 | end
141 |
142 | if n > 0 then
143 | local ret = new_tab(n, 0)
144 | for i = 1, n do
145 | local v = values[i - 1]
146 | ret[i] = ffi_str(v.data, v.len)
147 | end
148 | return ret
149 | end
150 |
151 | -- n == FFI_ERROR
152 | return error("no memory")
153 | end
154 |
155 |
156 | do
157 | local mt = getmetatable(ngx.header)
158 | mt.__newindex = set_resp_header
159 | mt.__index = get_resp_header
160 | end
161 |
162 |
163 | return {
164 | version = base.version
165 | }
166 |
--------------------------------------------------------------------------------
/doc/nginx_conf_配置过程.md:
--------------------------------------------------------------------------------
1 | nginx.conf的配置过程
2 | ===========================
3 |
4 | dygateway主要基于ngx_lua开发,需要在nginx配置文件中做大量配置,以配合lua代码实现功能,因此nginx.conf中的配置相当多。首先介绍下配置文件的结构:
5 |
6 | dygateway配置文件结构示意
7 | -------------------------
8 |
9 | * nginx.conf: 总nginx配置文件
10 |
11 | ```bash
12 | http {
13 | 1. 设置lua路径相关
14 | 2. lua code相关配置
15 | 3. nginx的一些通用配置
16 |
17 | include "upstream.conf";
18 | include "default.conf";
19 | include "s1.conf";
20 | include "s2.conf";
21 | }
22 | ```
23 |
24 | * default.conf:dygateway的管理相关配置
25 |
26 | ```bash
27 | server {
28 | # ab管理接口
29 | location ab_admin {
30 |
31 | }
32 | }
33 | ```
34 |
35 | * virtual server conf:单个virtual host配置
36 |
37 | ```bash
38 | #s1.conf:virtual host配置
39 |
40 | # 设置ngx_lua级别的cache
41 |
42 | lua_shared_dict abc_sysConfig 1m;
43 | lua_shared_dict kv_abc_upstream 100m;
44 |
45 | server {
46 |
47 | # 分流接口 /abc
48 | location /abc {
49 | set $hostkey api.weibo.cn.abc;
50 | set $sysConfig abc_sysConfig;
51 | set $kv_upstream kv_abc_upstream;
52 |
53 | set $backend 'default_upstream';
54 | rewrite_by_lua_file "luacode/ab/diversion.lua";
55 | proxy_pass http://$backend;
56 | }
57 | }
58 | ```
59 |
60 | nginx配置过程
61 | ---------------------------------
62 |
63 | * ***Step*** 1. 在nginx.conf中,http配置块里,添加如下配置。该配置在nginx全局有效。
64 |
65 | ```bash
66 | #打开lua的代码缓存
67 | lua_code_cache on;
68 |
69 | #lua代码的路径
70 | lua_package_path "/usr/local/dygateway/luacode/ab/?.lua;/usr/local/dygateway/luacode/ab/lib/?.lua;/usr/local/dygateway/luacode/ab/lib/lua-resty-core/lib/?.lua;;";
71 |
72 | #ngx_lua获取post数据配置
73 | lua_need_request_body on;
74 |
75 | ```
76 |
77 | * ***Step*** 2. 在管理server的server配置块内添加:
78 |
79 | ```bash
80 | # ab管理功能需要读写redis数据库,所以需要配置
81 | set $redis_host '127.0.0.1'; --本机redis的IP
82 | set $redis_port '6379'; --本机redis的port
83 | set $redis_uds '/tmp/redis.sock'; --本机redis的uds设置,优先使用uds
84 | set $redis_connect_timeout 10000; --设置连接超时时间
85 | set $redis_dbid 0; --设置选择redis db0作为存储库
86 |
87 | set $redis_pool_size 1000; --lua-resty-redis的连接池大小
88 | set $redis_keepalive_timeout 90000; --(连接池keepalive_time, in ms)
89 |
90 | # ab管理功能配置
91 | location = /ab_admin {
92 | content_by_lua_file '/usr/local/dygateway/luacode/ab/admin/ab_action.lua';
93 | }
94 |
95 | ```
96 |
97 | * ***Step*** 3. virtual host s1.conf配置
98 |
99 | ```bash
100 |
101 | # location / 的 运行时信息缓存
102 | lua_shared_dict root_sysConfig 1m;
103 | # location / 的 info:upstream 缓存
104 | lua_shared_dict kv_root_upstream 100m;
105 |
106 |
107 | # location /abc 的 运行时信息缓存
108 | lua_shared_dict abc_sysConfig 1m;
109 | # location /abc 的 info:upstream 缓存
110 | lua_shared_dict kv_abc_upstream 100m;
111 |
112 | server {
113 | listen 8030 backlog=16384;
114 | server_name api.weibo.cn;
115 |
116 | set $redis_host '127.0.0.1';
117 | set $redis_port '6379';
118 | set $redis_uds '/tmp/redis.sock';
119 | set $redis_connect_timeout 10000;
120 | set $redis_dbid 0;
121 | set $redis_pool_size 1000;
122 | set $redis_keepalive_timeout 90000; #(keepalive_time, in ms)
123 |
124 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
125 | proxy_set_header Host $http_host;
126 | proxy_set_header X-Real-IP $remote_addr;
127 | proxy_set_header Connection "";
128 | proxy_http_version 1.1;
129 |
130 |
131 | location / {
132 | # 指定该接口的HOST,用于配置运行时信息:api.weibo.cn,(可任意取名,只要进入本loction,运行时信息以此值为key,为本location设置运行时信息时,也以此值为key,需要约定好)
133 | set $hostkey $server_name;
134 |
135 | # 指定sysConfig的名字,与 缓存名字 root_sysConfig 一样(可任意取名,不要与别的lua_shared_dict冲突即可,但要与之前声明的shared_dict名字一样:root_sysConfig)
136 | set $sysConfig root_sysConfig;
137 | # 指定kv_upstream 的名字 与 缓存名字 kv_root_upstream 一样
138 | set $kv_upstream kv_root_upstream;
139 |
140 | # 设置默认upstream(该upstrema必须存在于upstream.conf,并且应该考虑到大部分请求将分流至默认upstream)
141 | set $backend 'stable';
142 |
143 | rewrite_by_lua_file '/usr/local/dygateway/luacode/ab/diversion/diversion.lua';
144 |
145 | proxy_pass http://$backend;
146 | }
147 |
148 | location /abc {
149 |
150 | # 指定该接口的HOST,用于配置运行时信息:api.weibo.cn.abc
151 | set $hostkey $server_name.abc;
152 |
153 | set $sysConfig abc_sysConfig;
154 | set $kv_upstream kv_abc_upstream;
155 |
156 | set $backend 'stable';
157 |
158 | rewrite_by_lua_file '/usr/local/dygateway/luacode/ab/diversion/diversion.lua';
159 | proxy_pass http://$backend;
160 | }
161 | }
162 |
163 | ```
164 | * ***Step*** 4. upstream.conf配置
165 |
166 | ```bash
167 | #必须要有默认upstream
168 |
169 | upstream stable {
170 | server 1
171 | server 2
172 | ...
173 | }
174 |
175 | #以及其他upstream
176 | upstream bar {
177 |
178 | }
179 |
180 | upstream foo {
181 |
182 | }
183 | ```
184 |
185 |
186 |
--------------------------------------------------------------------------------
/lib/abtesting/adapter/policy.lua:
--------------------------------------------------------------------------------
1 | ---
2 | -- @classmod abtesting.adapter.policy
3 | -- @release 0.0.1
4 | local modulename = "abtestingAdapterPolicy"
5 |
6 | local _M = { _VERSION = "0.0.1" }
7 | local mt = { __index = _M }
8 |
9 | local ERRORINFO = require('abtesting.error.errcode').info
10 | local fields = require('abtesting.utils.init').fields
11 |
12 | local separator = ':'
13 | ---
14 | -- policyIO new function
15 | -- @param database opened redis.
16 | -- @param baseLibrary a library(prefix of redis key) of policies.
17 | -- @return runtimeInfoIO object
18 | _M.new = function(self, database, baseLibrary)
19 | if not database then
20 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
21 | end
22 | if not baseLibrary then
23 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy baselib'}
24 | end
25 |
26 | self.database = database
27 | self.baseLibrary = baseLibrary
28 | self.idCountKey = table.concat({baseLibrary, fields.idCount}, separator)
29 |
30 | local ok, err = database:exists(self.idCountKey)
31 | if not ok then error{ERRORINFO.REDIS_ERROR, err} end
32 |
33 | if 0 == ok then
34 | local ok, err = database:set(self.idCountKey, '-1')
35 | if not ok then error{ERRORINFO.REDIS_ERROR, err} end
36 | end
37 | return setmetatable(self, mt)
38 | end
39 |
40 | ---
41 | -- get id for current policy
42 | -- @return the id
43 | _M.getIdCount = function(self)
44 | local database = self.database
45 | local key = self.idCountKey
46 | local idCount, err = database:incr(key)
47 | if not idCount then error{ERRORINFO.REDIS_ERROR, err} end
48 |
49 | return idCount
50 | end
51 |
52 | ---
53 | -- private function, set diversion type
54 | -- @param id identify a policy
55 | -- @param divtype diversion type (ipange/uid/...)
56 | -- @return allways returned SUCCESS
57 | _M._setDivtype = function(self, id, divtype)
58 | local database = self.database
59 | local key = table.concat({self.baseLibrary, id, fields.divtype}, separator)
60 | local ok, err = database:set(key, divtype)
61 | if not ok then error{ERRORINFO.REDIS_ERROR, err} end
62 | end
63 |
64 | ---
65 | -- private function, set diversion data
66 | -- @param id identify a policy
67 | -- @param divdata diversion data
68 | -- @param modulename module name of diversion data (decision by diversion type)
69 | -- @return allways returned SUCCESS
70 | _M._setDivdata = function(self, id, divdata, modulename)
71 | local divModule = require(modulename)
72 | local database = self.database
73 | local key = table.concat({self.baseLibrary, id, fields.divdata}, separator)
74 |
75 | divModule:new(database, key):set(divdata)
76 | end
77 |
78 | ---
79 | -- addtion a policy to specified redis lib
80 | -- @param policy policy of addtion
81 | -- @return allways returned SUCCESS
82 | _M.set = function(self, policy)
83 | local id = self:getIdCount()
84 | local database = self.database
85 | local divModulename = table.concat({'abtesting', 'diversion', policy.divtype}, '.')
86 |
87 | self:_setDivtype(id, policy.divtype)
88 | self:_setDivdata(id, policy.divdata, divModulename)
89 |
90 | return id
91 | end
92 |
93 | _M.check = function(self, policy)
94 | local divModulename = table.concat({'abtesting', 'diversion', policy.divtype}, '.')
95 | local divModule = require(divModulename)
96 | local database = self.database
97 |
98 | return divModule:new(database, ''):check(policy.divdata)
99 | end
100 |
101 | ---
102 | -- delete a policy from specified redis lib
103 | -- @param id the policy identify
104 | -- @return allways returned SUCCESS
105 | _M.del = function(self, id)
106 | local database = self.database
107 | local baseLibrary = self.baseLibrary
108 |
109 | local policyLib = baseLibrary .. ':' .. id .. ':'
110 |
111 | local keys, err = database:keys(policyLib..'*')
112 | if not keys then
113 | error{ERRORINFO.REDIS_ERROR, err}
114 | end
115 |
116 | database:init_pipeline()
117 | for _i, key in pairs(keys) do
118 | database:del(key)
119 | end
120 | local ok, err = database:commit_pipeline()
121 | if not ok then error{ERRORINFO.REDIS_ERROR, err} end
122 |
123 | end
124 |
125 | _M.get = function(self, id)
126 | local divTypeKey = table.concat({self.baseLibrary, id, fields.divtype}, separator)
127 | local divDataKey = table.concat({self.baseLibrary, id, fields.divdata}, separator)
128 | local database = self.database
129 | local policy = {}
130 | policy.divtype = ngx.null
131 | policy.divdata = ngx.null
132 |
133 | local divtype, err = database:get(divTypeKey)
134 | if not divtype then
135 | error{ERRORINFO.REDIS_ERROR, err}
136 | elseif divtype == ngx.null then
137 | return policy
138 | end
139 |
140 | local divModulename = table.concat({'abtesting', 'diversion', divtype}, '.')
141 | local divModule = require(divModulename):new(database, divDataKey)
142 |
143 | local divdata = divModule:get()
144 | policy.divtype = divtype
145 | policy.divdata = divdata
146 | return policy
147 | end
148 |
149 | return _M
150 |
--------------------------------------------------------------------------------
/doc/ABTestingGateway之添加新的分流方式.md:
--------------------------------------------------------------------------------
1 | ABTestingGateway 的基于分流策略的动态更新来实现动态调度的。
2 | 当开发者需要结合自身需求添加新的分流方式时,首先需要为其指定分流策略divPolicy,然后开发分流模块divModule和响应的信息提取模块uesrInfoModule。
3 | 下面我们以一个小例子来说明添加新分流方式的方法,我们的新需求是按照请求url的arg参数中的city字段分流。
4 |
5 | ###为新的分流方式制定分流策略
6 | ABTestingGateway的分流策略有固定格式:
7 | ```bash
8 | {
9 | "divtype":"arg_city",
10 | "divdata":[
11 | {"city":"BJ", "upstream":"beta1"},
12 | {"city":"SH", "upstream":"beta2"},
13 | {"city":"TJ", "upstream":"beta1"},
14 | {"city":"CQ", "upstream":"beta3"}]
15 | }
16 | ```
17 | 分流策略的divtype在下一步是分流模块名的关键部分。
18 | 分流策略的divdata是策略内容,由于是按照city字段分流,这种kv形式的策略,在数据库层面可以采用redis的hash实现,在缓存层可以采用ngx_lua的sharedDict实现。
19 |
20 | ###开发分流模块divModule
21 |
22 | ABTestingGateway的分流模块都在**/lib/abtesting/diversion/**文件夹中,其下的每个lua文件是一个分流模块,比如iprange分流方式的分流模块就是**lib/abtesting/diversion/iprange.lua**,而我们的arg_city分流方式根据divtype就是**lib/abtesting/diversion/arg_city.lua**。
23 |
24 | 分流模块主要有两个功能,一是分流策略的管理功能,包括检查策略合法、添加策略set、读取策略get;二是分流功能getUpstream,这个接口得到用户请求对应的upstream。
25 |
26 | arg_city.lua是一个典型Lua Module实现:
27 | ```lua
28 | local modulename = "abtestingDiversionArgCity"
29 |
30 | local _M = {}
31 | local mt = { __index = _M }
32 | _M._VERSION = "0.0.1"
33 |
34 | _M.new = function(self, database, policyLib)
35 | self.database = database
36 | self.policyLib = policyLib
37 | return setmetatable(self, mt)
38 | end
39 |
40 | _M.check = function(self, policy)
41 | ...
42 | end
43 |
44 | _M.set = function(self, policy)
45 | ...
46 | end
47 |
48 | _M.get = function(self)
49 | ...
50 | end
51 |
52 | _M.getUpstream = function(self, city)
53 | ...
54 | end
55 |
56 | return _M
57 | ```
58 |
59 | ####1. 分流模块初始化方法
60 | ```lua
61 | _M.new = function(self, database, policyLib)
62 | if not database then
63 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
64 | end if not policyLib then
65 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
66 | end
67 |
68 | self.database = database
69 | self.policyLib = policyLib
70 | return setmetatable(self, mt)
71 | end
72 | ```
73 | 在分流模块初始化方法中,database是策略数据库,目前是redis;policyLib是分流策略在数据库中的key。
74 | 而
75 | ```lua
76 | error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
77 | ```
78 | 是ABTestingGateway设计的基于xpcall的防御性机制,用于处理捕获异常。**ERRORINFO**作为系统的错误码编号,具体内容在**/lib/abtesting/error/errcode.lua**中
79 | ####2.策略检查 check方法
80 | 主要功能是对用户输入的策略进行合法性检查
81 | ```lua
82 | _M.check = function(self, policy)
83 |
84 | for _, v in pairs(policy) do
85 | local city = v[k_city]
86 | local upstream = v[k_upstream]
87 |
88 | if not city or not upstream then
89 | local info = ERRORINFO.POLICY_INVALID_ERROR
90 | local desc = ' need '..k_city..' and '..k_upstream
91 | return {false, info, desc}
92 | end
93 |
94 | end
95 |
96 | return {true}
97 | end
98 | ```
99 |
100 | ####3.策略添加 set方法
101 | 向系统中添加用户策略,这里的策略policy是经过check后的。
102 | ```lua
103 | _M.set = function(self, policy)
104 | local database = self.database
105 | local policyLib = self.policyLib
106 |
107 | database:init_pipeline()
108 | for _, v in pairs(policy) do
109 | database:hset(policyLib, v[k_city], v[k_upstream])
110 | end
111 | local ok, err = database:commit_pipeline()
112 | if not ok then
113 | error{ERRORINFO.REDIS_ERROR, err}
114 | end
115 | end
116 | ```
117 | arg_city的分流策略在redis中采用hash结构存储。
118 |
119 | ####4.策略读取 get方法
120 | 从数据库中读取用户策略的数据
121 | ```lua
122 | _M.get = function(self)
123 | local database = self.database
124 | local policyLib = self.policyLib
125 |
126 | local data, err = database:hgetall(policyLib)
127 | if not data then
128 | error{ERRORINFO.REDIS_ERROR, err}
129 | end
130 |
131 | return data
132 | end
133 | ```
134 | 目前只是将策略数据从redis中读出,然后以json形式发送给client,至于如何解析json字符串为策略数据,可以在系统的/admin/policy/get接口实现,也可以在client中实现。目前ABTestingGateway没有实现。
135 |
136 |
137 | ####5.获取用户请求对应的upstream
138 | 从数据库中读取用户策略的数据
139 | ```lua
140 | _M.getUpstream = function(self, city)
141 | local database = self.database
142 | local policyLib = self.policyLib
143 |
144 | local upstream, err = database:hget(policyLib , city)
145 | if not upstream then error{ERRORINFO.REDIS_ERROR, err} end
146 |
147 | if upstream == ngx.null then
148 | return nil
149 | else
150 | return upstream
151 | end
152 | end
153 | ```
154 | 分流模块获取upstream的方法,策略key为policylib,用户请求特征为city。getUpstream得到结果后返回,系统分流接口将请求转发至目标upstream。
155 |
156 | ###分流方式对应的 用户特征提取模块
157 | 在getUpstream方法中,分流模块根据用户请求中的city来计算upstream,这个city相当于用户请求特征。每种分流方式需要指定用户特征提取模块,由它提取用户请求的特征。
158 |
159 | 分流策略中的divtype将用来指定用户特征提取模块。ABTestingGateway的所有用户特征提取模块都在**lib/abtesting/userinfo/**文件夹,其下的每个lua文件是一个分流模块。
160 |
161 | 在**lib/abtesting/utils/init.lua**中
162 | ```lua
163 | _M.divtypes = {
164 | ["iprange"] = 'ipParser',
165 | ["uidrange"] = 'uidParser',
166 | ["uidsuffix"] = 'uidParser',
167 | ["uidappoint"] = 'uidParser',
168 | ["arg_city"] = 'cityParser'
169 | }
170 | ```
171 | 每种divtype会有对应的提取模块,因此divtype为arg_city的分流方式对应的用户信息提取模块就是**lib/abtesting/userinfo/cityParser.lua**。
172 | ```lua
173 | local _M = {
174 | _VERSION = '0.01'
175 | }
176 |
177 | _M.get = function()
178 | local u = ngx.var.arg_city
179 | ngx.log(ngx.ERR, u)
180 | return u
181 | end
182 | return _M
183 | ```
184 | 以上就是向系统添加新的分流方式的具体步骤。最新的代码已提交至repo中。
185 |
--------------------------------------------------------------------------------
/lib/resty/lock.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (C) Yichun Zhang (agentzh)
2 |
3 |
4 | local ffi = require "ffi"
5 | local ffi_new = ffi.new
6 | local shared = ngx.shared
7 | local sleep = ngx.sleep
8 | local shdict_mt
9 | local debug = ngx.config.debug
10 | local setmetatable = setmetatable
11 | local getmetatable = getmetatable
12 | local tonumber = tonumber
13 |
14 |
15 | local _M = { _VERSION = '0.04' }
16 | local mt = { __index = _M }
17 |
18 |
19 | local FREE_LIST_REF = 0
20 |
21 | -- FIXME: we don't need this when we have __gc metamethod support on Lua
22 | -- tables.
23 | local memo = {}
24 | if debug then _M.memo = memo end
25 |
26 |
27 | local function ref_obj(key)
28 | if key == nil then
29 | return -1
30 | end
31 | local ref = memo[FREE_LIST_REF]
32 | if ref and ref ~= 0 then
33 | memo[FREE_LIST_REF] = memo[ref]
34 |
35 | else
36 | ref = #memo + 1
37 | end
38 | memo[ref] = key
39 |
40 | -- print("ref key_id returned ", ref)
41 | return ref
42 | end
43 | if debug then _M.ref_obj = ref_obj end
44 |
45 |
46 | local function unref_obj(ref)
47 | if ref >= 0 then
48 | memo[ref] = memo[FREE_LIST_REF]
49 | memo[FREE_LIST_REF] = ref
50 | end
51 | end
52 | if debug then _M.unref_obj = unref_obj end
53 |
54 |
55 | local function gc_lock(cdata)
56 | local dict_id = tonumber(cdata.dict_id)
57 | local key_id = tonumber(cdata.key_id)
58 |
59 | -- print("key_id: ", key_id, ", key: ", memo[key_id], "dict: ",
60 | -- type(memo[cdata.dict_id]))
61 | if key_id > 0 then
62 | local key = memo[key_id]
63 | unref_obj(key_id)
64 | local dict = memo[dict_id]
65 | -- print("dict.delete type: ", type(dict.delete))
66 | local ok, err = dict:delete(key)
67 | if not ok then
68 | ngx.log(ngx.ERR, 'failed to delete key "', key, '": ', err)
69 | end
70 | cdata.key_id = 0
71 | end
72 |
73 | unref_obj(dict_id)
74 | end
75 |
76 |
77 | local ctype = ffi.metatype("struct { int key_id; int dict_id; }",
78 | { __gc = gc_lock })
79 |
80 |
81 | function _M.new(_, dict_name, opts)
82 | local dict = shared[dict_name]
83 | if not dict then
84 | return nil, "dictionary not found"
85 | end
86 | local cdata = ffi_new(ctype)
87 | cdata.key_id = 0
88 | cdata.dict_id = ref_obj(dict)
89 |
90 | local timeout, exptime, step, ratio, max_step
91 | if opts then
92 | timeout = opts.timeout
93 | exptime = opts.exptime
94 | step = opts.step
95 | ratio = opts.ratio
96 | max_step = opts.max_step
97 | end
98 |
99 | if not exptime then
100 | exptime = 30
101 | end
102 |
103 | if timeout and timeout > exptime then
104 | timeout = exptime
105 | end
106 |
107 | local self = {
108 | cdata = cdata,
109 | dict = dict,
110 | timeout = timeout or 5,
111 | exptime = exptime,
112 | step = step or 0.001,
113 | ratio = ratio or 2,
114 | max_step = max_step or 0.5,
115 | }
116 | return setmetatable(self, mt)
117 | end
118 |
119 |
120 | function _M.lock(self, key)
121 | if not key then
122 | return nil, "nil key"
123 | end
124 |
125 | local dict = self.dict
126 | local cdata = self.cdata
127 | if cdata.key_id > 0 then
128 | return nil, "locked"
129 | end
130 | local exptime = self.exptime
131 | local ok, err = dict:add(key, true, exptime)
132 | if ok then
133 | cdata.key_id = ref_obj(key)
134 | if not shdict_mt then
135 | shdict_mt = getmetatable(dict)
136 | end
137 | return 0
138 | end
139 | if err ~= "exists" then
140 | return nil, err
141 | end
142 | -- lock held by others
143 | local step = self.step
144 | local ratio = self.ratio
145 | local timeout = self.timeout
146 | local max_step = self.max_step
147 | local elapsed = 0
148 | while timeout > 0 do
149 | if step > timeout then
150 | step = timeout
151 | end
152 |
153 | sleep(step)
154 | elapsed = elapsed + step
155 | timeout = timeout - step
156 |
157 | local ok, err = dict:add(key, true, exptime)
158 | if ok then
159 | cdata.key_id = ref_obj(key)
160 | if not shdict_mt then
161 | shdict_mt = getmetatable(dict)
162 | end
163 | return elapsed
164 | end
165 |
166 | if err ~= "exists" then
167 | return nil, err
168 | end
169 |
170 | if timeout <= 0 then
171 | break
172 | end
173 |
174 | step = step * ratio
175 | if step <= 0 then
176 | step = 0.001
177 | end
178 | if step > max_step then
179 | step = max_step
180 | end
181 | end
182 |
183 | return nil, "timeout"
184 | end
185 |
186 |
187 | function _M.unlock(self)
188 | local dict = self.dict
189 | local cdata = self.cdata
190 | local key_id = tonumber(cdata.key_id)
191 | if key_id <= 0 then
192 | return nil, "unlocked"
193 | end
194 |
195 | local key = memo[key_id]
196 | unref_obj(key_id)
197 |
198 | local ok, err = dict:delete(key)
199 | if not ok then
200 | return nil, err
201 | end
202 | cdata.key_id = 0
203 |
204 | return 1
205 | end
206 |
207 | return _M
208 |
209 |
--------------------------------------------------------------------------------
/doc/ABTestingGateway之系统配置参数说明.md:
--------------------------------------------------------------------------------
1 | 目前灰度系统的配置参数主要包括:
2 |
3 | 1. 管理和分流功能的相关配置
4 | 2. redis数据库读写配置
5 |
6 | 在repo中的**utils/conf/nginx.conf**文件是一个灰度系统的最小配置。
7 | ```python
8 | worker_processes auto;
9 |
10 | pid logs/nginx-uid.pid;
11 | error_log logs/error.log ;
12 |
13 | events {
14 | worker_connections 32768;
15 | accept_mutex off;
16 | multi_accept on;
17 | }
18 |
19 | http {
20 | include mime.types;
21 |
22 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
23 | '$status $body_bytes_sent "$http_referer" '
24 | '"$http_user_agent" "$http_x_forwarded_for"'
25 | ' $request_time $upstream_response_time';
26 | access_log logs/access.log main;
27 |
28 | sendfile on;
29 |
30 | # keepalive_timeout 0;
31 | # keepalive_requests 0;
32 |
33 | upstream stable{
34 | keepalive 1000;
35 | server localhost:8040;
36 | }
37 |
38 | upstream beta1 {
39 | keepalive 1000;
40 | server localhost:8020;
41 | }
42 |
43 | upstream beta2 {
44 | keepalive 1000;
45 | server localhost:8021;
46 | }
47 |
48 | upstream beta3 {
49 | keepalive 1000;
50 | server 127.1.0.1:8022;
51 |
52 | # check interval=3000 rise=2 fall=5 timeout=1000 type=http;
53 | # check_http_send "HEAD / HTTP/1.0\r\n\r\n";
54 | # check_http_expect_alive http_2xx http_3xx;
55 | }
56 |
57 | upstream beta4 {
58 | keepalive 1000;
59 | server localhost:8023;
60 | }
61 |
62 | lua_code_cache off;
63 | lua_package_path "../lib/?.lua;;";
64 | lua_shared_dict sysConfig 1m;
65 | lua_shared_dict kv_upstream 10m;
66 | lua_shared_dict rt_locks 100k;
67 | lua_shared_dict up_locks 100k;
68 |
69 | lua_need_request_body on;
70 |
71 | server {
72 | server_name localhost;
73 | listen 8030 backlog=16384;
74 |
75 | set $redis_host '127.0.0.1';
76 | set $redis_port '6379';
77 | set $redis_uds '/tmp/redis.sock';
78 | set $redis_connect_timeout 10000;
79 | set $redis_dbid 0;
80 |
81 | set $redis_pool_size 1000;
82 | set $redis_keepalive_timeout 90000; #(keepalive_time, in ms)
83 |
84 | set $runtime_prefix 'ab:test:runtimeInfo';
85 | set $policy_prefix 'ab:test:policies';
86 |
87 | set $default_backend 'stable';
88 | set $shdict_expire 60;
89 |
90 | set $rt_cache_lock rt_locks; #set name of cache locks, should be same as lua_shared_dict
91 | set $up_cache_lock up_locks;
92 | set $lock_expire 0.001 ; #wait for cache_lock 0.001 seconds
93 |
94 | location / {
95 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
96 | proxy_set_header Host $http_host;
97 | proxy_set_header X-Real-IP $remote_addr;
98 | proxy_set_header Connection "";
99 | proxy_http_version 1.1;
100 |
101 | set $backend $default_backend;
102 | rewrite_by_lua_file '../diversion/diversion.lua';
103 |
104 | proxy_pass http://$backend;
105 | }
106 | location = /admin/policy/set {
107 | content_by_lua_file '../admin/policy/set.lua';
108 | }
109 | location = /admin/policy/get {
110 | content_by_lua_file '../admin/policy/get.lua';
111 | }
112 | location = /admin/policy/del {
113 | content_by_lua_file '../admin/policy/del.lua';
114 | }
115 | location = /admin/policy/check {
116 | content_by_lua_file '../admin/policy/check.lua';
117 | }
118 |
119 | location = /admin/runtime/set {
120 | content_by_lua_file '../admin/runtime/set.lua';
121 | }
122 | location = /admin/runtime/get {
123 | content_by_lua_file '../admin/runtime/get.lua';
124 | }
125 | location = /admin/runtime/del {
126 | content_by_lua_file '../admin/runtime/del.lua';
127 | }
128 |
129 | location /nginx_status {
130 | stub_status on;
131 | access_log off;
132 | allow 127.0.0.1;
133 | deny all;
134 | }
135 |
136 | location /status {
137 | check_status;
138 |
139 | access_log off;
140 | allow 127.0.0.1;
141 | deny all;
142 | }
143 | }
144 | }
145 | ```
146 |
147 | ####主机名 server_name 配置
148 |
149 | 主机名十分重要,一来是7层服务器的主机名,二来是灰度系统的运行时策略库前缀。
150 | server_name可以配置多个,灰度系统将取用第一个作为运行时信息前缀。
151 |
152 | ```bash
153 | server_name localhost
154 | ```
155 |
156 | ####灰度系统的 redis 读写相关配置
157 | ```bash
158 | set $redis_host '127.0.0.1'; --本机redis的IP
159 | set $redis_port '6379'; --本机redis的port
160 | set $redis_uds '/tmp/redis.sock'; --本机redis的uds设置,优先使用uds
161 | set $redis_connect_timeout 10000;
162 | set $redis_dbid 0;
163 |
164 | set $redis_pool_size 1000; --lua-resty-redis的连接池大小
165 | set $redis_keepalive_timeout 90000; --(连接池keepalive_time, in ms)
166 | ```
167 | ####灰度系统的 策略和运行时信息 相关
168 | 灰度系统的 运行时信息 和 策略库 的前缀,在redis数据库中的key名前缀。
169 |
170 | ```bash
171 | set $runtime_prefix 'ab:test:runtimeInfo';
172 | set $policy_prefix 'ab:test:policies';
173 | ```
174 | ####灰度系统的 默认upstream配置
175 | ```bash
176 | set $default_backend 'http://stable';
177 | set $backend $default_backend;
178 | rewrite_by_lua_file '../diversion/diversion.lua';
179 |
180 | proxy_pass http://$backend;
181 | ```
182 | ####灰度系统的 系统缓存 配置
183 | ```bash
184 | lua_shared_dict sysConfig 1m; --运行时信息的kv缓存
185 | lua_shared_dict rt_locks 100k; --缓存锁
186 |
187 | lua_shared_dict kv_upstream 10m; --用户请求与目标upstream的kv缓存
188 | lua_shared_dict up_locks 100k; --缓存锁
189 |
190 | set $shdict_expire 60; --缓存失效时间
191 | ```
192 |
193 | ####灰度系统的 系统缓存锁 配置
194 |
195 | 缓存锁配置。缓存锁的的本质是一个shared-dict,就是在**系统缓存 配置**中的rt_locks和up_locks。
196 | 在这里改名,是想给系统提供一个不变的缓存锁名字。
197 | ```bash
198 | set $rt_cache_lock rt_locks;
199 | set $up_cache_lock up_locks;
200 | ```
201 | 缓存锁死锁时间
202 |
203 | ```bash
204 | set $cache_expire 0.001 ;
205 | ```
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/var.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 5);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_shared_dict dogs 1m;
18 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | check_accum_error_log();
37 | run_tests();
38 |
39 | __DATA__
40 |
41 | === TEST 1: get normal var
42 | --- http_config eval: $::HttpConfig
43 | --- config
44 | location = /t {
45 | set $foo hello;
46 | content_by_lua '
47 | local ffi = require "ffi"
48 | local val
49 | for i = 1, 100 do
50 | val = ngx.var.foo
51 | end
52 | ngx.say("value: ", val)
53 | ';
54 | }
55 | --- request
56 | GET /t
57 | --- response_body
58 | value: hello
59 | --- error_log eval
60 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
61 | --- no_error_log
62 | [error]
63 | -- NYI: (?!return to lower frame)
64 |
65 |
66 |
67 | === TEST 2: get normal var (case)
68 | --- http_config eval: $::HttpConfig
69 | --- config
70 | location = /t {
71 | set $foo hello;
72 | content_by_lua '
73 | local ffi = require "ffi"
74 | local val
75 | for i = 1, 100 do
76 | val = ngx.var.FOO
77 | end
78 | ngx.say("value: ", val)
79 | ';
80 | }
81 | --- request
82 | GET /t
83 | --- response_body
84 | value: hello
85 | --- error_log eval
86 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
87 | --- no_error_log
88 | [error]
89 | -- NYI: (?!return to lower frame)
90 |
91 |
92 |
93 | === TEST 3: get capturing var (bad)
94 | --- http_config eval: $::HttpConfig
95 | --- config
96 | location = /t {
97 | set $foo hello;
98 | content_by_lua '
99 | local ffi = require "ffi"
100 | local val
101 | for i = 1, 100 do
102 | val = ngx.var[0]
103 | end
104 | ngx.say("value: ", val)
105 | ';
106 | }
107 | --- request
108 | GET /t
109 | --- response_body
110 | value: nil
111 | --- error_log eval
112 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
113 | --- no_error_log
114 | [error]
115 | -- NYI:
116 |
117 |
118 |
119 | === TEST 4: get capturing var
120 | --- http_config eval: $::HttpConfig
121 | --- config
122 | location ~ '^(/t)' {
123 | set $foo hello;
124 | content_by_lua '
125 | local ffi = require "ffi"
126 | local val
127 | for i = 1, 100 do
128 | val = ngx.var[1]
129 | end
130 | ngx.say("value: ", val)
131 | ';
132 | }
133 | --- request
134 | GET /t
135 | --- response_body
136 | value: /t
137 | --- error_log eval
138 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
139 | --- no_error_log
140 | [error]
141 | -- NYI: (?!return to lower frame)
142 |
143 |
144 |
145 | === TEST 5: set normal var (string value)
146 | --- http_config eval: $::HttpConfig
147 | --- config
148 | location = /t {
149 | set $foo hello;
150 | content_by_lua '
151 | local ffi = require "ffi"
152 | local val = "hello"
153 | for i = 1, 100 do
154 | ngx.var.foo = val
155 | end
156 | ngx.say("value: ", val)
157 | ';
158 | }
159 | --- request
160 | GET /t
161 | --- response_body
162 | value: hello
163 | --- error_log eval
164 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
165 | --- no_error_log
166 | [error]
167 | -- NYI:
168 |
169 |
170 |
171 | === TEST 6: set normal var (nil value)
172 | --- http_config eval: $::HttpConfig
173 | --- config
174 | location = /t {
175 | set $foo hello;
176 | content_by_lua '
177 | local ffi = require "ffi"
178 | for i = 1, 100 do
179 | ngx.var.foo = nil
180 | end
181 | ngx.say("value: ", ngx.var.foo)
182 | ';
183 | }
184 | --- request
185 | GET /t
186 | --- response_body
187 | value: nil
188 | --- error_log eval
189 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
190 | --- no_error_log
191 | [error]
192 | -- NYI:
193 |
194 |
195 |
196 | === TEST 7: set normal var (number value)
197 | --- http_config eval: $::HttpConfig
198 | --- config
199 | location = /t {
200 | set $foo hello;
201 | content_by_lua '
202 | local ffi = require "ffi"
203 | for i = 1, 100 do
204 | ngx.var.foo = i
205 | end
206 | ngx.say("value: ", ngx.var.foo)
207 | ';
208 | }
209 | --- request
210 | GET /t
211 | --- response_body
212 | value: 100
213 | --- error_log eval
214 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
215 | --- no_error_log
216 | [error]
217 | -- NYI:
218 |
219 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/encode-base64.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 5);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
18 | init_by_lua '
19 | local verbose = false
20 | if verbose then
21 | local dump = require "jit.dump"
22 | dump.on(nil, "$Test::Nginx::Util::ErrLogFile")
23 | else
24 | local v = require "jit.v"
25 | v.on("$Test::Nginx::Util::ErrLogFile")
26 | end
27 |
28 | require "resty.core"
29 | -- jit.off()
30 | ';
31 | _EOC_
32 |
33 | #no_diff();
34 | #no_long_string();
35 | check_accum_error_log();
36 | run_tests();
37 |
38 | __DATA__
39 |
40 | === TEST 1: set base64 (string)
41 | --- http_config eval: $::HttpConfig
42 | --- config
43 | location = /base64 {
44 | content_by_lua '
45 | local s
46 | for i = 1, 100 do
47 | s = ngx.encode_base64("hello")
48 | end
49 | ngx.say(s)
50 | ';
51 | }
52 | --- request
53 | GET /base64
54 | --- response_body
55 | aGVsbG8=
56 | --- error_log eval
57 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
58 | --- no_error_log
59 | [error]
60 | -- NYI:
61 |
62 |
63 |
64 | === TEST 2: set base64 (nil)
65 | --- http_config eval: $::HttpConfig
66 | --- config
67 | location = /base64 {
68 | content_by_lua '
69 | local s
70 | for i = 1, 100 do
71 | s = ngx.encode_base64(nil)
72 | end
73 | ngx.say(s)
74 | ';
75 | }
76 | --- request
77 | GET /base64
78 | --- response_body eval: "\n"
79 | --- error_log eval
80 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
81 | --- no_error_log
82 | [error]
83 | -- NYI:
84 |
85 |
86 |
87 | === TEST 3: set base64 (number)
88 | --- http_config eval: $::HttpConfig
89 | --- config
90 | location = /base64 {
91 | content_by_lua '
92 | local s
93 | for i = 1, 100 do
94 | s = ngx.encode_base64(3.14)
95 | end
96 | ngx.say(s)
97 | ';
98 | }
99 | --- request
100 | GET /base64
101 | --- response_body
102 | My4xNA==
103 | --- error_log eval
104 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
105 | --- no_error_log
106 | [error]
107 | -- NYI:
108 |
109 |
110 |
111 | === TEST 4: set base64 (boolean)
112 | --- http_config eval: $::HttpConfig
113 | --- config
114 | location = /base64 {
115 | content_by_lua '
116 | local s
117 | for i = 1, 100 do
118 | s = ngx.encode_base64(true)
119 | end
120 | ngx.say(s)
121 | ';
122 | }
123 | --- request
124 | GET /base64
125 | --- response_body
126 | dHJ1ZQ==
127 | --- error_log eval
128 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
129 | --- no_error_log
130 | [error]
131 | -- NYI:
132 |
133 |
134 |
135 | === TEST 5: set base64 (buf is a little larger than 4096)
136 | --- http_config eval: $::HttpConfig
137 | --- config
138 | location = /base64 {
139 | content_by_lua '
140 | local s
141 | for i = 1, 100 do
142 | s = ngx.encode_base64(string.rep("a", 3073))
143 | end
144 | ngx.say(string.len(s))
145 | ';
146 | }
147 | --- request
148 | GET /base64
149 | --- response_body
150 | 4100
151 | --- error_log eval
152 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
153 | --- no_error_log
154 | [error]
155 | -- NYI:
156 |
157 |
158 |
159 | === TEST 6: set base64 (buf is just 4096)
160 | --- http_config eval: $::HttpConfig
161 | --- config
162 | location = /base64 {
163 | content_by_lua '
164 | local s
165 | for i = 1, 100 do
166 | s = ngx.encode_base64(string.rep("a", 3071))
167 | end
168 | ngx.say(string.len(s))
169 | ';
170 | }
171 | --- request
172 | GET /base64
173 | --- response_body
174 | 4096
175 | --- error_log eval
176 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
177 | --- no_error_log
178 | [error]
179 | -- NYI:
180 |
181 |
182 |
183 | === TEST 7: set base64 (number) without padding (explicitly specified)
184 | --- http_config eval: $::HttpConfig
185 | --- config
186 | location = /base64 {
187 | content_by_lua '
188 | local s
189 | for i = 1, 200 do
190 | s = ngx.encode_base64(3.14, true)
191 | end
192 | ngx.say(s)
193 | ';
194 | }
195 | --- request
196 | GET /base64
197 | --- response_body
198 | My4xNA
199 | --- error_log eval
200 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
201 | --- no_error_log
202 | [error]
203 | -- NYI:
204 |
205 |
206 |
207 | === TEST 8: set base64 (number) with padding (explictly specified)
208 | --- http_config eval: $::HttpConfig
209 | --- config
210 | location = /base64 {
211 | content_by_lua '
212 | local s
213 | for i = 1, 200 do
214 | s = ngx.encode_base64(3.14, false)
215 | end
216 | ngx.say(s)
217 | ';
218 | }
219 | --- request
220 | GET /base64
221 | --- response_body
222 | My4xNA==
223 | --- error_log eval
224 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
225 | --- no_error_log
226 | [error]
227 | -- NYI:
228 |
229 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/t/response.t:
--------------------------------------------------------------------------------
1 | # vim:set ft= ts=4 sw=4 et fdm=marker:
2 | use lib 'lib';
3 | use Test::Nginx::Socket::Lua;
4 | use Cwd qw(cwd);
5 |
6 | #worker_connections(1014);
7 | #master_process_enabled(1);
8 | #log_level('warn');
9 |
10 | repeat_each(2);
11 |
12 | plan tests => repeat_each() * (blocks() * 5 + 5);
13 |
14 | my $pwd = cwd();
15 |
16 | our $HttpConfig = <<_EOC_;
17 | lua_shared_dict dogs 1m;
18 | lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
19 | init_by_lua '
20 | local verbose = false
21 | if verbose then
22 | local dump = require "jit.dump"
23 | dump.on("b", "$Test::Nginx::Util::ErrLogFile")
24 | else
25 | local v = require "jit.v"
26 | v.on("$Test::Nginx::Util::ErrLogFile")
27 | end
28 |
29 | require "resty.core"
30 | -- jit.off()
31 | ';
32 | _EOC_
33 |
34 | #no_diff();
35 | #no_long_string();
36 | check_accum_error_log();
37 | run_tests();
38 |
39 | __DATA__
40 |
41 | === TEST 1: write to ngx.header.HEADER (single value)
42 | --- http_config eval: $::HttpConfig
43 | --- config
44 | location = /t {
45 | set $foo hello;
46 | content_by_lua '
47 | for i = 1, 100 do
48 | ngx.header["Foo"] = i
49 | end
50 | ngx.say("Foo: ", ngx.header["Foo"])
51 | ';
52 | }
53 | --- request
54 | GET /t
55 | --- response_body
56 | Foo: 100
57 |
58 | --- error_log eval
59 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):2 loop\]/
60 | --- no_error_log
61 | [error]
62 | -- NYI:
63 |
64 |
65 |
66 | === TEST 2: write to ngx.header.HEADER (nil)
67 | --- http_config eval: $::HttpConfig
68 | --- config
69 | location = /t {
70 | set $foo hello;
71 | content_by_lua '
72 | for i = 1, 200 do
73 | ngx.header["Foo"] = i
74 | ngx.header["Foo"] = nil
75 | end
76 | ngx.say("Foo: ", ngx.header["Foo"])
77 | ';
78 | }
79 | --- request
80 | GET /t
81 | --- response_body
82 | Foo: nil
83 |
84 | --- error_log eval
85 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):2 loop\]/
86 | --- wait: 0.2
87 | --- no_error_log
88 | [error]
89 | -- NYI:
90 |
91 |
92 |
93 | === TEST 3: write to ngx.header.HEADER (multi-value)
94 | --- http_config eval: $::HttpConfig
95 | --- config
96 | location = /t {
97 | set $foo hello;
98 | content_by_lua '
99 | for i = 1, 200 do
100 | ngx.header["Foo"] = {i, i + 1}
101 | end
102 | local v = ngx.header["Foo"]
103 | if type(v) == "table" then
104 | ngx.say("Foo: ", table.concat(v, ", "))
105 | else
106 | ngx.say("Foo: ", v)
107 | end
108 | ';
109 | }
110 | --- request
111 | GET /t
112 | --- response_body
113 | Foo: 200, 201
114 |
115 | --- error_log eval
116 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):2 loop\]/
117 | --- no_error_log
118 | [error]
119 | -- NYI:
120 |
121 |
122 |
123 | === TEST 4: read from ngx.header.HEADER (single value)
124 | --- http_config eval: $::HttpConfig
125 | --- config
126 | location = /t {
127 | set $foo hello;
128 | content_by_lua '
129 | local v
130 | for i = 1, 100 do
131 | ngx.header["Foo"] = i
132 | v = ngx.header["Foo"]
133 | end
134 | ngx.say("Foo: ", v)
135 | ';
136 | }
137 | --- request
138 | GET /t
139 | --- response_body
140 | Foo: 100
141 |
142 | --- error_log eval
143 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
144 | --- no_error_log eval
145 | [
146 | "[error]",
147 | qr/ -- NYI: (?!return to lower frame)/,
148 | "stitch",
149 | ]
150 |
151 |
152 |
153 | === TEST 5: read from ngx.header.HEADER (not found)
154 | --- http_config eval: $::HttpConfig
155 | --- config
156 | location = /t {
157 | set $foo hello;
158 | content_by_lua '
159 | local v
160 | for i = 1, 100 do
161 | v = ngx.header["Foo"]
162 | end
163 | ngx.say("Foo: ", v)
164 | ';
165 | }
166 | --- request
167 | GET /t
168 | --- response_body
169 | Foo: nil
170 |
171 | --- error_log eval
172 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
173 | --- no_error_log
174 | [error]
175 | -- NYI:
176 | stitch
177 |
178 |
179 |
180 | === TEST 6: read from ngx.header.HEADER (multi-value)
181 | --- http_config eval: $::HttpConfig
182 | --- config
183 | location = /t {
184 | set $foo hello;
185 | content_by_lua '
186 | ngx.header["Foo"] = {"foo", "bar"}
187 | local v
188 | for i = 1, 100 do
189 | v = ngx.header["Foo"]
190 | end
191 | ngx.say("Foo: ", table.concat(v, ", "))
192 | ';
193 | }
194 | --- request
195 | GET /t
196 | --- response_body
197 | Foo: foo, bar
198 |
199 | --- error_log eval
200 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
201 | --- no_error_log
202 | [error]
203 | -- NYI:
204 | stitch
205 |
206 |
207 |
208 | === TEST 7: set multi values to cache-control and override it with multiple values
209 | --- http_config eval: $::HttpConfig
210 | --- config
211 | location /lua {
212 | content_by_lua '
213 | ngx.header.cache_control = { "private", "no-store" }
214 | ngx.header.cache_control = { "no-cache", "blah", "foo" }
215 | local v
216 | for i = 1, 400 do
217 | v = ngx.header.cache_control
218 | end
219 | ngx.say("Cache-Control: ", table.concat(v, ", "))
220 | ';
221 | }
222 | --- request
223 | GET /lua
224 | --- response_headers
225 | Cache-Control: no-cache, blah, foo
226 | --- response_body_like chop
227 | ^Cache-Control: no-cache[;,] blah[;,] foo$
228 | --- error_log eval
229 | qr/\[TRACE \d+ content_by_lua\(nginx\.conf:\d+\):5 (?:loop|-> \d+)\]/
230 | --- no_error_log
231 | [error]
232 | -- NYI:
233 | stitch
234 |
235 |
--------------------------------------------------------------------------------
/lib/lua-resty-core/lib/ngx/ssl.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_str = ffi.string
10 | local getfenv = getfenv
11 | local error = error
12 | local tonumber = tonumber
13 | local errmsg = base.get_errmsg_ptr()
14 | local get_string_buf = base.get_string_buf
15 | local get_size_ptr = base.get_size_ptr
16 | local FFI_DECLINED = base.FFI_DECLINED
17 | local FFI_OK = base.FFI_OK
18 |
19 |
20 | ffi.cdef[[
21 |
22 | struct ngx_ssl_conn_s;
23 | typedef struct ngx_ssl_conn_s ngx_ssl_conn_t;
24 |
25 | int ngx_http_lua_ffi_ssl_set_der_certificate(ngx_http_request_t *r,
26 | const char *data, size_t len, char **err);
27 |
28 | int ngx_http_lua_ffi_ssl_clear_certs(ngx_http_request_t *r, char **err);
29 |
30 | int ngx_http_lua_ffi_ssl_set_der_private_key(ngx_http_request_t *r,
31 | const char *data, size_t len, char **err);
32 |
33 | int ngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr,
34 | size_t *addrlen, int *addrtype, char **err);
35 |
36 | int ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name,
37 | size_t *namelen, char **err);
38 |
39 | int ngx_http_lua_ffi_cert_pem_to_der(const unsigned char *pem, size_t pem_len,
40 | unsigned char *der, char **err);
41 |
42 | int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,
43 | size_t pem_len, unsigned char *der, char **err);
44 |
45 | int ngx_http_lua_ffi_ssl_get_tls1_version(ngx_http_request_t *r, char **err);
46 | ]]
47 |
48 |
49 | local _M = { version = base.version }
50 |
51 |
52 | local charpp = ffi.new("char*[1]")
53 | local intp = ffi.new("int[1]")
54 |
55 |
56 | function _M.clear_certs()
57 | local r = getfenv(0).__ngx_req
58 | if not r then
59 | return error("no request found")
60 | end
61 |
62 | local rc = C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)
63 | if rc == FFI_OK then
64 | return true
65 | end
66 |
67 | return nil, ffi_str(errmsg[0])
68 | end
69 |
70 |
71 | function _M.set_der_cert(data)
72 | local r = getfenv(0).__ngx_req
73 | if not r then
74 | return error("no request found")
75 | end
76 |
77 | local rc = C.ngx_http_lua_ffi_ssl_set_der_certificate(r, data, #data,
78 | errmsg)
79 | if rc == FFI_OK then
80 | return true
81 | end
82 |
83 | return nil, ffi_str(errmsg[0])
84 | end
85 |
86 |
87 | function _M.set_der_priv_key(data)
88 | local r = getfenv(0).__ngx_req
89 | if not r then
90 | return error("no request found")
91 | end
92 |
93 | local rc = C.ngx_http_lua_ffi_ssl_set_der_private_key(r, data, #data,
94 | errmsg)
95 | if rc == FFI_OK then
96 | return true
97 | end
98 |
99 | return nil, ffi_str(errmsg[0])
100 | end
101 |
102 |
103 | local addr_types = {
104 | [0] = "unix",
105 | [1] = "inet",
106 | [2] = "inet6",
107 | }
108 |
109 |
110 | function _M.raw_server_addr()
111 | local r = getfenv(0).__ngx_req
112 | if not r then
113 | return error("no request found")
114 | end
115 |
116 | local sizep = get_size_ptr()
117 |
118 | local rc = C.ngx_http_lua_ffi_ssl_raw_server_addr(r, charpp, sizep,
119 | intp, errmsg)
120 | if rc == FFI_OK then
121 | local typ = addr_types[intp[0]]
122 | if not typ then
123 | return nil, nil, "unknown address type: " .. intp[0]
124 | end
125 | return ffi_str(charpp[0], sizep[0]), typ
126 | end
127 |
128 | return nil, nil, ffi_str(errmsg[0])
129 | end
130 |
131 |
132 | function _M.server_name()
133 | local r = getfenv(0).__ngx_req
134 | if not r then
135 | return error("no request found")
136 | end
137 |
138 | local sizep = get_size_ptr()
139 |
140 | local rc = C.ngx_http_lua_ffi_ssl_server_name(r, charpp, sizep, errmsg)
141 | if rc == FFI_OK then
142 | return ffi_str(charpp[0], sizep[0])
143 | end
144 |
145 | if rc == FFI_DECLINED then
146 | return nil
147 | end
148 |
149 | return nil, ffi_str(errmsg[0])
150 | end
151 |
152 |
153 | function _M.cert_pem_to_der(pem)
154 | local r = getfenv(0).__ngx_req
155 | if not r then
156 | return error("no request found")
157 | end
158 |
159 | local outbuf = get_string_buf(#pem)
160 |
161 | local sz = C.ngx_http_lua_ffi_cert_pem_to_der(pem, #pem, outbuf, errmsg)
162 | if sz > 0 then
163 | return ffi_str(outbuf, sz)
164 | end
165 |
166 | return nil, ffi_str(errmsg[0])
167 | end
168 |
169 |
170 | function _M.priv_key_pem_to_der(pem)
171 | local r = getfenv(0).__ngx_req
172 | if not r then
173 | return error("no request found")
174 | end
175 |
176 | local outbuf = get_string_buf(#pem)
177 |
178 | local sz = C.ngx_http_lua_ffi_priv_key_pem_to_der(pem, #pem, outbuf, errmsg)
179 | if sz > 0 then
180 | return ffi_str(outbuf, sz)
181 | end
182 |
183 | return nil, ffi_str(errmsg[0])
184 | end
185 |
186 |
187 | local function get_tls1_version()
188 |
189 | local r = getfenv(0).__ngx_req
190 | if not r then
191 | return error("no request found")
192 | end
193 |
194 | local ver = C.ngx_http_lua_ffi_ssl_get_tls1_version(r, errmsg)
195 |
196 | ver = tonumber(ver)
197 |
198 | if ver >= 0 then
199 | return ver
200 | end
201 |
202 | -- rc == FFI_ERROR
203 |
204 | return nil, ffi_str(errmsg[0])
205 | end
206 | _M.get_tls1_version = get_tls1_version
207 |
208 |
209 | do
210 | _M.SSL3_VERSION = 0x0300
211 | _M.TLS1_VERSION = 0x0301
212 | _M.TLS1_1_VERSION = 0x0302
213 | _M.TLS1_2_VERSION = 0x0303
214 |
215 | local map = {
216 | [_M.SSL3_VERSION] = "SSLv3",
217 | [_M.TLS1_VERSION] = "TLSv1",
218 | [_M.TLS1_1_VERSION] = "TLSv1.1",
219 | [_M.TLS1_2_VERSION] = "TLSv1.2",
220 | }
221 |
222 | function _M.get_tls1_version_str()
223 | local ver, err = get_tls1_version()
224 | if not ver then
225 | return nil, err
226 | end
227 | return map[ver]
228 | end
229 | end
230 |
231 |
232 | return _M
233 |
--------------------------------------------------------------------------------