├── 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 | --------------------------------------------------------------------------------