Squashed 'src/deps/src/lua-resty-core/' content from commit 31fae862a

git-subtree-dir: src/deps/src/lua-resty-core
git-subtree-split: 31fae862a1ed64033591f991fadb0dd80358ba0b
This commit is contained in:
Théophile Diot 2023-06-30 15:38:28 -04:00
commit fd02afef8e
176 changed files with 53432 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.t linguist-language=Text

27
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,27 @@
This place is for bug reports and development discussions only. For general questions and
discussions, please join the openresty-en mailing list instead: https://groups.google.com/group/openresty-en.
If you want to use Chinese, please join the openresty (Chinese) mailing list instead: https://groups.google.com/group/openresty.
Do not use Chinese in this place.
Before you open a new issue, please search the internet and make sure it is not duplicate.
Ensure you have provided the following details while reporting a problem:
- [ ] The exact version of the related software, including but not limited to the OpenResty version
(if any), the NGINX core version, the `ngx_lua` module version(via `openresty -V` or `nginx -V`),
and the `lua-resty-core` version(via `resty -e 'print(require("resty.core").version)'`),
and your operating system version(via `uname -a`).
- [ ] **A minimal and standalone test case** that others can easily run on their side and
reproduce the issue you are seeing.
- [ ] Do not simply say "something is broken" or "something does not work". Always provide
as much details as possible. Always describe **the symptoms and your expected results**.
You can (temporarily) enable the nginx debugging logs to see the internal workings
of NGINX in your nginx''s `error.log` file. See http://nginx.org/en/docs/debugging_log.html
The same instructions apply equally well to OpenResty.
If you are seeing crashes, please provide the full backtrace for the crash. See
https://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/#core-dump
for more details.
Thanks for your cooperation.

2
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,2 @@
I hereby granted the copyright of the changes in this pull request
to the authors of this lua-resty-core project.

View File

@ -0,0 +1,30 @@
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Configure which types are allowed.
# Default: https://github.com/commitizen/conventional-commit-types
types: |
bugfix # bug fixes
change # backward incompatible changes
doc # documentation changes including code comments
editor # code editor related configurations
feature # implementing a new feature
optimize # performance optimizations
refactor # code refactoring and other code rearrangement
style # coding style changes
tests # test suite changes

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
*.swp
*.swo
*~
go
t/servroot*
reindex
nginx
ctags
tags
a.lua
mockeagain.so

6
.luacheckrc Normal file
View File

@ -0,0 +1,6 @@
std = 'ngx_lua'
globals = { 'ngx', 'ndk' }
unused_args = false
read_globals = {
"coroutine._yield"
}

105
.travis.yml Normal file
View File

@ -0,0 +1,105 @@
---
sudo: required
dist: focal
branches:
only:
- "master"
os: linux
language: c
compiler:
- gcc
addons:
apt:
packages:
- axel
- luarocks
- daemonize
cache:
directories:
- download-cache
env:
global:
- JOBS=2
- NGX_BUILD_JOBS=$JOBS
- LUAJIT_PREFIX=/opt/luajit21
- LUAJIT_LIB=$LUAJIT_PREFIX/lib
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
- LUA_INCLUDE_DIR=$LUAJIT_INC
- LUA_CMODULE_DIR=/lib
- PCRE_VER=8.45
- PCRE_PREFIX=/opt/pcre
- PCRE_LIB=$PCRE_PREFIX/lib
- PCRE_INC=$PCRE_PREFIX/include
- OPENSSL_PREFIX=/opt/ssl
- OPENSSL_LIB=$OPENSSL_PREFIX/lib
- OPENSSL_INC=$OPENSSL_PREFIX/include
- LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
- TEST_NGINX_SLEEP=0.005
- TEST_NGINX_RANDOMIZE=1
- LUACHECK_VER=0.21.1
matrix:
- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1u OPENSSL_PATCH_VER=1.1.1f
services:
- memcache
before_install:
- sudo luarocks install luacheck $LUACHECK_VER
- luacheck --globals coroutine -q .
- '! grep -n -P ''(?<=.{80}).+'' --color `find . -name ''*.lua''` || (echo "ERROR: Found Lua source lines exceeding 80 columns." > /dev/stderr; exit 1)'
- '! grep -n -P ''\t+'' --color `find . -name ''*.lua''` || (echo "ERROR: Cannot use tabs." > /dev/stderr; exit 1)'
- cpanm --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
install:
- if [ ! -d download-cache ]; then mkdir download-cache; fi
- if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi
- if [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache http://ftp.cs.stanford.edu/pub/exim/pcre/pcre-$PCRE_VER.tar.gz; fi
- git clone https://github.com/openresty/openresty.git ../openresty
- git clone https://github.com/openresty/openresty-devel-utils.git
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
- git clone https://github.com/openresty/lua-resty-lrucache.git
- git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2
- git clone https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module
- git clone https://github.com/openresty/mockeagain.git
- git clone https://github.com/openresty/test-nginx.git
- git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
script:
- cd luajit2/
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2 -O1 -DLUAJIT_SECURITY_STRID=0 -DLUAJIT_SECURITY_STRHASH=0 -DLUAJIT_SECURITY_PRNG=0 -DLUAJIT_SECURITY_MCODE=0 -DLUAJIT_TEST_FIXED_ORDER' > build.log 2>&1 || (cat build.log && exit 1)
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
- cd ..
- cd lua-resty-lrucache && sudo make DESTDIR=$LUAJIT_PREFIX LUA_LIB_DIR=/share/lua/5.1 install && cd ..
- tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz
- cd openssl-$OPENSSL_VER/
- if [ -n "$OPENSSL_PATCH_VER" ]; then patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch; fi
- ./config no-threads shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1)
- make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
- sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1)
- cd ../mockeagain/ && make CC=$CC -j$JOBS && cd ..
- tar zxf download-cache/pcre-$PCRE_VER.tar.gz
- cd pcre-$PCRE_VER/
- ./configure --prefix=$PCRE_PREFIX --enable-jit --enable-utf --enable-unicode-properties > build.log 2>&1 || (cat build.log && exit 1)
- make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
- sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1)
- cd ..
- export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH
- export LD_PRELOAD=$PWD/mockeagain/mockeagain.so
- export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH
- export TEST_NGINX_RESOLVER=8.8.4.4
- export NGX_BUILD_CC=$CC
- ngx-build $NGINX_VERSION --with-ipv6 --with-http_realip_module --with-http_ssl_module --with-pcre-jit --with-cc-opt="-I$OPENSSL_INC -I$PCRE_INC" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB -L$PCRE_LIB -Wl,-rpath,$PCRE_LIB" --add-module=../ndk-nginx-module --add-module=../echo-nginx-module --add-module=../set-misc-nginx-module --add-module=../headers-more-nginx-module --add-module=../lua-nginx-module --with-debug --with-stream_ssl_module --with-stream --with-ipv6 --add-module=../stream-lua-nginx-module > build.log 2>&1 || (cat build.log && exit 1)
- nginx -V
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
- prove -I. -Itest-nginx/lib -j$JOBS -r t

24
Makefile Normal file
View File

@ -0,0 +1,24 @@
OPENRESTY_PREFIX=/usr/local/openresty
#LUA_VERSION := 5.1
PREFIX ?= /usr/local
LUA_INCLUDE_DIR ?= $(PREFIX)/include
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
INSTALL ?= install
.PHONY: all test install
all: ;
install: all
$(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/resty/core/
$(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/ngx/
$(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/ngx/ssl
$(INSTALL) lib/resty/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/
$(INSTALL) lib/resty/core/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/core/
$(INSTALL) lib/ngx/*.lua $(DESTDIR)$(LUA_LIB_DIR)/ngx/
$(INSTALL) lib/ngx/ssl/*.lua $(DESTDIR)$(LUA_LIB_DIR)/ngx/ssl/
test: all
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t

439
README.markdown Normal file
View File

@ -0,0 +1,439 @@
Name
====
lua-resty-core - New FFI-based Lua API for ngx_http_lua_module and/or ngx_stream_lua_module
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Prerequisites](#prerequisites)
* [API Implemented](#api-implemented)
* [resty.core.hash](#restycorehash)
* [resty.core.base64](#restycorebase64)
* [resty.core.uri](#restycoreuri)
* [resty.core.regex](#restycoreregex)
* [resty.core.exit](#restycoreexit)
* [resty.core.shdict](#restycoreshdict)
* [resty.core.var](#restycorevar)
* [resty.core.ctx](#restycorectx)
* [get_ctx_table](#get_ctx_table)
* [resty.core.request](#restycorerequest)
* [resty.core.response](#restycoreresponse)
* [resty.core.misc](#restycoremisc)
* [resty.core.time](#restycoretime)
* [resty.core.worker](#restycoreworker)
* [resty.core.phase](#restycorephase)
* [resty.core.ndk](#restycorendk)
* [resty.core.socket](#restycoresocket)
* [resty.core.param](#restycoreparam)
* [ngx.semaphore](#ngxsemaphore)
* [ngx.balancer](#ngxbalancer)
* [ngx.ssl](#ngxssl)
* [ngx.ssl.clienthello](#ngxsslclienthello)
* [ngx.ssl.session](#ngxsslsession)
* [ngx.re](#ngxre)
* [ngx.resp](#ngxresp)
* [ngx.pipe](#ngxpipe)
* [ngx.process](#ngxprocess)
* [ngx.errlog](#ngxerrlog)
* [ngx.base64](#ngxbase64)
* [Caveat](#caveat)
* [TODO](#todo)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This library is production ready.
Synopsis
========
This library is automatically loaded by default in OpenResty 1.15.8.1. This
behavior can be disabled via the
[lua_load_resty_core](https://github.com/openresty/lua-nginx-module#lua_load_resty_core)
directive, but note that the use of this library is vividly recommended, as its
FFI implementation is both faster, safer, and more complete than the Lua C API
of the ngx_lua module.
If you are using an older version of OpenResty, you must load this library like
so:
```nginx
# nginx.conf
http {
# you do NOT need to configure the following line when you
# are using the OpenResty bundle 1.4.3.9+.
lua_package_path "/path/to/lua-resty-core/lib/?.lua;;";
init_by_lua_block {
require "resty.core"
collectgarbage("collect") -- just to collect any garbage
}
...
}
```
Description
===========
This pure Lua library reimplements part of the [ngx_lua](https://github.com/openresty/lua-nginx-module#readme) module's
[Nginx API for Lua](https://github.com/openresty/lua-nginx-module#nginx-api-for-lua)
with LuaJIT FFI and installs the new FFI-based Lua API into the ngx.* and ndk.* namespaces
used by the ngx_lua module.
In addition, this Lua library implements any significant new Lua APIs of
the [ngx_lua](https://github.com/openresty/lua-nginx-module#readme) module
as proper Lua modules, like [ngx.semaphore](#ngxsemaphore) and [ngx.balancer](#ngxbalancer).
The FFI-based Lua API can work with LuaJIT's JIT compiler. ngx_lua's default API is based on the standard
Lua C API, which will never be JIT compiled and the user Lua code is always interpreted (slowly).
Support for the new [ngx_stream_lua_module](https://github.com/openresty/stream-lua-nginx-module) has also begun.
This library is shipped with the OpenResty bundle by default. So you do not really need to worry about the dependencies
and requirements.
[Back to TOC](#table-of-contents)
Prerequisites
=============
**WARNING** This library is included with every OpenResty release. You should use the bundled version
of this library in the particular OpenResty release you are using. Otherwise you may run
into serious compatibility issues.
* LuaJIT 2.1 (for now, it is the v2.1 git branch in the official luajit-2.0 git repository: http://luajit.org/download.html )
* [ngx_http_lua_module](https://github.com/openresty/lua-nginx-module) v0.10.21.
* [ngx_stream_lua_module](https://github.com/openresty/stream-lua-nginx-module) v0.0.11.
* [lua-resty-lrucache](https://github.com/openresty/lua-resty-lrucache)
[Back to TOC](#table-of-contents)
API Implemented
===============
[Back to TOC](#table-of-contents)
## resty.core.hash
* [ngx.md5](https://github.com/openresty/lua-nginx-module#ngxmd5)
* [ngx.md5_bin](https://github.com/openresty/lua-nginx-module#ngxmd5_bin)
* [ngx.sha1_bin](https://github.com/openresty/lua-nginx-module#ngxsha1_bin)
[Back to TOC](#table-of-contents)
## resty.core.base64
* [ngx.encode_base64](https://github.com/openresty/lua-nginx-module#ngxencode_base64)
* [ngx.decode_base64](https://github.com/openresty/lua-nginx-module#ngxdecode_base64)
[Back to TOC](#table-of-contents)
## resty.core.uri
* [ngx.escape_uri](https://github.com/openresty/lua-nginx-module#ngxescape_uri)
* [ngx.unescape_uri](https://github.com/openresty/lua-nginx-module#ngxunescape_uri)
[Back to TOC](#table-of-contents)
## resty.core.regex
* [ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)
* [ngx.re.gmatch](https://github.com/openresty/lua-nginx-module#ngxregmatch)
* [ngx.re.find](https://github.com/openresty/lua-nginx-module#ngxrefind)
* [ngx.re.sub](https://github.com/openresty/lua-nginx-module#ngxresub)
* [ngx.re.gsub](https://github.com/openresty/lua-nginx-module#ngxregsub)
[Back to TOC](#table-of-contents)
## resty.core.exit
* [ngx.exit](https://github.com/openresty/lua-nginx-module#ngxexit)
[Back to TOC](#table-of-contents)
## resty.core.shdict
* [ngx.shared.DICT.get](https://github.com/openresty/lua-nginx-module#ngxshareddictget)
* [ngx.shared.DICT.get_stale](https://github.com/openresty/lua-nginx-module#ngxshareddictget_stale)
* [ngx.shared.DICT.incr](https://github.com/openresty/lua-nginx-module#ngxshareddictincr)
* [ngx.shared.DICT.set](https://github.com/openresty/lua-nginx-module#ngxshareddictset)
* [ngx.shared.DICT.safe_set](https://github.com/openresty/lua-nginx-module#ngxshareddictsafe_set)
* [ngx.shared.DICT.add](https://github.com/openresty/lua-nginx-module#ngxshareddictadd)
* [ngx.shared.DICT.safe_add](https://github.com/openresty/lua-nginx-module#ngxshareddictsafe_add)
* [ngx.shared.DICT.replace](https://github.com/openresty/lua-nginx-module#ngxshareddictreplace)
* [ngx.shared.DICT.delete](https://github.com/openresty/lua-nginx-module#ngxshareddictdelete)
* [ngx.shared.DICT.ttl](https://github.com/openresty/lua-nginx-module#ngxshareddictttl)
* [ngx.shared.DICT.expire](https://github.com/openresty/lua-nginx-module#ngxshareddictexpire)
* [ngx.shared.DICT.flush_all](https://github.com/openresty/lua-nginx-module#ngxshareddictflush_all)
* [ngx.shared.DICT.free_space](https://github.com/openresty/lua-nginx-module#ngxshareddictfree_space)
* [ngx.shared.DICT.capacity](https://github.com/openresty/lua-nginx-module#ngxshareddictcapacity)
[Back to TOC](#table-of-contents)
## resty.core.var
* [ngx.var.VARIABLE](https://github.com/openresty/lua-nginx-module#ngxvarvariable)
[Back to TOC](#table-of-contents)
## resty.core.ctx
* [ngx.ctx](https://github.com/openresty/lua-nginx-module#ngxctx)
[Back to TOC](#table-of-contents)
## get_ctx_table
**syntax:** *ctx = resty.core.ctx.get_ctx_table(ctx?)*
Similar to [ngx.ctx](#restycorectx) but it accepts an optional `ctx` argument.
It will use the `ctx` from caller instead of creating a new table
when the `ctx` table does not exist.
Notice: the `ctx` table will be used in the current request's whole life cycle.
Please be very careful when you try to reuse the `ctx` table.
You need to make sure there is no Lua code using or going to use the `ctx` table
in the current request before you reusing the `ctx` table in some other place.
[Back to TOC](#table-of-contents)
## resty.core.request
* [ngx.req.get_headers](https://github.com/openresty/lua-nginx-module#ngxreqget_headers)
* [ngx.req.get_uri_args](https://github.com/openresty/lua-nginx-module#ngxreqget_uri_args)
* [ngx.req.start_time](https://github.com/openresty/lua-nginx-module#ngxreqstart_time)
* [ngx.req.get_method](https://github.com/openresty/lua-nginx-module#ngxreqget_method)
* [ngx.req.set_method](https://github.com/openresty/lua-nginx-module#ngxreqset_method)
* [ngx.req.set_header](https://github.com/openresty/lua-nginx-module#ngxreqset_header)
* [ngx.req.clear_header](https://github.com/openresty/lua-nginx-module#ngxreqclear_header)
[Back to TOC](#table-of-contents)
## resty.core.response
* [ngx.header.HEADER](https://github.com/openresty/lua-nginx-module#ngxheaderheader)
[Back to TOC](#table-of-contents)
## resty.core.misc
* [ngx.status](https://github.com/openresty/lua-nginx-module#ngxstatus)
* [ngx.is_subrequest](https://github.com/openresty/lua-nginx-module#ngxis_subrequest)
* [ngx.headers_sent](https://github.com/openresty/lua-nginx-module#ngxheaders_sent)
* [ngx.req.is_internal](https://github.com/openresty/lua-nginx-module#ngxreqis_internal)
[Back to TOC](#table-of-contents)
## resty.core.time
* [ngx.time](https://github.com/openresty/lua-nginx-module#ngxtime)
* [ngx.now](https://github.com/openresty/lua-nginx-module#ngxnow)
* [ngx.update_time](https://github.com/openresty/lua-nginx-module#ngxupdate_time)
* [ngx.localtime](https://github.com/openresty/lua-nginx-module#ngxlocaltime)
* [ngx.utctime](https://github.com/openresty/lua-nginx-module#ngxutctime)
* [ngx.cookie_time](https://github.com/openresty/lua-nginx-module#ngxcookie_time)
* [ngx.http_time](https://github.com/openresty/lua-nginx-module#ngxhttp_time)
* [ngx.parse_http_time](https://github.com/openresty/lua-nginx-module#ngxparse_http_time)
* [monotonic_msec](./lib/resty/core/time.md#monotonic_msec)
* [monotonic_time](./lib/resty/core/time.md#monotonic_time)
[Back to TOC](#table-of-contents)
## resty.core.worker
* [ngx.worker.exiting](https://github.com/openresty/lua-nginx-module#ngxworkerexiting)
* [ngx.worker.pid](https://github.com/openresty/lua-nginx-module#ngxworkerpid)
* [ngx.worker.id](https://github.com/openresty/lua-nginx-module#ngxworkerid)
* [ngx.worker.count](https://github.com/openresty/lua-nginx-module#ngxworkercount)
[Back to TOC](#table-of-contents)
## resty.core.phase
* [ngx.get_phase](https://github.com/openresty/lua-nginx-module#ngxget_phase)
[Back to TOC](#table-of-contents)
## resty.core.ndk
* [ndk.set_var](https://github.com/openresty/lua-nginx-module#ndkset_vardirective)
[Back to TOC](#table-of-contents)
## resty.core.socket
* [socket.setoption](https://github.com/openresty/lua-nginx-module#tcpsocksetoption)
* [socket.setclientcert](https://github.com/openresty/lua-nginx-module#tcpsocksetclientcert)
* [socket.sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
[Back to TOC](#table-of-contents)
## resty.core.param
* [ngx.arg](https://github.com/openresty/lua-nginx-module#ngxarg) (getter only)
[Back to TOC](#table-of-contents)
## ngx.semaphore
This Lua module implements a semaphore API for efficient "light thread" synchronization,
which can work across different requests (but not across nginx worker processes).
See the [documentation](./lib/ngx/semaphore.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.balancer
This Lua module implements for defining dynamic upstream balancers in Lua.
See the [documentation](./lib/ngx/balancer.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.ssl
This Lua module provides a Lua API for controlling SSL certificates, private keys,
SSL protocol versions, and etc in NGINX downstream SSL handshakes.
See the [documentation](./lib/ngx/ssl.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.ssl.clienthello
This Lua module provides a Lua API for post-processing SSL client hello message
for NGINX downstream SSL connections.
See the [documentation](./lib/ngx/ssl/clienthello.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.ssl.session
This Lua module provides a Lua API for manipulating SSL session data and IDs
for NGINX downstream SSL connections.
See the [documentation](./lib/ngx/ssl/session.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.re
This Lua module provides a Lua API which implements convenience utilities for
the `ngx.re` API.
See the [documentation](./lib/ngx/re.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.resp
This Lua module provides Lua API which could be used to handle HTTP response.
See the [documentation](./lib/ngx/resp.md) for this Lua module for more details.
[Back to TOC](#table-of-contents)
## ngx.pipe
This module provides a Lua API to spawn processes and communicate with them in
a non-blocking fashion.
See the [documentation](./lib/ngx/pipe.md) for this Lua module for more
details.
This module was first introduced in lua-resty-core v0.1.16.
[Back to TOC](#table-of-contents)
## ngx.process
This Lua module is used to manage the nginx process in Lua.
See the [documentation](./lib/ngx/process.md) for this Lua module for more details.
This module was first introduced in lua-resty-core v0.1.12.
[Back to TOC](#table-of-contents)
## ngx.errlog
This Lua module provides Lua API to capture and manage nginx error log messages.
See the [documentation](./lib/ngx/errlog.md) for this Lua module for more details.
This module was first introduced in lua-resty-core v0.1.12.
[Back to TOC](#table-of-contents)
## ngx.base64
This Lua module provides Lua API to urlsafe base64 encode/decode.
See the [documentation](./lib/ngx/base64.md) for this Lua module for more details.
This module was first introduced in lua-resty-core v0.1.14.
[Back to TOC](#table-of-contents)
Caveat
======
If the user Lua code is not JIT compiled, then use of this library may
lead to performance drop in interpreted mode. You will only observe
speedup when you get a good part of your user Lua code JIT compiled.
[Back to TOC](#table-of-contents)
TODO
====
* Re-implement `ngx_lua`'s cosocket API with FFI.
* Re-implement `ngx_lua`'s `ngx.eof` and `ngx.flush` API functions with FFI.
[Back to TOC](#table-of-contents)
Author
======
Yichun "agentzh" Zhang (章亦春) <agentzh@gmail.com>, OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2013-2019, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the ngx_lua module: https://github.com/openresty/lua-nginx-module#readme
* LuaJIT FFI: http://luajit.org/ext_ffi.html
[Back to TOC](#table-of-contents)

10
dist.ini Normal file
View File

@ -0,0 +1,10 @@
name=lua-resty-core
abstract=New FFI-based Lua API for the ngx_lua module
author=Yichun "agentzh" Zhang (agentzh)
is_original=yes
license=2bsd
lib_dir=lib
doc_dir=lib
repo_link=https://github.com/openresty/lua-resty-core
main_module=lib/resty/core/base.lua
requires = luajit >= 2.1.0, nginx >= 1.13.6, ngx_http_lua = 0.10.13, openresty/lua-resty-lrucache >= 0.08

234
lib/ngx/balancer.lua Normal file
View File

@ -0,0 +1,234 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local errmsg = base.get_errmsg_ptr()
local FFI_OK = base.FFI_OK
local FFI_ERROR = base.FFI_ERROR
local int_out = ffi.new("int[1]")
local get_request = base.get_request
local error = error
local type = type
local tonumber = tonumber
local max = math.max
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_balancer_set_current_peer
local ngx_lua_ffi_balancer_set_more_tries
local ngx_lua_ffi_balancer_get_last_failure
local ngx_lua_ffi_balancer_set_timeouts -- used by both stream and http
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r,
const unsigned char *addr, size_t addr_len, int port, char **err);
int ngx_http_lua_ffi_balancer_set_more_tries(ngx_http_request_t *r,
int count, char **err);
int ngx_http_lua_ffi_balancer_get_last_failure(ngx_http_request_t *r,
int *status, char **err);
int ngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r,
long connect_timeout, long send_timeout,
long read_timeout, char **err);
int ngx_http_lua_ffi_balancer_recreate_request(ngx_http_request_t *r,
char **err);
]]
ngx_lua_ffi_balancer_set_current_peer =
C.ngx_http_lua_ffi_balancer_set_current_peer
ngx_lua_ffi_balancer_set_more_tries =
C.ngx_http_lua_ffi_balancer_set_more_tries
ngx_lua_ffi_balancer_get_last_failure =
C.ngx_http_lua_ffi_balancer_get_last_failure
ngx_lua_ffi_balancer_set_timeouts =
C.ngx_http_lua_ffi_balancer_set_timeouts
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_balancer_set_current_peer(
ngx_stream_lua_request_t *r,
const unsigned char *addr, size_t addr_len, int port, char **err);
int ngx_stream_lua_ffi_balancer_set_more_tries(ngx_stream_lua_request_t *r,
int count, char **err);
int ngx_stream_lua_ffi_balancer_get_last_failure(
ngx_stream_lua_request_t *r, int *status, char **err);
int ngx_stream_lua_ffi_balancer_set_timeouts(ngx_stream_lua_request_t *r,
long connect_timeout, long timeout, char **err);
]]
ngx_lua_ffi_balancer_set_current_peer =
C.ngx_stream_lua_ffi_balancer_set_current_peer
ngx_lua_ffi_balancer_set_more_tries =
C.ngx_stream_lua_ffi_balancer_set_more_tries
ngx_lua_ffi_balancer_get_last_failure =
C.ngx_stream_lua_ffi_balancer_get_last_failure
local ngx_stream_lua_ffi_balancer_set_timeouts =
C.ngx_stream_lua_ffi_balancer_set_timeouts
ngx_lua_ffi_balancer_set_timeouts =
function(r, connect_timeout, send_timeout, read_timeout, err)
local timeout = max(send_timeout, read_timeout)
return ngx_stream_lua_ffi_balancer_set_timeouts(r, connect_timeout,
timeout, err)
end
else
error("unknown subsystem: " .. subsystem)
end
local peer_state_names = {
[1] = "keepalive",
[2] = "next",
[4] = "failed",
}
local _M = { version = base.version }
function _M.set_current_peer(addr, port)
local r = get_request()
if not r then
error("no request found")
end
if not port then
port = 0
elseif type(port) ~= "number" then
port = tonumber(port)
end
local rc = ngx_lua_ffi_balancer_set_current_peer(r, addr, #addr,
port, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
function _M.set_more_tries(count)
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_balancer_set_more_tries(r, count, errmsg)
if rc == FFI_OK then
if errmsg[0] == nil then
return true
end
return true, ffi_str(errmsg[0]) -- return the warning
end
return nil, ffi_str(errmsg[0])
end
function _M.get_last_failure()
local r = get_request()
if not r then
error("no request found")
end
local state = ngx_lua_ffi_balancer_get_last_failure(r, int_out, errmsg)
if state == 0 then
return nil
end
if state == FFI_ERROR then
return nil, nil, ffi_str(errmsg[0])
end
return peer_state_names[state] or "unknown", int_out[0]
end
function _M.set_timeouts(connect_timeout, send_timeout, read_timeout)
local r = get_request()
if not r then
error("no request found")
end
if not connect_timeout then
connect_timeout = 0
elseif type(connect_timeout) ~= "number" or connect_timeout <= 0 then
error("bad connect timeout", 2)
else
connect_timeout = connect_timeout * 1000
end
if not send_timeout then
send_timeout = 0
elseif type(send_timeout) ~= "number" or send_timeout <= 0 then
error("bad send timeout", 2)
else
send_timeout = send_timeout * 1000
end
if not read_timeout then
read_timeout = 0
elseif type(read_timeout) ~= "number" or read_timeout <= 0 then
error("bad read timeout", 2)
else
read_timeout = read_timeout * 1000
end
local rc
rc = ngx_lua_ffi_balancer_set_timeouts(r, connect_timeout,
send_timeout, read_timeout,
errmsg)
if rc == FFI_OK then
return true
end
return false, ffi_str(errmsg[0])
end
if subsystem == 'http' then
function _M.recreate_request()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_balancer_recreate_request(r, errmsg)
if rc == FFI_OK then
return true
end
if errmsg[0] ~= nil then
return nil, ffi_str(errmsg[0])
end
return nil, "failed to recreate the upstream request"
end
end
return _M

335
lib/ngx/balancer.md Normal file
View File

@ -0,0 +1,335 @@
Name
====
ngx.balancer - Lua API for defining dynamic upstream balancers in Lua
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [http subsystem](#http-subsystem)
* [stream subsystem](#stream-subsystem)
* [Description](#description)
* [Methods](#methods)
* [set_current_peer](#set_current_peer)
* [set_more_tries](#set_more_tries)
* [get_last_failure](#get_last_failure)
* [set_timeouts](#set_timeouts)
* [recreate_request](#recreate_request)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
http subsystem
--------------
```nginx
http {
upstream backend {
server 0.0.0.1; # just an invalid address as a place holder
balancer_by_lua_block {
local balancer = require "ngx.balancer"
-- well, usually we calculate the peer's host and port
-- according to some balancing policies instead of using
-- hard-coded values like below
local host = "127.0.0.2"
local port = 8080
local ok, err = balancer.set_current_peer(host, port)
if not ok then
ngx.log(ngx.ERR, "failed to set the current peer: ", err)
return ngx.exit(500)
end
}
keepalive 10; # connection pool
}
server {
# this is the real entry point
listen 80;
location / {
# make use of the upstream named "backend" defined above:
proxy_pass http://backend/fake;
}
}
server {
# this server is just for mocking up a backend peer here...
listen 127.0.0.2:8080;
location = /fake {
echo "this is the fake backend peer...";
}
}
}
```
[Back to TOC](#table-of-contents)
stream subsystem
----------------
```nginx
stream {
upstream backend {
server 0.0.0.1:1234; # just an invalid address as a place holder
balancer_by_lua_block {
local balancer = require "ngx.balancer"
-- well, usually we calculate the peer's host and port
-- according to some balancing policies instead of using
-- hard-coded values like below
local host = "127.0.0.2"
local port = 8080
local ok, err = balancer.set_current_peer(host, port)
if not ok then
ngx.log(ngx.ERR, "failed to set the current peer: ", err)
return ngx.exit(ngx.ERROR)
end
}
}
server {
# this is the real entry point
listen 10000;
# make use of the upstream named "backend" defined above:
proxy_pass backend;
}
server {
# this server is just for mocking up a backend peer here...
listen 127.0.0.2:8080;
echo "this is the fake backend peer...";
}
}
```
[Back to TOC](#table-of-contents)
Description
===========
This Lua module provides API functions to allow defining highly dynamic NGINX load balancers for
any existing nginx upstream modules like [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html),
[ngx_http_fastcgi_module](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html) and
[ngx_stream_proxy_module](https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html).
It allows you to dynamically select a backend peer to connect to (or retry) on a per-request
basis from a list of backend peers which may also be dynamic.
[Back to TOC](#table-of-contents)
Methods
=======
All the methods of this module are static (or module-level). That is, you do not need an object (or instance)
to call these methods.
[Back to TOC](#table-of-contents)
set_current_peer
----------------
**syntax:** *ok, err = balancer.set_current_peer(host, port)*
**context:** *balancer_by_lua&#42;*
Sets the peer address (host and port) for the current backend query (which may be a retry).
Domain names in `host` do not make sense. You need to use OpenResty libraries like
[lua-resty-dns](https://github.com/openresty/lua-resty-dns) to obtain IP address(es) from
all the domain names before entering the `balancer_by_lua*` handler (for example,
you can perform DNS lookups in an earlier phase like [access_by_lua*](https://github.com/openresty/lua-nginx-module#access_by_lua)
and pass the results to the `balancer_by_lua*` handler via [ngx.ctx](https://github.com/openresty/lua-nginx-module#ngxctx).
[Back to TOC](#table-of-contents)
set_more_tries
--------------
**syntax:** *ok, err = balancer.set_more_tries(count)*
**context:** *balancer_by_lua&#42;*
Sets the tries performed when the current attempt (which may be a retry) fails (as determined
by directives like [proxy_next_upstream](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream), depending on what
particular nginx uptream module you are currently using). Note that the current attempt is *excluded* in the `count` number set here.
Please note that, the total number of tries in a single downstream request cannot exceed the
hard limit configured by directives like [proxy_next_upstream_tries](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries),
depending on what concrete nginx upstream module you are using. When exceeding this limit,
the `count` value will get reduced to meet the limit and the second return value will be
the string `"reduced tries due to limit"`, which is a warning, while the first return value
is still a `true` value.
[Back to TOC](#table-of-contents)
get_last_failure
----------------
**syntax:** *state_name, status_code = balancer.get_last_failure()*
**context:** *balancer_by_lua&#42;*
Retrieves the failure details about the previous failed attempt (if any) when the `next_upstream` retrying
mechanism is in action. When there was indeed a failed previous attempt, it returned a string describing
that attempt's state name, as well as an integer describing the status code of that attempt.
Possible state names are as follows:
* `"next"`
Failures due to bad status codes sent from the backend server. The origin's response is same though, which means the backend connection
can still be reused for future requests.
* `"failed"`
Fatal errors while communicating to the backend server (like connection timeouts, connection resets, and etc). In this case,
the backend connection must be aborted and cannot get reused.
Possible status codes are those HTTP error status codes like `502` and `504`.
For stream module, `status_code` will always be 0 (ngx.OK) and is provided for compatibility reasons.
When the current attempt is the first attempt for the current downstream request (which means
there is no previous attempts at all), this
method always returns a single `nil` value.
[Back to TOC](#table-of-contents)
set_timeouts
------------
**syntax:** `ok, err = balancer.set_timeouts(connect_timeout, send_timeout, read_timeout)`
**context:** *balancer_by_lua&#42;*
Sets the upstream timeout (connect, send and read) in seconds for the current and any
subsequent backend requests (which might be a retry).
If you want to inherit the timeout value of the global `nginx.conf` configuration (like `proxy_connect_timeout`), then
just specify the `nil` value for the corresponding argument (like the `connect_timeout` argument).
Zero and negative timeout values are not allowed.
You can specify millisecond precision in the timeout values by using floating point numbers like 0.001 (which means 1ms).
**Note:** `send_timeout` and `read_timeout` are controlled by the same config
[`proxy_timeout`](https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_timeout)
for `ngx_stream_proxy_module`. To keep API compatibility, this function will use `max(send_timeout, read_timeout)`
as the value for setting `proxy_timeout`.
Returns `true` when the operation is successful; returns `nil` and a string describing the error
otherwise.
This only affects the current downstream request. It is not a global change.
For the best performance, you should use the [OpenResty](https://openresty.org/) bundle.
This function was first added in the `0.1.7` version of this library.
[Back to TOC](#table-of-contents)
recreate_request
----------------
**syntax:** `ok, err = balancer.recreate_request()`
**context:** *balancer_by_lua&#42;*
Recreates the request buffer for sending to the upstream server. This is useful, for example
if you want to change a request header field to the new upstream server on balancer retries.
Normally this does not work because the request buffer is created once during upstream module
initialization and won't be regenerated for subsequent retries. However you can use
`proxy_set_header My-Header $my_header` and set the `ngx.var.my_header` variable inside the
balancer phase. Calling `balancer.recreate_request()` after updating a header field will
cause the request buffer to be re-generated and the `My-Header` header will thus contain
the new value.
**Warning:** because the request buffer has to be recreated and such allocation occurs on the
request memory pool, the old buffer has to be thrown away and will only be freed after the request
finishes. Do not call this function too often or memory leaks may be noticeable. Even so, a call
to this function should be made **only** if you know the request buffer must be regenerated,
instead of unconditionally in each balancer retries.
This function was first added in the `0.1.20` version of this library.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Yichun Zhang &lt;agentzh@gmail.com&gt; (agentzh), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2015-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* the [balancer_by_lua*](https://github.com/openresty/lua-nginx-module#balancer_by_lua_block) directive.
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

89
lib/ngx/base64.lua Normal file
View File

@ -0,0 +1,89 @@
-- Copyright (C) by Yichun Zhang (agentzh)
-- Copyright (C) by OpenResty Inc.
local ffi = require("ffi")
local base = require("resty.core.base")
local ffi_str = ffi.string
local type = type
local C = ffi.C
local NGX_ERROR = ngx.ERROR
local _M = { version = base.version }
ffi.cdef[[
typedef intptr_t ngx_int_t;
void ngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src);
ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);
]]
local get_string_buf = base.get_string_buf
local dst_str_t = ffi.new("ngx_str_t[1]")
local src_str_t = ffi.new("ngx_str_t[1]")
local function base64_encoded_length(len)
return ((len + 2) / 3) * 4
end
local function base64_decoded_length(len)
return ((len + 3) / 4) * 3
end
function _M.encode_base64url(s)
if type(s) ~= "string" then
return nil, "must provide a string"
end
local len = #s
local trans_len = base64_encoded_length(len)
local src = src_str_t[0]
local dst = dst_str_t[0]
src.data = s
src.len = len
dst.data = get_string_buf(trans_len)
dst.len = trans_len
C.ngx_encode_base64url(dst_str_t, src_str_t)
return ffi_str(dst.data, dst.len)
end
function _M.decode_base64url(s)
if type(s) ~= "string" then
return nil, "must provide a string"
end
local len = #s
local trans_len = base64_decoded_length(len)
local src = src_str_t[0]
local dst = dst_str_t[0]
src.data = s
src.len = len
dst.data = get_string_buf(trans_len)
dst.len = trans_len
local ret = C.ngx_decode_base64url(dst_str_t, src_str_t)
if ret == NGX_ERROR then
return nil, "invalid input"
end
return ffi_str(dst.data, dst.len)
end
return _M

137
lib/ngx/base64.md Normal file
View File

@ -0,0 +1,137 @@
Name
====
`ngx.base64` - urlsafe base64 encode/decode functions OpenResty/ngx\_lua.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Methods](#methods)
* [encode\_base64url](#encode_base64url)
* [decode\_base64url](#decode_base64url)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is production ready.
Synopsis
========
```lua
local b64 = require("ngx.base64")
local res, err
res = b64.encode_base64url("foo")
res, err = b64.decode_base64url(res)
if not res then
-- invalid input
ngx.log(ngx.ERR, err)
end
assert(res == "foo")
```
[Back to TOC](#table-of-contents)
Methods
=======
encode\_base64url
-----------------
**syntax:** *encoded = base64.encode_base64url(input)*
**context:** *any*
Encode `input` using base64url rules. Returns the encoded string.
[Back to TOC](#table-of-contents)
decode\_base64url
-----------------
**syntax:** *decoded, err = base64.decode_base64url(input)*
**context:** *any*
Decode `input` using base64url rules. Returns the decoded string.
If the `input` is not a valid base64url encoded string, `decoded `will be `nil`
and `err` will be a string describing the error.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Datong Sun &lt;datong@openresty.com&gt; (dndx), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

170
lib/ngx/errlog.lua Normal file
View File

@ -0,0 +1,170 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local ffi = require 'ffi'
local ffi_string = ffi.string
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local C = ffi.C
local new_tab = base.new_tab
local ffi_new = ffi.new
local charpp = ffi_new("char *[1]")
local intp = ffi.new("int[1]")
local num_value = ffi_new("double[1]")
local get_request = base.get_request
local tonumber = tonumber
local type = type
local error = error
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_errlog_set_filter_level
local ngx_lua_ffi_errlog_get_msg
local ngx_lua_ffi_errlog_get_sys_filter_level
local ngx_lua_ffi_raw_log
local _M = { version = base.version }
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_errlog_set_filter_level(int level, unsigned char *err,
size_t *errlen);
int ngx_http_lua_ffi_errlog_get_msg(char **log, int *loglevel,
unsigned char *err, size_t *errlen, double *log_time);
int ngx_http_lua_ffi_errlog_get_sys_filter_level(ngx_http_request_t *r);
int ngx_http_lua_ffi_raw_log(ngx_http_request_t *r, int level,
const unsigned char *s, size_t s_len);
]]
ngx_lua_ffi_errlog_set_filter_level =
C.ngx_http_lua_ffi_errlog_set_filter_level
ngx_lua_ffi_errlog_get_msg = C.ngx_http_lua_ffi_errlog_get_msg
ngx_lua_ffi_errlog_get_sys_filter_level =
C.ngx_http_lua_ffi_errlog_get_sys_filter_level
ngx_lua_ffi_raw_log = C.ngx_http_lua_ffi_raw_log
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_errlog_set_filter_level(int level, unsigned char *err,
size_t *errlen);
int ngx_stream_lua_ffi_errlog_get_msg(char **log, int *loglevel,
unsigned char *err, size_t *errlen, double *log_time);
int ngx_stream_lua_ffi_errlog_get_sys_filter_level(ngx_stream_lua_request_t *r);
int ngx_stream_lua_ffi_raw_log(ngx_stream_lua_request_t *r, int level,
const unsigned char *s, size_t s_len);
]]
ngx_lua_ffi_errlog_set_filter_level =
C.ngx_stream_lua_ffi_errlog_set_filter_level
ngx_lua_ffi_errlog_get_msg = C.ngx_stream_lua_ffi_errlog_get_msg
ngx_lua_ffi_errlog_get_sys_filter_level =
C.ngx_stream_lua_ffi_errlog_get_sys_filter_level
ngx_lua_ffi_raw_log = C.ngx_stream_lua_ffi_raw_log
end
local ERR_BUF_SIZE = 128
local FFI_ERROR = base.FFI_ERROR
function _M.set_filter_level(level)
if not level then
return nil, [[missing "level" argument]]
end
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local rc = ngx_lua_ffi_errlog_set_filter_level(level, err, errlen)
if rc == FFI_ERROR then
return nil, ffi_string(err, errlen[0])
end
return true
end
function _M.get_logs(max, logs)
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local log = charpp
local loglevel = intp
local log_time = num_value
max = max or 10
if not logs then
logs = new_tab(max * 3 + 1, 0)
end
local count = 0
for i = 1, max do
local loglen = ngx_lua_ffi_errlog_get_msg(log, loglevel, err, errlen,
log_time)
if loglen == FFI_ERROR then
return nil, ffi_string(err, errlen[0])
end
if loglen > 0 then
logs[count + 1] = loglevel[0]
logs[count + 2] = log_time[0]
logs[count + 3] = ffi_string(log[0], loglen)
count = count + 3
end
if loglen < 0 then -- no error log
logs[count + 1] = nil
break
end
if i == max then -- last one
logs[count + 1] = nil
break
end
end
return logs
end
function _M.get_sys_filter_level()
local r = get_request()
return tonumber(ngx_lua_ffi_errlog_get_sys_filter_level(r))
end
function _M.raw_log(level, msg)
if type(level) ~= "number" then
error("bad argument #1 to 'raw_log' (must be a number)", 2)
end
if type(msg) ~= "string" then
error("bad argument #2 to 'raw_log' (must be a string)", 2)
end
local r = get_request()
local rc = ngx_lua_ffi_raw_log(r, level, msg, #msg)
if rc == FFI_ERROR then
error("bad log level", 2)
end
end
return _M

429
lib/ngx/errlog.md Normal file
View File

@ -0,0 +1,429 @@
Name
====
`ngx.errlog` - manage nginx error log data in Lua for OpenResty/ngx_lua.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Capturing nginx error logs with specified log filtering level](#capturing-nginx-error-logs-with-specified-log-filtering-level)
* [Methods](#methods)
* [set_filter_level](#set_filter_level)
* [get_logs](#get_logs)
* [get_sys_filter_level](#get_sys_filter_level)
* [raw_log](#raw_log)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
The API is still in flux and may change in the future without notice.
Synopsis
========
Capturing nginx error logs with specified log filtering level
-------------------------------------------------------------
```nginx
error_log logs/error.log info;
http {
# enable capturing error logs
lua_capture_error_log 32m;
init_by_lua_block {
local errlog = require "ngx.errlog"
local status, err = errlog.set_filter_level(ngx.WARN)
if not status then
ngx.log(ngx.ERR, err)
return
end
ngx.log(ngx.WARN, "set error filter level: WARN")
}
server {
# ...
location = /t {
content_by_lua_block {
local errlog = require "ngx.errlog"
ngx.log(ngx.INFO, "test1")
ngx.log(ngx.WARN, "test2")
ngx.log(ngx.ERR, "test3")
local logs, err = errlog.get_logs(10)
if not logs then
ngx.say("FAILED ", err)
return
end
for i = 1, #logs, 3 do
ngx.say("level: ", logs[i], " time: ", logs[i + 1],
" data: ", logs[i + 2])
end
}
}
}
}
```
The example location above produces a response like this:
```
level: 5 time: 1498546995.304 data: 2017/06/27 15:03:15 [warn] 46877#0:
[lua] init_by_lua:8: set error filter level: WARN
level: 5 time: 1498546999.178 data: 2017/06/27 15:03:19 [warn] 46879#0: *1
[lua] test.lua:5: test2, client: 127.0.0.1, server: localhost, ......
level: 4 time: 1498546999.178 data: 2017/06/27 15:03:19 [error] 46879#0: *1
[lua] test.lua:6: test3, client: 127.0.0.1, server: localhost, ......
```
[Back to TOC](#table-of-contents)
Methods
=======
set_filter_level
-----------------
**syntax:** *status, err = log_module.set_filter_level(log_level)*
**context:** *init_by_lua&#42;*
Specifies the filter log level, only to capture and buffer the error logs with a log level
no lower than the specified level.
If we don't call this API, all of the error logs will be captured by default.
In case of error, `nil` will be returned as well as a string describing the
error.
This API should always work with directive
[lua_capture_error_log](https://github.com/openresty/lua-nginx-module#lua_capture_error_log).
See [Nginx log level constants](https://github.com/openresty/lua-nginx-module#nginx-log-level-constants) for all nginx log levels.
For example,
```lua
init_by_lua_block {
local errlog = require "ngx.errlog"
errlog.set_filter_level(ngx.WARN)
}
```
*NOTE:* The debugging logs since when OpenResty or NGINX is not built with `--with-debug`, all the debug level logs are suppressed regardless.
[Back to TOC](#table-of-contents)
get_logs
--------
**syntax:** *res, err = log_module.get_logs(max?, res?)*
**context:** *any*
Fetches the captured nginx error log messages if any in the global data buffer
specified by `ngx_lua`'s
[lua_capture_error_log](https://github.com/openresty/lua-nginx-module#lua_capture_error_log)
directive. Upon return, this Lua function also *removes* those messages from
that global capturing buffer to make room for future new error log data.
In case of error, `nil` will be returned as well as a string describing the
error.
The optional `max` argument is a number that when specified, will prevent
`errlog.get_logs` from adding more than `max` messages to the `res` array.
```lua
for i = 1, 20 do
ngx.log(ngx.ERR, "test")
end
local errlog = require "ngx.errlog"
local res = errlog.get_logs(10)
-- the number of messages in the `res` table is 10 and the `res` table
-- has 30 elements.
```
The resulting table has the following structure:
```lua
{ level1, time1, msg1, level2, time2, msg2, ... }
```
The `levelX` values are constants defined below:
https://github.com/openresty/lua-nginx-module/#nginx-log-level-constants
The `timeX` values are UNIX timestamps in seconds with millisecond precision. The sub-second part is presented as the decimal part.
The time format is exactly the same as the value returned by [ngx.now](https://github.com/openresty/lua-nginx-module/#ngxnow). It is
also subject to NGINX core's time caching.
The `msgX` values are the error log message texts.
So to traverse this array, the user can use a loop like this:
```lua
for i = 1, #res, 3 do
local level = res[i]
if not level then
break
end
local time = res[i + 1]
local msg = res[i + 2]
-- handle the current message with log level in `level`,
-- log time in `time`, and log message body in `msg`.
end
```
Specifying `max <= 0` disables this behavior, meaning that the number of
results won't be limited.
The optional 2th argument `res` can be a user-supplied Lua table
to hold the result instead of creating a brand new table. This can avoid
unnecessary table dynamic allocations on hot Lua code paths. It is used like this:
```lua
local errlog = require "ngx.errlog"
local new_tab = require "table.new"
local buffer = new_tab(100 * 3, 0) -- for 100 messages
local errlog = require "ngx.errlog"
local res, err = errlog.get_logs(0, buffer)
if res then
-- res is the same table as `buffer`
for i = 1, #res, 3 do
local level = res[i]
if not level then
break
end
local time = res[i + 1]
local msg = res[i + 2]
...
end
end
```
When provided with a `res` table, `errlog.get_logs` won't clear the table
for performance reasons, but will rather insert a trailing `nil` value
after the last table element.
When the trailing `nil` is not enough for your purpose, you should
clear the table yourself before feeding it into the `errlog.get_logs` function.
[Back to TOC](#table-of-contents)
get_sys_filter_level
--------------------
**syntax:** *log_level = log_module.get_sys_filter_level()*
**context:** *any*
Return the nginx core's error log filter level (defined via the [error_log](http://nginx.org/r/error_log)
configuration directive in `nginx.conf`) as an integer value matching the nginx error log level
constants documented below:
https://github.com/openresty/lua-nginx-module/#nginx-log-level-constants
For example:
```lua
local errlog = require "ngx.errlog"
local log_level = errlog.get_sys_filter_level()
-- Now the filter level is always one level higher than system default log level on priority
local status, err = errlog.set_filter_level(log_level - 1)
if not status then
ngx.log(ngx.ERR, err)
return
end
```
[Back to TOC](#table-of-contents)
raw_log
-------
**syntax:** *log_module.raw_log(log_level, msg)*
**context:** *any*
Log `msg` to the error logs with the given logging level.
Just like the [ngx.log](https://github.com/openresty/lua-nginx-module#ngxlog)
API, the `log_level` argument can take constants like `ngx.ERR` and `ngx.WARN`.
Check out [Nginx log level constants for
details.](https://github.com/openresty/lua-nginx-module#nginx-log-level-constants)
However, unlike the `ngx.log` API which accepts variadic arguments, this
function only accepts a single string as its second argument `msg`.
This function differs from `ngx.log` in the way that it will not prefix the
written logs with any sort of debug information (such as the caller's file
and line number).
For example, while `ngx.log` would produce
```
2017/07/09 19:36:25 [notice] 25932#0: *1 [lua] content_by_lua(nginx.conf:51):5: hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
```
from
```lua
ngx.log(ngx.NOTICE, "hello world")
```
the `errlog.raw_log()` call produces
```
2017/07/09 19:36:25 [notice] 25932#0: *1 hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
```
from
```lua
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.NOTICE, "hello world")
```
This function is best suited when the format and/or stack level of the debug
information proposed by `ngx.log` is not desired. A good example of this would
be a custom logging function which prefixes each log with a namespace in
an application:
```
1. local function my_log(lvl, ...)
2. ngx.log(lvl, "[prefix] ", ...)
3. end
4.
5. my_log(ngx.ERR, "error")
```
Here, the produced log would indicate that this error was logged at line `2.`,
when in reality, we wish the investigator of that log to realize it was logged
at line `5.` right away.
For such use cases (or other formatting reasons), one may use `raw_log` to
create a logging utility that supports such requirements. Here is a suggested
implementation:
```lua
local errlog = require "ngx.errlog"
local function my_log(lvl, ...)
-- log to error logs with our custom prefix, stack level
-- and separator
local n = select("#", ...)
local t = { ... }
local info = debug.getinfo(2, "Sl")
local prefix = string.format("(%s):%d:", info.short_src, info.currentline)
local buf = { prefix }
for i = 1, n do
buf[i + 1] = tostring(t[i])
end
local msg = table.concat(buf, " ")
errlog.raw_log(lvl, msg) -- line 19.
end
local function my_function()
-- do something and log
my_log(ngx.ERR, "hello from", "raw_log:", true) -- line 25.
end
my_function()
```
This utility function will produce the following log, explicitly stating that
the error was logged on line `25.`:
```
2017/07/09 20:03:07 [error] 26795#0: *2 (/path/to/file.lua):25: hello from raw_log: true, context: ngx.timer
```
As a reminder to the reader, one must be wary of the cost of string
concatenation on the Lua land, and should prefer the combined use of a buffer
table and `table.concat` to avoid unnecessary GC pressure.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Yuansheng Wang &lt;membphis@gmail.com&gt; (membphis), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

150
lib/ngx/ocsp.lua Normal file
View File

@ -0,0 +1,150 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http')
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local get_request = base.get_request
local error = error
local tonumber = tonumber
local errmsg = base.get_errmsg_ptr()
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local get_size_ptr = base.get_size_ptr
local FFI_DECLINED = base.FFI_DECLINED
local FFI_OK = base.FFI_OK
local FFI_BUSY = base.FFI_BUSY
ffi.cdef[[
int ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain(
const char *chain_data, size_t chain_len, char *out, size_t *out_size,
char **err);
int ngx_http_lua_ffi_ssl_create_ocsp_request(const char *chain_data,
size_t chain_len, unsigned char *out, size_t *out_size, char **err);
int ngx_http_lua_ffi_ssl_validate_ocsp_response(const unsigned char *resp,
size_t resp_len, const char *chain_data, size_t chain_len,
unsigned char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_ssl_set_ocsp_status_resp(ngx_http_request_t *r,
const unsigned char *resp, size_t resp_len, char **err);
]]
local _M = { version = base.version }
function _M.get_ocsp_responder_from_der_chain(certs, maxlen)
local buf_size = maxlen
if not buf_size then
buf_size = get_string_buf_size()
end
local buf = get_string_buf(buf_size)
local sizep = get_size_ptr()
sizep[0] = buf_size
local rc = C.ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain(certs,
#certs, buf, sizep, errmsg)
if rc == FFI_DECLINED then
return nil
end
if rc == FFI_OK then
return ffi_str(buf, sizep[0])
end
if rc == FFI_BUSY then
return ffi_str(buf, sizep[0]), "truncated"
end
return nil, ffi_str(errmsg[0])
end
function _M.create_ocsp_request(certs, maxlen)
local buf_size = maxlen
if not buf_size then
buf_size = get_string_buf_size()
end
local buf = get_string_buf(buf_size)
local sizep = get_size_ptr()
sizep[0] = buf_size
local rc = C.ngx_http_lua_ffi_ssl_create_ocsp_request(certs,
#certs, buf, sizep,
errmsg)
if rc == FFI_OK then
return ffi_str(buf, sizep[0])
end
if rc == FFI_BUSY then
return nil, ffi_str(errmsg[0]) .. ": " .. tonumber(sizep[0])
.. " > " .. buf_size
end
return nil, ffi_str(errmsg[0])
end
function _M.validate_ocsp_response(resp, chain, max_errmsg_len)
local errbuf_size = max_errmsg_len
if not errbuf_size then
errbuf_size = get_string_buf_size()
end
local errbuf = get_string_buf(errbuf_size)
local sizep = get_size_ptr()
sizep[0] = errbuf_size
local rc = C.ngx_http_lua_ffi_ssl_validate_ocsp_response(
resp, #resp, chain, #chain, errbuf, sizep)
if rc == FFI_OK then
return true
end
-- rc == FFI_ERROR
return nil, ffi_str(errbuf, sizep[0])
end
function _M.set_ocsp_status_resp(ocsp_resp)
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_ssl_set_ocsp_status_resp(r, ocsp_resp,
#ocsp_resp,
errmsg)
if rc == FFI_DECLINED then
-- no client status req
return true, "no status req"
end
if rc == FFI_OK then
return true
end
-- rc == FFI_ERROR
return nil, ffi_str(errmsg[0])
end
return _M

298
lib/ngx/ocsp.md Normal file
View File

@ -0,0 +1,298 @@
Name
====
ngx.ocsp - Lua API for implementing OCSP stapling in ssl_certificate_by_lua*
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [get_ocsp_responder_from_der_chain](#get_ocsp_responder_from_der_chain)
* [create_ocsp_request](#create_ocsp_request)
* [validate_ocsp_response](#validate_ocsp_response)
* [set_ocsp_status_resp](#set_ocsp_status_resp)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
```nginx
# Note: you do not need the following line if you are using
# OpenResty 1.9.7.2+.
lua_package_path "/path/to/lua-resty-core/lib/?.lua;;";
server {
listen 443 ssl;
server_name test.com;
# useless placeholders: just to shut up NGINX configuration
# loader errors:
ssl_certificate /path/to/fallback.crt;
ssl_certificate_key /path/to/fallback.key;
ssl_certificate_by_lua_block {
local ssl = require "ngx.ssl"
local ocsp = require "ngx.ocsp"
local http = require "resty.http.simple"
-- assuming the user already defines the my_load_certificate_chain()
-- herself.
local pem_cert_chain = assert(my_load_certificate_chain())
local der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)
if not der_cert_chain then
ngx.log(ngx.ERR, "failed to convert certificate chain ",
"from PEM to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local ocsp_url, err = ocsp.get_ocsp_responder_from_der_chain(der_cert_chain)
if not ocsp_url then
ngx.log(ngx.ERR, "failed to get OCSP responder: ", err)
return ngx.exit(ngx.ERROR)
end
print("ocsp_url: ", ocsp_url)
-- use cosocket-based HTTP client libraries like lua-resty-http-simple
-- to send the request (url + ocsp_req as POST params or URL args) to
-- CA's OCSP server. assuming the server returns the OCSP response
-- in the Lua variable, resp.
local schema, host, port, ocsp_uri, err = parse_url(ocsp_url)
local ocsp_req, err = ocsp.create_ocsp_request(der_cert_chain)
if not ocsp_req then
ngx.log(ngx.ERR, "failed to create OCSP request: ", err)
return ngx.exit(ngx.ERROR)
end
local res, err = http.request(host, port, {
path = ocsp_uri,
headers = { Host = host,
["Content-Type"] = "application/ocsp-request" },
timeout = 10000, -- 10 sec
method = "POST",
body = ocsp_req,
maxsize = 102400, -- 100KB
})
if not res then
ngx.log(ngx.ERR, "OCSP responder query failed: ", err)
return ngx.exit(ngx.ERROR)
end
local http_status = res.status
if http_status ~= 200 then
ngx.log(ngx.ERR, "OCSP responder returns bad HTTP status code ",
http_status)
return ngx.exit(ngx.ERROR)
end
local ocsp_resp = res.body
if ocsp_resp and #ocsp_resp > 0 then
local ok, err = ocsp.validate_ocsp_response(ocsp_resp, der_cert_chain)
if not ok then
ngx.log(ngx.ERR, "failed to validate OCSP response: ", err)
return ngx.exit(ngx.ERROR)
end
-- set the OCSP stapling
ok, err = ocsp.set_ocsp_status_resp(ocsp_resp)
if not ok then
ngx.log(ngx.ERR, "failed to set ocsp status resp: ", err)
return ngx.exit(ngx.ERROR)
end
end
}
location / {
root html;
}
}
```
Description
===========
This Lua module provides API to perform OCSP queries, OCSP response validations, and
OCSP stapling planting.
Usually, this module is used together with the [ngx.ssl](ssl.md) module in the
context of [ssl_certificate_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_certificate_by_lua_block)
(of the [ngx_lua](https://github.com/openresty/lua-nginx-module#readme) module).
To load the `ngx.ocsp` module in Lua, just write
```lua
local ocsp = require "ngx.ocsp"
```
[Back to TOC](#table-of-contents)
Methods
=======
get_ocsp_responder_from_der_chain
---------------------------------
**syntax:** *ocsp_url, err = ocsp.get_ocsp_responder_from_der_chain(der_cert_chain, max_len)*
**context:** *any*
Extracts the OCSP responder URL (like `"http://test.com/ocsp/"`) from the SSL server certificate chain in the DER format.
Usually the SSL server certificate chain is originally formatted in PEM. You can use the Lua API
provided by the [ngx.ssl](ssl.md) module to do the PEM to DER conversion.
The optional `max_len` argument specifies the maximum length of OCSP URL allowed. This determines
the buffer size; so do not specify an unnecessarily large value here. It defaults to the internal
string buffer size used throughout this `lua-resty-core` library (usually default to 4KB).
In case of failures, returns `nil` and a string describing the error.
[Back to TOC](#table-of-contents)
create_ocsp_request
-------------------
**syntax:** *ocsp_req, err = ocsp.create_ocsp_request(der_cert_chain, max_len)*
**context:** *any*
Builds an OCSP request from the SSL server certificate chain in the DER format, which
can be used to send to the OCSP server for validation.
The optional `max_len` argument specifies the maximum length of the OCSP request allowed.
This value determines the size of the internal buffer allocated, so do not specify an
unnecessarily large value here. It defaults to the internal string buffer size used
throughout this `lua-resty-core` library (usually defaults to 4KB).
In case of failures, returns `nil` and a string describing the error.
The raw OCSP response data can be used as the request body directly if the POST method
is used for the OCSP request. But for GET requests, you need to do base64 encoding and
then URL encoding on the data yourself before appending it to the OCSP URL obtained
by the [get_ocsp_responder_from_der_chain](#get_ocsp_responder_from_der_chain) function.
[Back to TOC](#table-of-contents)
validate_ocsp_response
----------------------
**syntax:** *ok, err = ocsp.validate_ocsp_response(ocsp_resp, der_cert_chain, max_err_msg_len)*
**context:** *any*
Validates the raw OCSP response data specified by the `ocsp_resp` argument using the SSL
server certificate chain in DER format as specified in the `der_cert_chain` argument.
Returns true when the validation is successful.
In case of failures, returns `nil` and a string
describing the failure. The maximum
length of the error string is controlled by the optional `max_err_msg` argument (which defaults
to the default internal string buffer size used throughout this `lua-resty-core` library, usually
being 4KB).
[Back to TOC](#table-of-contents)
set_ocsp_status_resp
--------------------
**syntax:** *ok, err = ocsp.set_ocsp_status_resp(ocsp_resp)*
**context:** *ssl_certificate_by_lua&#42;*
Sets the OCSP response as the OCSP stapling for the current SSL connection.
Returns `true` in case of successes. If the SSL client does not send a "status request"
at all, then this method still returns `true` but also with a string as the warning
`"no status req"`.
In case of failures, returns `nil` and a string describing the error.
The OCSP response is returned from CA's OCSP server. See the [create_ocsp_request](#create_ocsp_request)
function for how to create an OCSP request and also [validate_ocsp_response](#validate_ocsp_response)
for how to validate the OCSP response.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Yichun Zhang &lt;agentzh@gmail.com&gt; (agentzh), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2015-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* the [ngx.ssl](ssl.md) module.
* the [ssl_certificate_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_certificate_by_lua_block) directive.
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

645
lib/ngx/pipe.lua Normal file
View File

@ -0,0 +1,645 @@
-- Copyright (C) by OpenResty Inc.
local base = require "resty.core.base"
base.allows_subsystem("http")
require "resty.core.phase" -- for ngx.get_phase
local assert = assert
local error = error
local ipairs = ipairs
local tonumber = tonumber
local tostring = tostring
local type = type
local str_find = string.find
local table_concat = table.concat
local ffi = require "ffi"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ngx_phase = ngx.get_phase
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local get_request = base.get_request
local FFI_AGAIN = base.FFI_AGAIN
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_DECLINED = base.FFI_DECLINED
local FFI_ERROR = base.FFI_ERROR
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_OK = base.FFI_OK
local co_yield = coroutine._yield
ffi.cdef[[
typedef int ngx_pid_t;
typedef uintptr_t ngx_msec_t;
typedef unsigned char u_char;
typedef struct ngx_http_lua_pipe_s ngx_http_lua_pipe_t;
typedef struct {
ngx_pid_t _pid;
ngx_msec_t write_timeout;
ngx_msec_t stdout_read_timeout;
ngx_msec_t stderr_read_timeout;
ngx_msec_t wait_timeout;
ngx_http_lua_pipe_t *pipe;
} ngx_http_lua_ffi_pipe_proc_t;
int ngx_http_lua_ffi_pipe_spawn(ngx_http_request_t *r,
ngx_http_lua_ffi_pipe_proc_t *proc,
const char *file, const char **argv, int merge_stderr, size_t buffer_size,
const char **environ, u_char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_proc_read(ngx_http_request_t *r,
ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, int reader_type,
size_t length, u_char **buf, size_t *buf_size, u_char *errbuf,
size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_get_read_result(ngx_http_request_t *r,
ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, u_char **buf,
size_t *buf_size, u_char *errbuf, size_t *errbuf_size);
ssize_t ngx_http_lua_ffi_pipe_proc_write(ngx_http_request_t *r,
ngx_http_lua_ffi_pipe_proc_t *proc, const u_char *data, size_t len,
u_char *errbuf, size_t *errbuf_size);
ssize_t ngx_http_lua_ffi_pipe_get_write_result(ngx_http_request_t *r,
ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_proc_shutdown_stdin(
ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_proc_shutdown_stdout(
ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_proc_shutdown_stderr(
ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_proc_wait(ngx_http_request_t *r,
ngx_http_lua_ffi_pipe_proc_t *proc, char **reason, int *status,
u_char *errbuf, size_t *errbuf_size);
int ngx_http_lua_ffi_pipe_proc_kill(ngx_http_lua_ffi_pipe_proc_t *proc,
int signal, u_char *errbuf, size_t *errbuf_size);
void ngx_http_lua_ffi_pipe_proc_destroy(ngx_http_lua_ffi_pipe_proc_t *proc);
]]
if not pcall(function() return C.ngx_http_lua_ffi_pipe_spawn end) then
error("pipe API is not supported due to either a platform issue " ..
"or lack of the HAVE_SOCKET_CLOEXEC_PATCH patch", 2)
end
local _M = { version = base.version }
local ERR_BUF_SIZE = 256
local VALUE_BUF_SIZE = 512
local PIPE_READ_ALL = 0
local PIPE_READ_BYTES = 1
local PIPE_READ_LINE = 2
local PIPE_READ_ANY = 3
local proc_set_timeouts
do
local MAX_TIMEOUT = 0xffffffff
function proc_set_timeouts(proc, write_timeout, stdout_read_timeout,
stderr_read_timeout, wait_timeout)
-- the implementation below is straightforward but could not be JIT
-- compiled by the latest LuaJIT. When called in loops, LuaJIT will try
-- to unroll it, and fall back to interpreter after it reaches the
-- unroll limit.
--[[
local function set_timeout(proc, attr, timeout)
if timeout then
if timeout > MAX_TIMEOUT then
error("bad timeout value", 3)
end
proc[attr] = timeout
end
end
set_timeout(...)
]]
if write_timeout then
if write_timeout < 0 or MAX_TIMEOUT < write_timeout then
error("bad write_timeout option", 3)
end
proc.write_timeout = write_timeout
end
if stdout_read_timeout then
if stdout_read_timeout < 0 or MAX_TIMEOUT < stdout_read_timeout then
error("bad stdout_read_timeout option", 3)
end
proc.stdout_read_timeout = stdout_read_timeout
end
if stderr_read_timeout then
if stderr_read_timeout < 0 or MAX_TIMEOUT < stderr_read_timeout then
error("bad stderr_read_timeout option", 3)
end
proc.stderr_read_timeout = stderr_read_timeout
end
if wait_timeout then
if wait_timeout < 0 or MAX_TIMEOUT < wait_timeout then
error("bad wait_timeout option", 3)
end
proc.wait_timeout = wait_timeout
end
end
end
local function check_proc_instance(proc)
if type(proc) ~= "cdata" then
error("not a process instance", 3)
end
end
local proc_read
do
local value_buf = ffi_new("char[?]", VALUE_BUF_SIZE)
local buf = ffi_new("char *[1]")
local buf_size = ffi_new("size_t[1]")
function proc_read(proc, stderr, reader_type, len)
check_proc_instance(proc)
local r = get_request()
if not r then
error("no request found")
end
buf[0] = value_buf
buf_size[0] = VALUE_BUF_SIZE
local errbuf = get_string_buf(ERR_BUF_SIZE)
local errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_pipe_proc_read(r, proc, stderr,
reader_type, len, buf,
buf_size, errbuf,
errbuf_size)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error(ffi_str(errbuf, errbuf_size[0]), 2)
end
while true do
if rc == FFI_ERROR then
return nil, ffi_str(errbuf, errbuf_size[0])
end
if rc == FFI_OK then
local p = buf[0]
if p ~= value_buf then
p = ffi_new("char[?]", buf_size[0])
buf[0] = p
C.ngx_http_lua_ffi_pipe_get_read_result(r, proc, stderr,
buf, buf_size,
errbuf, errbuf_size)
assert(p == buf[0])
end
return ffi_str(p, buf_size[0])
end
if rc == FFI_DECLINED then
local err = ffi_str(errbuf, errbuf_size[0])
local p = buf[0]
if p ~= value_buf then
p = ffi_new("char[?]", buf_size[0])
buf[0] = p
C.ngx_http_lua_ffi_pipe_get_read_result(r, proc, stderr,
buf, buf_size,
errbuf, errbuf_size)
assert(p == buf[0])
end
local partial = ffi_str(p, buf_size[0])
return nil, err, partial
end
assert(rc == FFI_AGAIN)
co_yield()
buf[0] = value_buf
buf_size[0] = VALUE_BUF_SIZE
errbuf = get_string_buf(ERR_BUF_SIZE)
errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
rc = C.ngx_http_lua_ffi_pipe_get_read_result(r, proc, stderr, buf,
buf_size, errbuf,
errbuf_size)
end
end
end
local function proc_write(proc, data)
check_proc_instance(proc)
local r = get_request()
if not r then
error("no request found", 2)
end
local data_type = type(data)
if data_type ~= "string" then
if data_type == "table" then
data = table_concat(data, "")
elseif data_type == "number" then
data = tostring(data)
else
error("bad data arg: string, number, or table expected, got "
.. data_type, 2)
end
end
local errbuf = get_string_buf(ERR_BUF_SIZE)
local errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_pipe_proc_write(r, proc, data, #data, errbuf,
errbuf_size)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found", 2)
end
if rc == FFI_BAD_CONTEXT then
error(ffi_str(errbuf, errbuf_size[0]), 2)
end
while true do
if rc == FFI_ERROR then
return nil, ffi_str(errbuf, errbuf_size[0])
end
if rc >= 0 then
-- rc holds the bytes sent
return tonumber(rc)
end
assert(rc == FFI_AGAIN)
co_yield()
errbuf = get_string_buf(ERR_BUF_SIZE)
errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
rc = C.ngx_http_lua_ffi_pipe_get_write_result(r, proc, errbuf,
errbuf_size)
end
end
local function proc_shutdown(proc, direction)
check_proc_instance(proc)
local rc
local errbuf = get_string_buf(ERR_BUF_SIZE)
local errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
if direction == "stdin" then
rc = C.ngx_http_lua_ffi_pipe_proc_shutdown_stdin(proc, errbuf,
errbuf_size)
elseif direction == "stdout" then
rc = C.ngx_http_lua_ffi_pipe_proc_shutdown_stdout(proc, errbuf,
errbuf_size)
elseif direction == "stderr" then
rc = C.ngx_http_lua_ffi_pipe_proc_shutdown_stderr(proc, errbuf,
errbuf_size)
else
error("bad shutdown arg: " .. direction, 2)
end
if rc == FFI_ERROR then
return nil, ffi_str(errbuf, errbuf_size[0])
end
return true
end
local proc_wait
do
local reason = ffi_new("char *[1]")
local status = ffi_new("int[1]")
function proc_wait(proc)
check_proc_instance(proc)
local r = get_request()
if not r then
error("no request found", 2)
end
local errbuf = get_string_buf(ERR_BUF_SIZE)
local errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_pipe_proc_wait(r, proc, reason, status,
errbuf, errbuf_size)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found", 2)
end
if rc == FFI_BAD_CONTEXT then
error(ffi_str(errbuf, errbuf_size[0]), 2)
end
if rc == FFI_ERROR then
return nil, ffi_str(errbuf, errbuf_size[0])
end
if rc == FFI_OK then
return true, ffi_str(reason[0]), tonumber(status[0])
end
if rc == FFI_DECLINED then
return false, ffi_str(reason[0]), tonumber(status[0])
end
local ok, exit_reason, exit_status
ok, exit_reason, exit_status = co_yield()
return ok, exit_reason, exit_status
end
end
local function proc_kill(proc, signal)
check_proc_instance(proc)
if type(signal) ~= "number" then
error("bad signal arg: number expected, got " .. tostring(signal), 2)
end
local errbuf = get_string_buf(ERR_BUF_SIZE)
local errbuf_size = get_size_ptr()
errbuf_size[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_pipe_proc_kill(proc, signal, errbuf,
errbuf_size)
if rc == FFI_ERROR then
return nil, ffi_str(errbuf, errbuf_size[0])
end
return true
end
local mt = {
__gc = C.ngx_http_lua_ffi_pipe_proc_destroy,
__index = {
pid = function (proc)
return proc._pid
end,
set_timeouts = function (proc, write_timeout, stdout_read_timeout,
stderr_read_timeout, wait_timeout)
proc_set_timeouts(proc, write_timeout, stdout_read_timeout,
stderr_read_timeout, wait_timeout)
end,
stdout_read_all = function (proc)
local data, err, partial = proc_read(proc, 0, PIPE_READ_ALL, 0)
return data, err, partial
end,
stdout_read_bytes = function (proc, len)
if len <= 0 then
if len < 0 then
error("bad len argument", 2)
end
return ""
end
local data, err, partial = proc_read(proc, 0, PIPE_READ_BYTES, len)
return data, err, partial
end,
stdout_read_line = function (proc)
local data, err, partial = proc_read(proc, 0, PIPE_READ_LINE, 0)
return data, err, partial
end,
stdout_read_any = function (proc, max)
if type(max) ~= "number" then
max = tonumber(max)
end
if not max or max <= 0 then
error("bad max argument", 2)
end
local data, err, partial = proc_read(proc, 0, PIPE_READ_ANY, max)
return data, err, partial
end,
stderr_read_all = function (proc)
local data, err, partial = proc_read(proc, 1, PIPE_READ_ALL, 0)
return data, err, partial
end,
stderr_read_bytes = function (proc, len)
if len <= 0 then
if len < 0 then
error("bad len argument", 2)
end
return ""
end
local data, err, partial = proc_read(proc, 1, PIPE_READ_BYTES, len)
return data, err, partial
end,
stderr_read_line = function (proc)
local data, err, partial = proc_read(proc, 1, PIPE_READ_LINE, 0)
return data, err, partial
end,
stderr_read_any = function (proc, max)
if type(max) ~= "number" then
max = tonumber(max)
end
if not max or max <= 0 then
error("bad max argument", 2)
end
local data, err, partial = proc_read(proc, 1, PIPE_READ_ANY, max)
return data, err, partial
end,
write = proc_write,
shutdown = proc_shutdown,
wait = proc_wait,
kill = proc_kill,
}
}
local Proc = ffi.metatype("ngx_http_lua_ffi_pipe_proc_t", mt)
local pipe_spawn
do
local sh_exe = "/bin/sh"
local opt_c = "-c"
local shell_args = ffi_new("const char* [?]", 4)
shell_args[0] = sh_exe
shell_args[1] = opt_c
shell_args[3] = nil
local write_timeout = 10000
local stdout_read_timeout = 10000
local stderr_read_timeout = 10000
local wait_timeout = 10000
-- reference shell cmd's constant strings here to prevent them from getting
-- collected by the Lua GC.
_M._gc_ref_c_opt = opt_c
function pipe_spawn(args, opts)
if ngx_phase() == "init" then
error("API disabled in the current context", 2)
end
local exe
local proc_args
local proc_envs
local args_type = type(args)
if args_type == "table" then
local nargs = 0
for i, arg in ipairs(args) do
nargs = nargs + 1
if type(arg) ~= "string" then
args[i] = tostring(arg)
end
end
if nargs == 0 then
error("bad args arg: non-empty table expected", 2)
end
exe = args[1]
proc_args = ffi_new("const char* [?]", nargs + 1, args)
proc_args[nargs] = nil
elseif args_type == "string" then
exe = sh_exe
shell_args[2] = args
proc_args = shell_args
else
error("bad args arg: table expected, got " .. args_type, 2)
end
local merge_stderr = 0
local buffer_size = 4096
local proc = Proc()
if opts then
merge_stderr = opts.merge_stderr and 1 or 0
if opts.buffer_size then
buffer_size = tonumber(opts.buffer_size)
if not buffer_size or buffer_size < 1 then
error("bad buffer_size option", 2)
end
end
if opts.environ then
local environ = opts.environ
local environ_type = type(environ)
if environ_type ~= "table" then
error("bad environ option: table expected, got " ..
environ_type, 2)
end
local nenv = 0
for i, env in ipairs(environ) do
nenv = nenv + 1
local env_type = type(env)
if env_type ~= "string" then
error("bad value at index " .. i .. " of environ " ..
"option: string expected, got " .. env_type, 2)
end
if not str_find(env, "=", 2, true) then
error("bad value at index " .. i .. " of environ " ..
"option: 'name=[value]' format expected, got '" ..
env .. "'", 2)
end
end
if nenv > 0 then
proc_envs = ffi_new("const char* [?]", nenv + 1, environ)
proc_envs[nenv] = nil
end
end
proc_set_timeouts(proc,
opts.write_timeout or write_timeout,
opts.stdout_read_timeout or stdout_read_timeout,
opts.stderr_read_timeout or stderr_read_timeout,
opts.wait_timeout or wait_timeout)
else
proc_set_timeouts(proc,
write_timeout,
stdout_read_timeout,
stderr_read_timeout,
wait_timeout)
end
local errbuf = get_string_buf(ERR_BUF_SIZE)
local errbuf_size = get_size_ptr()
local r = get_request()
errbuf_size[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_pipe_spawn(r, proc, exe, proc_args,
merge_stderr, buffer_size,
proc_envs, errbuf, errbuf_size)
if rc == FFI_ERROR then
return nil, ffi_str(errbuf, errbuf_size[0])
end
return proc
end
end -- do
_M.spawn = pipe_spawn
return _M

584
lib/ngx/pipe.md Normal file
View File

@ -0,0 +1,584 @@
Name
====
`ngx.pipe` - spawn and communicate with OS processes via stdin/stdout/stderr in
a non-blocking fashion.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [spawn](#spawn)
* [set_timeouts](#set_timeouts)
* [wait](#wait)
* [pid](#pid)
* [kill](#kill)
* [shutdown](#shutdown)
* [write](#write)
* [stderr_read_all](#stderr_read_all)
* [stdout_read_all](#stdout_read_all)
* [stderr_read_line](#stderr_read_line)
* [stdout_read_line](#stdout_read_line)
* [stderr_read_bytes](#stderr_read_bytes)
* [stdout_read_bytes](#stdout_read_bytes)
* [stderr_read_any](#stderr_read_any)
* [stdout_read_any](#stdout_read_any)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
```nginx
location = /t {
content_by_lua_block {
local ngx_pipe = require "ngx.pipe"
local select = select
local function count_char(...)
local proc = ngx_pipe.spawn({'wc', '-c'})
local n = select('#', ...)
for i = 1, n do
local arg = select(i, ...)
local bytes, err = proc:write(arg)
if not bytes then
ngx.say(err)
return
end
end
local ok, err = proc:shutdown('stdin')
if not ok then
ngx.say(err)
return
end
local data, err = proc:stdout_read_line()
if not data then
ngx.say(err)
return
end
ngx.say(data)
end
count_char(("1234"):rep(2048))
}
}
```
This example counts characters (bytes) directly fed by OpenResty to the UNIX
command `wc`.
You could not do this with either `io.popen` or `os.execute` because `wc` will
not output the result until its stdin is closed.
[Back to TOC](#table-of-contents)
Description
===========
This module does not support non-POSIX operating systems like Windows yet.
If you are not using the Nginx core shipped with OpenResty, you will need to
apply the `socket_cloexec` patch to the standard Nginx core.
Under the hood, this module uses `fork` and `execvp` with the user-specified
command, and communicate with such spawned processes via the POSIX `pipe` API,
which contributes to the name of this module.
A signal handler for `SIGCHLD` is registered so that we can receive a
notification once the spawned processes exited.
We combine the above implementation with Nginx's event mechanism and
OpenResty's Lua coroutine scheduler, in order to ensure communication with the
spawned processes is non-blocking.
The communication APIs do not work in phases which do not support yielding,
such as `init_worker_by_lua*` or `log_by_lua*`, because there is no way to
yield the current light thread to avoid blocking the OS thread when
communicating with processes in those phases.
[Back to TOC](#table-of-contents)
Methods
=======
spawn
-----
**syntax:** *proc, err = pipe_module.spawn(args, opts?)*
**context:** *all phases except init_by_lua&#42;*
Creates and returns a new sub-process instance we can communicate with later.
For example:
```lua
local ngx_pipe = require "ngx.pipe"
local proc, err = ngx_pipe.spawn({"sh", "-c", "sleep 0.1 && exit 2"})
if not proc then
ngx.say(err)
return
end
```
In case of failure, this function returns `nil` and a string describing the
error.
The sub-process will be killed via `SIGKILL` if it is still alive when the
instance is collected by the garbage collector.
Note that `args` should either be a single level array-like Lua table with
string values, or just a single string.
Some more examples:
```lua
local proc, err = ngx_pipe.spawn({"ls", "-l"})
local proc, err = ngx_pipe.spawn({"perl", "-e", "print 'hello, wolrd'"})
```
If `args` is specified as a string, it will be executed by the operating system
shell, just like `os.execute`. The above example could thus be rewritten as:
```lua
local ngx_pipe = require "ngx.pipe"
local proc, err = ngx_pipe.spawn("sleep 0.1 && exit 2")
if not proc then
ngx.say(err)
return
end
```
In the shell mode, you should be very careful about shell injection attacks
when interpolating variables into command string, especially variables from
untrusted sources. Please make sure that you escape those variables while
assembling the command string. For this reason, it is highly recommended to use
the multi-arguments form (`args` as a table) to specify each command-line
argument explicitly.
Since by default, Nginx does not pass along the `PATH` system environment
variable, you will need to configure the `env PATH` directive if you wish for
it to be respected during the searching of sub-processes:
```nginx
env PATH;
...
content_by_lua_block {
local ngx_pipe = require "ngx.pipe"
local proc = ngx_pipe.spawn({'ls'})
}
```
The optional table argument `opts` can be used to control the behavior of
spawned processes. For instance:
```lua
local opts = {
merge_stderr = true,
buffer_size = 256,
environ = {"PATH=/tmp/bin", "CWD=/tmp/work"}
}
local proc, err = ngx_pipe.spawn({"sh", "-c", ">&2 echo data"}, opts)
if not proc then
ngx.say(err)
return
end
```
The following options are supported:
* `merge_stderr`: when set to `true`, the output to stderr will be redirected
to stdout in the spawned process. This is similar to doing `2>&1` in a shell.
* `buffer_size`: specifies the buffer size used by reading operations, in
bytes. The default buffer size is `4096`.
* `environ`: specifies environment variables for the spawned process. The value
must be a single-level, array-like Lua table with string values. If the
current platform does not support this option, `nil` plus a string `"environ
option not supported"` will be returned.
* `write_timeout`: specifies the write timeout threshold, in milliseconds. The
default threshold is `10000`. If the threshold is `0`, the write operation
will never time out.
* `stdout_read_timeout`: specifies the stdout read timeout threshold, in
milliseconds. The default threshold is `10000`. If the threshold is `0`, the
stdout read operation will never time out.
* `stderr_read_timeout`: specifies the stderr read timeout threshold, in
milliseconds. The default threshold is `10000`. If the threshold is `0`, the
stderr read operation will never time out.
* `wait_timeout`: specifies the wait timeout threshold, in milliseconds. The
default threshold is `10000`. If the threshold is `0`, the wait operation
will never time out.
[Back to TOC](#table-of-contents)
set_timeouts
------------
**syntax:** *proc:set_timeouts(write_timeout?, stdout_read_timeout?, stderr_read_timeout?, wait_timeout?)*
Respectively sets: the write timeout threshold, stdout read timeout threshold,
stderr read timeout threshold, and wait timeout threshold. All timeouts are in
milliseconds.
The default threshold for each timeout is 10 seconds.
If the specified timeout argument is `nil`, the corresponding timeout threshold
will not be changed. For example:
```lua
local proc, err = ngx_pipe.spawn({"sleep", "10s"})
-- only change the wait_timeout to 0.1 second.
proc:set_timeouts(nil, nil, nil, 100)
-- only change the send_timeout to 0.1 second.
proc:set_timeouts(100)
```
If the specified timeout argument is `0`, the corresponding operation will
never time out.
[Back to TOC](#table-of-contents)
wait
----
**syntax:** *ok, reason, status = proc:wait()*
**context:** *phases that support yielding*
Waits until the current sub-process exits.
It is possible to control how long to wait via [set_timeouts](#set_timeouts).
The default timeout is 10 seconds.
If process exited with status code zero, the `ok` return value will be `true`.
If process exited abnormally, the `ok` return value will be `false`.
The second return value, `reason`, will be a string. Its values may be:
* `exit`: the process exited by calling `exit(3)`, `_exit(2)`, or by
returning from `main()`. In this case, `status` will be the exit code.
* `signal`: the process was terminated by a signal. In this case, `status` will
be the signal number.
Note that only one light thread can wait on a process at a time. If another
light thread tries to wait on a process, the return values will be `nil` and
the error string `"pipe busy waiting"`.
If a thread tries to wait an exited process, the return values will be `nil`
and the error string `"exited"`.
[Back to TOC](#table-of-contents)
pid
---
**syntax:** *pid = proc:pid()*
Returns the pid number of the sub-process.
[Back to TOC](#table-of-contents)
kill
----
**syntax:** *ok, err = proc:kill(signum)*
Sends a signal to the sub-process.
Note that the `signum` argument should be signal's numerical value. If the
specified `signum` is not a number, an error will be thrown.
You should use [lua-resty-signal's signum()
function](https://github.com/openresty/lua-resty-signal#signum) to convert
signal names to signal numbers in order to ensure portability of your
application.
In case of success, this method returns `true`. Otherwise, it returns `nil` and
a string describing the error.
Killing an exited sub-process will return `nil` and the error string
`"exited"`.
Sending an invalid signal to the process will return `nil` and the error string
`"invalid signal"`.
[Back to TOC](#table-of-contents)
shutdown
--------
**syntax:** *ok, err = proc:shutdown(direction)*
Closes the specified direction of the current sub-process.
The `direction` argument should be one of these three values: `stdin`, `stdout`
and `stderr`.
In case of success, this method returns `true`. Otherwise, it returns `nil` and
a string describing the error.
If the `merge_stderr` option is specified in [spawn](#spawn), closing the
`stderr` direction will return `nil` and the error string `"merged to stdout"`.
Shutting down a direction when a light thread is waiting on it (such as during
reading or writing) will abort the light thread and return `true`.
Shutting down directions of an exited process will return `nil` and the error
string `"closed"`.
It is fine to shut down the same direction of the same stream multiple times;
no side effects are to be expected.
[Back to TOC](#table-of-contents)
write
-----
**syntax:** *nbytes, err = proc:write(data)*
**context:** *phases that support yielding*
Writes data to the current sub-process's stdin stream.
The `data` argument can be a string or a single level array-like Lua table with
string values.
This method is a synchronous and non-blocking operation that will not return
until *all* the data has been flushed to the sub-process's stdin buffer, or
an error occurs.
In case of success, it returns the total number of bytes that have been sent.
Otherwise, it returns `nil` and a string describing the error.
The timeout threshold of this `write` operation can be controlled by the
[set_timeouts](#set_timeouts) method. The default timeout threshold is 10
seconds.
When a timeout occurs, the data may be partially written into the sub-process's
stdin buffer and read by the sub-process.
Only one light thread is allowed to write to the sub-process at a time. If
another light thread tries to write to it, this method will return `nil` and
the error string `"pipe busy writing"`.
If the `write` operation is aborted by the [shutdown](#shutdown) method,
it will return `nil` and the error string `"aborted"`.
Writing to an exited sub-process will return `nil` and the error string
`"closed"`.
[Back to TOC](#table-of-contents)
stderr_read_all
---------------
**syntax:** *data, err, partial = proc:stderr_read_all()*
**context:** *phases that support yielding*
Reads all data from the current sub-process's stderr stream until it is closed.
This method is a synchronous and non-blocking operation, just like the
[write](#write) method.
The timeout threshold of this reading operation can be controlled by
[set_timeouts](#set_timeouts). The default timeout is 10 seconds.
In case of success, it returns the data received. Otherwise, it returns three
values: `nil`, a string describing the error, and, optionally, the partial data
received so far.
When `merge_stderr` is specified in [spawn](#spawn), calling `stderr_read_all`
will return `nil` and the error string `"merged to stdout"`.
Only one light thread is allowed to read from a sub-process's stderr or stdout
stream at a time. If another thread tries to read from the same stream, this
method will return `nil` and the error string `"pipe busy reading"`.
If the reading operation is aborted by the [shutdown](#shutdown) method,
it will return `nil` and the error string `"aborted"`.
Streams for stdout and stderr are separated, so at most two light threads may
be reading from a sub-process at a time (one for each stream).
The same way, a light thread may read from a stream while another light thread
is writing to the sub-process stdin stream.
Reading from an exited process's stream will return `nil` and the error string
`"closed"`.
[Back to TOC](#table-of-contents)
stdout_read_all
---------------
**syntax:** *data, err, partial = proc:stdout_read_all()*
**context:** *phases that support yielding*
Similar to the [stderr_read_all](#stderr_read_all) method, but reading from the
stdout stream of the sub-process.
[Back to TOC](#table-of-contents)
stderr_read_line
----------------
**syntax:** *data, err, partial = proc:stderr_read_line()*
**context:** *phases that support yielding*
Reads from stderr like [stderr_read_all](#stderr_read_all), but only reads a
single line of data.
When `merge_stderr` is specified in [spawn](#spawn), calling `stderr_read_line`
will return `nil` plus the error string `"merged to stdout"`.
When the data stream is truncated without a new-line character, it returns 3
values: `nil`, the error string `"closed"`, and the partial data received so
far.
The line should be terminated by a `Line Feed` (LF) character (ASCII 10),
optionally preceded by a `Carriage Return` (CR) character (ASCII 13). The CR
and LF characters are not included in the returned line data.
[Back to TOC](#table-of-contents)
stdout_read_line
----------------
**syntax:** *data, err, partial = proc:stdout_read_line()*
**context:** *phases that support yielding*
Similar to [stderr_read_line](#stderr_read_line), but reading from the
stdout stream of the sub-process.
[Back to TOC](#table-of-contents)
stderr_read_bytes
-----------------
**syntax:** *data, err, partial = proc:stderr_read_bytes(len)*
**context:** *phases that support yielding*
Reads from stderr like [stderr_read_all](#stderr_read_all), but only reads the
specified number of bytes.
If `merge_stderr` is specified in [spawn](#spawn), calling `stderr_read_bytes`
will return `nil` plus the error string `"merged to stdout"`.
If the data stream is truncated (fewer bytes of data available than requested),
this method returns 3 values: `nil`, the error string `"closed"`, and the
partial data string received so far.
[Back to TOC](#table-of-contents)
stdout_read_bytes
-----------------
**syntax:** *data, err, partial = proc:stdout_read_bytes(len)*
**context:** *phases that support yielding*
Similar to [stderr_read_bytes](#stderr_read_bytes), but reading from the
stdout stream of the sub-process.
[Back to TOC](#table-of-contents)
stderr_read_any
---------------
**syntax:** *data, err = proc:stderr_read_any(max)*
**context:** *phases that support yielding*
Reads from stderr like [stderr_read_all](#stderr_read_all), but returns
immediately when any amount of data is received.
At most `max` bytes are received.
If `merge_stderr` is specified in [spawn](#spawn), calling `stderr_read_any`
will return `nil` plus the error string `"merged to stdout"`.
If the received data is more than `max` bytes, this method will return with
exactly `max` bytes of data. The remaining data in the underlying receive
buffer can be fetched with a subsequent reading operation.
[Back to TOC](#table-of-contents)
stdout_read_any
---------------
**syntax:** *data, err = proc:stdout_read_any(max)*
**context:** *phases that support yielding*
Similar to [stderr_read_any](#stderr_read_any), but reading from the stdout
stream of the sub-process.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list
is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for
Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2018, by OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

115
lib/ngx/process.lua Normal file
View File

@ -0,0 +1,115 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local ffi = require 'ffi'
local errmsg = base.get_errmsg_ptr()
local FFI_ERROR = base.FFI_ERROR
local ffi_str = ffi.string
local tonumber = tonumber
local subsystem = ngx.config.subsystem
if subsystem == 'http' then
require "resty.core.phase" -- for ngx.get_phase
end
local ngx_phase = ngx.get_phase
local process_type_names = {
[0 ] = "single",
[1 ] = "master",
[2 ] = "signaller",
[3 ] = "worker",
[4 ] = "helper",
[99] = "privileged agent",
}
local C = ffi.C
local _M = { version = base.version }
local ngx_lua_ffi_enable_privileged_agent
local ngx_lua_ffi_get_process_type
local ngx_lua_ffi_process_signal_graceful_exit
local ngx_lua_ffi_master_pid
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_enable_privileged_agent(char **err,
unsigned int connections);
int ngx_http_lua_ffi_get_process_type(void);
void ngx_http_lua_ffi_process_signal_graceful_exit(void);
int ngx_http_lua_ffi_master_pid(void);
]]
ngx_lua_ffi_enable_privileged_agent =
C.ngx_http_lua_ffi_enable_privileged_agent
ngx_lua_ffi_get_process_type = C.ngx_http_lua_ffi_get_process_type
ngx_lua_ffi_process_signal_graceful_exit =
C.ngx_http_lua_ffi_process_signal_graceful_exit
ngx_lua_ffi_master_pid = C.ngx_http_lua_ffi_master_pid
else
ffi.cdef[[
int ngx_stream_lua_ffi_enable_privileged_agent(char **err,
unsigned int connections);
int ngx_stream_lua_ffi_get_process_type(void);
void ngx_stream_lua_ffi_process_signal_graceful_exit(void);
int ngx_stream_lua_ffi_master_pid(void);
]]
ngx_lua_ffi_enable_privileged_agent =
C.ngx_stream_lua_ffi_enable_privileged_agent
ngx_lua_ffi_get_process_type = C.ngx_stream_lua_ffi_get_process_type
ngx_lua_ffi_process_signal_graceful_exit =
C.ngx_stream_lua_ffi_process_signal_graceful_exit
ngx_lua_ffi_master_pid = C.ngx_stream_lua_ffi_master_pid
end
function _M.type()
local typ = ngx_lua_ffi_get_process_type()
return process_type_names[tonumber(typ)]
end
function _M.enable_privileged_agent(connections)
if ngx_phase() ~= "init" then
return nil, "API disabled in the current context"
end
connections = connections or 512
if type(connections) ~= "number" or connections < 0 then
return nil, "bad 'connections' argument: " ..
"number expected and greater than 0"
end
local rc = ngx_lua_ffi_enable_privileged_agent(errmsg, connections)
if rc == FFI_ERROR then
return nil, ffi_str(errmsg[0])
end
return true
end
function _M.signal_graceful_exit()
ngx_lua_ffi_process_signal_graceful_exit()
end
function _M.get_master_pid()
local pid = ngx_lua_ffi_master_pid()
if pid == FFI_ERROR then
return nil
end
return tonumber(pid)
end
return _M

232
lib/ngx/process.md Normal file
View File

@ -0,0 +1,232 @@
Name
====
`ngx.process` - manage the nginx processes for OpenResty/ngx_lua.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Functions](#functions)
* [type](#type)
* [enable_privileged_agent](#enable_privileged_agent)
* [signal_graceful_exit](#signal_graceful_exit)
* [get_master_pid](#get_master_pid)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
The API is still in flux and may change in the future without notice.
Synopsis
========
Enables privileged agent process, gets process type, and then gets the master process PID:
```nginx
# http config
init_by_lua_block {
local process = require "ngx.process"
-- enables privileged agent process
local ok, err = process.enable_privileged_agent()
if not ok then
ngx.log(ngx.ERR, "enables privileged agent failed error:", err)
end
-- output process type
ngx.log(ngx.INFO, "process type: ", process.type())
}
init_worker_by_lua_block {
local process = require "ngx.process"
ngx.log(ngx.INFO, "process type: ", process.type())
}
server {
# ...
location = /t {
content_by_lua_block {
local process = require "ngx.process"
ngx.say("process type: ", process.type())
ngx.say("master process pid: ", process.get_master_pid() or "-")
}
}
}
```
The example config above produces an output to `error.log` when
server starts:
```
[lua] init_by_lua:11: process type: master
[lua] init_worker_by_lua:3: process type: privileged agent
[lua] init_worker_by_lua:3: process type: worker
```
The example location above produces the following response body:
```
process type: worker
master process pid: 8261
```
[Back to TOC](#table-of-contents)
Functions
=========
type
----
**syntax:** *type_name = process_module.type()*
**context:** *any*
Returns the type of the current Nginx process. Depending on the calling context
and current process, the type can be one of:
* `master`: returned when this function is called from within the master
process
* `worker`: returned when this function is called from within a worker process
* `single`: returned when Nginx is running in the single process mode
* `signaller`: returned when Nginx is running as a signaller process
* `privileged agent`: returned when this funtion is called from within a
privileged agent process
For example:
```lua
local process = require "ngx.process"
ngx.say("process type:", process.type()) -- RESPONSE: worker
```
[Back to TOC](#table-of-contents)
enable_privileged_agent
-----------------------
**syntax:** *ok, err = process_module.enable_privileged_agent(connections)*
**context:** *init_by_lua&#42;*
Enables the privileged agent process in Nginx.
The privileged agent process does not listen on any virtual server ports like those worker processes.
And it uses the same system account as the nginx master process, which is usually a privileged account
like `root`.
The `init_worker_by_lua*` directive handler still runs in the privileged agent process. And one can
use the [type](#type) function provided by this module to check if the current process is a privileged
agent.
The argument connections sets the maximum number of simultaneous connections that can be opened by privileged agent process.
In case of failures, returns `nil` and a string describing the error.
[Back to TOC](#table-of-contents)
signal_graceful_exit
--------------------
**syntax:** *process_module.signal_graceful_exit()*
**context:** *any*
Signals the *current* nginx (worker) process to quit gracefully, i.e., after all the timers have expired (in time or expired prematurely).
Note that this API function simply sets the nginx global C variable `ngx_quit` to signal the nginx event
loop directly. No UNIX signals or IPC are involved here.
WARNING: the official NGINX core does not perform the graceful exiting procedure when the [master_process](http://nginx.org/r/master_process)
directive is turned `off`. The OpenResty's NGINX core has a
[custom patch](https://github.com/openresty/openresty/blob/master/patches/nginx-1.11.2-single_process_graceful_exit.patch)
applied, which fixes this issue.
[Back to TOC](#table-of-contents)
get_master_pid
--------------
**syntax:** *pid = process_module.get_master_pid()*
**context:** *any*
Returns a number value for the nginx master process's process ID (or PID).
This function requires NGINX 1.13.8+ cores to work properly. Otherwise it returns `nil`.
This feature first appeared in lua-resty-core v0.1.14.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Yuansheng Wang &lt;membphis@gmail.com&gt; (membphis), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

318
lib/ngx/re.lua Normal file
View File

@ -0,0 +1,318 @@
-- I hereby assign copyright in this code to the lua-resty-core project,
-- to be licensed under the same terms as the rest of the code.
local base = require "resty.core.base"
local ffi = require 'ffi'
local bit = require "bit"
local core_regex = require "resty.core.regex"
if core_regex.no_pcre then
error("no support for 'ngx.re' module: OpenResty was " ..
"compiled without PCRE support", 3)
end
local C = ffi.C
local ffi_str = ffi.string
local sub = string.sub
local error = error
local type = type
local band = bit.band
local new_tab = base.new_tab
local tostring = tostring
local math_max = math.max
local math_min = math.min
local is_regex_cache_empty = core_regex.is_regex_cache_empty
local re_match_compile = core_regex.re_match_compile
local destroy_compiled_regex = core_regex.destroy_compiled_regex
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local FFI_OK = base.FFI_OK
local subsystem = ngx.config.subsystem
local MAX_ERR_MSG_LEN = 128
local FLAG_DFA = 0x02
local PCRE_ERROR_NOMATCH = -1
local DEFAULT_SPLIT_RES_SIZE = 4
local split_ctx = new_tab(0, 1)
local ngx_lua_ffi_set_jit_stack_size
local ngx_lua_ffi_exec_regex
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_set_jit_stack_size(int size, unsigned char *errstr,
size_t *errstr_size);
]]
ngx_lua_ffi_exec_regex = C.ngx_http_lua_ffi_exec_regex
ngx_lua_ffi_set_jit_stack_size = C.ngx_http_lua_ffi_set_jit_stack_size
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_set_jit_stack_size(int size, unsigned char *errstr,
size_t *errstr_size);
]]
ngx_lua_ffi_exec_regex = C.ngx_stream_lua_ffi_exec_regex
ngx_lua_ffi_set_jit_stack_size = C.ngx_stream_lua_ffi_set_jit_stack_size
end
local _M = { version = base.version }
local function re_split_helper(subj, compiled, compile_once, flags, ctx)
local rc
do
local pos = math_max(ctx.pos, 0)
rc = ngx_lua_ffi_exec_regex(compiled, flags, subj, #subj, pos)
end
if rc == PCRE_ERROR_NOMATCH then
return nil, nil, nil
end
if rc < 0 then
if not compile_once then
destroy_compiled_regex(compiled)
end
return nil, nil, nil, "pcre_exec() failed: " .. rc
end
if rc == 0 then
if band(flags, FLAG_DFA) == 0 then
if not compile_once then
destroy_compiled_regex(compiled)
end
return nil, nil, nil, "capture size too small"
end
rc = 1
end
local caps = compiled.captures
local ncaps = compiled.ncaptures
local from = caps[0]
local to = caps[1]
if from < 0 or to < 0 then
return nil, nil, nil
end
if from == to then
-- empty match, skip to next char
ctx.pos = to + 1
else
ctx.pos = to
end
-- convert to Lua string indexes
from = from + 1
to = to + 1
-- retrieve the first sub-match capture if any
if ncaps > 0 and rc > 1 then
return from, to, sub(subj, caps[2] + 1, caps[3])
end
return from, to
end
function _M.split(subj, regex, opts, ctx, max, res)
-- we need to cast this to strings to avoid exceptions when they are
-- something else.
-- needed because of further calls to string.sub in this function.
subj = tostring(subj)
if not ctx then
ctx = split_ctx
ctx.pos = 1 -- set or reset upvalue field
elseif not ctx.pos then
-- ctx provided by user but missing pos field
ctx.pos = 1
end
max = max or 0
if not res then
-- limit the initial arr_n size of res to a reasonable value
-- 0 < narr <= DEFAULT_SPLIT_RES_SIZE
local narr = DEFAULT_SPLIT_RES_SIZE
if max > 0 then
-- the user specified a valid max limiter if max > 0
narr = math_min(narr, max)
end
res = new_tab(narr, 0)
elseif type(res) ~= "table" then
error("res is not a table", 2)
end
local len = #subj
if ctx.pos > len then
res[1] = nil
return res
end
-- compile regex
local compiled, compile_once, flags = re_match_compile(regex, opts)
if compiled == nil then
-- compiled_once holds the error string
return nil, compile_once
end
local sub_idx = ctx.pos
local res_idx = 0
local last_empty_match
-- update to split_helper PCRE indexes
ctx.pos = sub_idx - 1
-- splitting: with and without a max limiter
if max > 0 then
local count = 1
while count < max do
local from, to, capture, err = re_split_helper(subj, compiled,
compile_once, flags, ctx)
if err then
return nil, err
end
if not from then
break
end
if last_empty_match then
sub_idx = last_empty_match
end
if from == to then
last_empty_match = from
end
if from > sub_idx or not last_empty_match then
count = count + 1
res_idx = res_idx + 1
res[res_idx] = sub(subj, sub_idx, from - 1)
if capture then
res_idx = res_idx + 1
res[res_idx] = capture
end
sub_idx = to
if sub_idx > len then
break
end
end
end
else
while true do
local from, to, capture, err = re_split_helper(subj, compiled,
compile_once, flags, ctx)
if err then
return nil, err
end
if not from then
break
end
if last_empty_match then
sub_idx = last_empty_match
end
if from == to then
last_empty_match = from
end
if from > sub_idx or not last_empty_match then
res_idx = res_idx + 1
res[res_idx] = sub(subj, sub_idx, from - 1)
if capture then
res_idx = res_idx + 1
res[res_idx] = capture
end
sub_idx = to
if sub_idx > len then
break
end
end
end
end
if not compile_once then
destroy_compiled_regex(compiled)
end
-- trailing nil for non-cleared res tables
-- delete empty trailing ones (without max)
if max <= 0 and sub_idx > len then
for ety_idx = res_idx, 1, -1 do
if res[ety_idx] ~= "" then
res_idx = ety_idx
break
end
res[ety_idx] = nil
end
else
res_idx = res_idx + 1
res[res_idx] = sub(subj, sub_idx)
end
res[res_idx + 1] = nil
return res
end
function _M.opt(option, value)
if option == "jit_stack_size" then
if not is_regex_cache_empty() then
error("changing jit stack size is not allowed when some " ..
"regexs have already been compiled and cached", 2)
end
local errbuf = get_string_buf(MAX_ERR_MSG_LEN)
local sizep = get_size_ptr()
sizep[0] = MAX_ERR_MSG_LEN
local rc = ngx_lua_ffi_set_jit_stack_size(value, errbuf, sizep)
if rc == FFI_OK then
return
end
error(ffi_str(errbuf, sizep[0]), 2)
end
error("unrecognized option name", 2)
end
return _M

249
lib/ngx/re.md Normal file
View File

@ -0,0 +1,249 @@
Name
====
ngx.re - Lua API for convenience utilities for `ngx.re`.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [split](#split)
* [opt](#opt)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
```lua
local ngx_re = require "ngx.re"
-- split
local res, err = ngx_re.split("a,b,c,d", ",")
--> res is now {"a", "b", "c", "d"}
-- opt
ngx_re.opt("jit_stack_size", 128 * 1024)
--> the PCRE jit stack can now handle more complex regular expressions
```
[Back to TOC](#table-of-contents)
Description
===========
This Lua module provides a Lua API which implements convenience utilities for
the `ngx.re` API.
[Back to TOC](#table-of-contents)
Methods
=======
All the methods of this module are static (or module-level). That is, you do
not need an object (or instance) to call these methods.
[Back to TOC](#table-of-contents)
split
-----
**syntax:** *res, err = ngx_re.split(subject, regex, options?, ctx?, max?, res?)*
Splits the `subject` string using the Perl compatible regular expression
`regex` with the optional `options`.
This function returns a Lua (array) table (with integer keys) containing the
split values.
In case of error, `nil` will be returned as well as a string describing the
error.
When `regex` contains a sub-match capturing group, and when such a match is
found, the first submatch capture will be inserted in between each split
value, like so:
```lua
local ngx_re = require "ngx.re"
local res, err = ngx_re.split("a,b,c,d", "(,)")
-- res is now {"a", ",", "b", ",", "c", ",", "d"}
```
When `regex` is empty string `""`, the `subject` will be split into chars,
like so:
```lua
local ngx_re = require "ngx.re"
local res, err = ngx_re.split("abcd", "")
-- res is now {"a", "b", "c", "d"}
```
The optional `ctx` table argument can be a Lua table holding an optional `pos`
field. When the `pos` field in the `ctx` table argument is specified,
`ngx_re.split` will start splitting the `subject` from that index:
```lua
local ngx_re = require "ngx.re"
local res, err = ngx_re.split("a,b,c,d", ",", nil, {pos = 5})
-- res is now {"c", "d"}
```
The optional `max` argument is a number that when specified, will prevent
`ngx_re.split` from adding more than `max` matches to the `res` array:
```lua
local ngx_re = require "ngx.re"
local res, err = ngx_re.split("a,b,c,d", ",", nil, nil, 3)
-- res is now {"a", "b", "c,d"}
```
Specifying `max <= 0` disables this behavior, meaning that the number of
results won't be limited.
The optional 6th argument `res` can be a table that `ngx_re.split` will re-use
to hold the results instead of creating a new one, which can improve
performance in hot code paths. It is used like so:
```lua
local ngx_re = require "ngx.re"
local my_table = {"hello world"}
local res, err = ngx_re.split("a,b,c,d", ",", nil, nil, nil, my_table)
-- res/my_table is now {"a", "b", "c", "d"}
```
When provided with a `res` table, `ngx_re.split` won't clear the table
for performance reasons, but will rather insert a trailing `nil` value
when the split is completed:
```lua
local ngx_re = require "ngx.re"
local my_table = {"W", "X", "Y", "Z"}
local res, err = ngx_re.split("a,b", ",", nil, nil, nil, my_table)
-- res/my_table is now {"a", "b", nil, "Z"}
```
When the trailing `nil` is not enough for your purpose, you should
clear the table yourself before feeding it into the `split` function.
[Back to TOC](#table-of-contents)
opt
-----
**syntax:** *ngx_re.opt(option, value)*
Allows changing of regex settings. Currently, it can only change the
`jit_stack_size` of the PCRE engine, like so:
```nginx
init_by_lua_block { require "ngx.re".opt("jit_stack_size", 200 * 1024) }
server {
location /re {
content_by_lua_block {
-- full regex and string are taken from https://github.com/JuliaLang/julia/issues/8278
local very_long_string = [[71.163.72.113 - - [30/Jul/2014:16:40:55 -0700] ...]]
local very_complicated_regex = [[([\d\.]+) ([\w.-]+) ([\w.-]+) (\[.+\]) ...]]
local from, to, err = ngx.re.find(very_long_string, very_complicated_regex, "jo")
-- with the regular jit_stack_size, we would get the error 'pcre_exec() failed: -27'
-- instead, we get a match
ngx.print(from .. "-" .. to) -- prints '1-1563'
}
}
}
```
The `jit_stack_size` cannot be set to a value lower than PCRE's default of 32K.
This method requires the PCRE library enabled in Nginx.
This feature was first introduced in the `v0.1.12` release.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list
is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for
Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Thibault Charbonnier - ([@thibaultcha](https://github.com/thibaultcha))
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2016-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

20
lib/ngx/req.lua Normal file
View File

@ -0,0 +1,20 @@
-- Copyright (C) by OpenResty Inc.
local base = require "resty.core.base"
base.allows_subsystem("http")
local core_request = require "resty.core.request"
local set_req_header = core_request.set_req_header
local _M = { version = base.version }
function _M.add_header(key, value)
set_req_header(key, value, false)
end
return _M

139
lib/ngx/req.md Normal file
View File

@ -0,0 +1,139 @@
Name
====
ngx.req - Lua API for HTTP request handling.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [add_header](#add_header)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
```lua
local ngx_req = require "ngx.req"
-- add_header
ngx_req.add_header("Foo", "bar")
ngx_req.add_header("Foo", "baz")
--> there will be two new headers in the HTTP request:
--> Foo: bar and Foo: baz
```
[Back to TOC](#table-of-contents)
Description
===========
This module provides a Lua API to handle HTTP requests.
[Back to TOC](#table-of-contents)
Methods
=======
All methods provided by this module are static (or module-level). That is, you
do not need an object (or instance) to call these methods.
[Back to TOC](#table-of-contents)
add_header
----------
**syntax:** *ngx_req.add_header(header_name, header_value)*
**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;*
This method adds the specified header and its value to the current
request. It works similarly as
[ngx.req.set_header](https://github.com/openresty/lua-nginx-module#ngxreqset_header),
with the exception that when the header already exists, the specified value(s)
will be appended instead of overriden.
The first argument `header_name` must be a non-empty string.
When the specified `header_name` is a builtin header (e.g. `User-Agent`), this
method will override its values.
The `header_value` argument can either be a string or a non-empty, array-like
table. A `nil` or empty table value will cause this function to throw an error.
This feature was first introduced in the `v0.1.18` release.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list
is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for
Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2016-2019, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* library [lua-resty-core](https://github.com/openresty/lua-resty-core)
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

18
lib/ngx/resp.lua Normal file
View File

@ -0,0 +1,18 @@
-- Copyright (C) Yichun Zhang. All rights reserved.
local base = require "resty.core.base"
base.allows_subsystem('http')
local core_response = require "resty.core.response"
local set_resp_header = core_response.set_resp_header
local _M = { version = base.version }
function _M.add_header(key, value)
set_resp_header(nil, key, value, true)
end
return _M

133
lib/ngx/resp.md Normal file
View File

@ -0,0 +1,133 @@
Name
====
ngx.resp - Lua API for HTTP response handling.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [add_header](#add_header)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
```lua
local ngx_resp = require "ngx.resp"
-- add_header
ngx_resp.add_header("Foo", "bar")
ngx_resp.add_header("Foo", "baz")
--> there will be two new headers in HTTP response:
--> Foo: bar and Foo: baz
```
[Back to TOC](#table-of-contents)
Description
===========
This Lua module provides Lua API which could be used to handle HTTP response.
[Back to TOC](#table-of-contents)
Methods
=======
All the methods of this module are static (or module-level). That is, you do
not need an object (or instance) to call these methods.
[Back to TOC](#table-of-contents)
add_header
----------
**syntax:** *ngx_resp.add_header(header_name, header_value)*
This function adds specified header with corresponding value to the response of
current request. The `header_value` could be either a string or a table.
The `ngx.resp.add_header` works mostly like:
* [ngx.header.HEADER](https://github.com/openresty/lua-nginx-module#ngxheaderheader)
* Nginx's [add_header](http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header) directive.
However, unlike `ngx.header.HEADER`, this method appends new header to the old
one instead of overriding it.
Unlike `add_header` directive, this method will override the builtin header
instead of appending it.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list
is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for
Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2018, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

193
lib/ngx/semaphore.lua Normal file
View File

@ -0,0 +1,193 @@
-- Copyright (C) Yichun Zhang (agentzh)
-- Copyright (C) cuiweixie
-- I hereby assign copyright in this code to the lua-resty-core project,
-- to be licensed under the same terms as the rest of the code.
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local ffi = require 'ffi'
local FFI_OK = base.FFI_OK
local FFI_ERROR = base.FFI_ERROR
local FFI_DECLINED = base.FFI_DECLINED
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_gc = ffi.gc
local C = ffi.C
local type = type
local error = error
local tonumber = tonumber
local get_request = base.get_request
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local setmetatable = setmetatable
local co_yield = coroutine._yield
local ERR_BUF_SIZE = 128
local subsystem = ngx.config.subsystem
local errmsg = base.get_errmsg_ptr()
local psem
local ngx_lua_ffi_sema_new
local ngx_lua_ffi_sema_post
local ngx_lua_ffi_sema_count
local ngx_lua_ffi_sema_wait
local ngx_lua_ffi_sema_gc
if subsystem == 'http' then
ffi.cdef[[
struct ngx_http_lua_sema_s;
typedef struct ngx_http_lua_sema_s ngx_http_lua_sema_t;
int ngx_http_lua_ffi_sema_new(ngx_http_lua_sema_t **psem,
int n, char **errmsg);
int ngx_http_lua_ffi_sema_post(ngx_http_lua_sema_t *sem, int n);
int ngx_http_lua_ffi_sema_count(ngx_http_lua_sema_t *sem);
int ngx_http_lua_ffi_sema_wait(ngx_http_request_t *r,
ngx_http_lua_sema_t *sem, int wait_ms,
unsigned char *errstr, size_t *errlen);
void ngx_http_lua_ffi_sema_gc(ngx_http_lua_sema_t *sem);
]]
psem = ffi_new("ngx_http_lua_sema_t *[1]")
ngx_lua_ffi_sema_new = C.ngx_http_lua_ffi_sema_new
ngx_lua_ffi_sema_post = C.ngx_http_lua_ffi_sema_post
ngx_lua_ffi_sema_count = C.ngx_http_lua_ffi_sema_count
ngx_lua_ffi_sema_wait = C.ngx_http_lua_ffi_sema_wait
ngx_lua_ffi_sema_gc = C.ngx_http_lua_ffi_sema_gc
elseif subsystem == 'stream' then
ffi.cdef[[
struct ngx_stream_lua_sema_s;
typedef struct ngx_stream_lua_sema_s ngx_stream_lua_sema_t;
int ngx_stream_lua_ffi_sema_new(ngx_stream_lua_sema_t **psem,
int n, char **errmsg);
int ngx_stream_lua_ffi_sema_post(ngx_stream_lua_sema_t *sem, int n);
int ngx_stream_lua_ffi_sema_count(ngx_stream_lua_sema_t *sem);
int ngx_stream_lua_ffi_sema_wait(ngx_stream_lua_request_t *r,
ngx_stream_lua_sema_t *sem, int wait_ms,
unsigned char *errstr, size_t *errlen);
void ngx_stream_lua_ffi_sema_gc(ngx_stream_lua_sema_t *sem);
]]
psem = ffi_new("ngx_stream_lua_sema_t *[1]")
ngx_lua_ffi_sema_new = C.ngx_stream_lua_ffi_sema_new
ngx_lua_ffi_sema_post = C.ngx_stream_lua_ffi_sema_post
ngx_lua_ffi_sema_count = C.ngx_stream_lua_ffi_sema_count
ngx_lua_ffi_sema_wait = C.ngx_stream_lua_ffi_sema_wait
ngx_lua_ffi_sema_gc = C.ngx_stream_lua_ffi_sema_gc
end
local _M = { version = base.version }
local mt = { __index = _M }
function _M.new(n)
n = tonumber(n) or 0
if n < 0 then
error("no negative number", 2)
end
local ret = ngx_lua_ffi_sema_new(psem, n, errmsg)
if ret == FFI_ERROR then
return nil, ffi_str(errmsg[0])
end
local sem = psem[0]
ffi_gc(sem, ngx_lua_ffi_sema_gc)
return setmetatable({ sem = sem }, mt)
end
function _M.wait(self, seconds)
if type(self) ~= "table" or type(self.sem) ~= "cdata" then
error("not a semaphore instance", 2)
end
local r = get_request()
if not r then
error("no request found")
end
local milliseconds = tonumber(seconds) * 1000
if milliseconds < 0 then
error("no negative number", 2)
end
local cdata_sem = self.sem
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local ret = ngx_lua_ffi_sema_wait(r, cdata_sem,
milliseconds, err, errlen)
if ret == FFI_ERROR then
return nil, ffi_str(err, errlen[0])
end
if ret == FFI_OK then
return true
end
if ret == FFI_DECLINED then
return nil, "timeout"
end
-- Note: we cannot use the tail-call form here since we
-- might need the current function call's activation
-- record to hold the reference to our semaphore object
-- to prevent it from getting GC'd prematurely.
local ok
ok, err = co_yield()
return ok, err
end
function _M.post(self, n)
if type(self) ~= "table" or type(self.sem) ~= "cdata" then
error("not a semaphore instance", 2)
end
local cdata_sem = self.sem
local num = n and tonumber(n) or 1
if num < 1 then
error("positive number required", 2)
end
-- always return NGX_OK
ngx_lua_ffi_sema_post(cdata_sem, num)
return true
end
function _M.count(self)
if type(self) ~= "table" or type(self.sem) ~= "cdata" then
error("not a semaphore instance", 2)
end
return ngx_lua_ffi_sema_count(self.sem)
end
return _M

356
lib/ngx/semaphore.md Normal file
View File

@ -0,0 +1,356 @@
Name
====
ngx.semaphore - light thread semaphore for OpenResty/ngx_lua.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Synchronizing threads in the same context](#synchronizing-threads-in-the-same-context)
* [Synchronizing threads in different contexts](#synchronizing-threads-in-different-contexts)
* [Description](#description)
* [Methods](#methods)
* [new](#new)
* [post](#post)
* [wait](#wait)
* [count](#count)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is production ready.
Synopsis
========
Synchronizing threads in the same context
-----------------------------------------
```nginx
location = /t {
content_by_lua_block {
local semaphore = require "ngx.semaphore"
local sema = semaphore.new()
local function handler()
ngx.say("sub thread: waiting on sema...")
local ok, err = sema:wait(1) -- wait for a second at most
if not ok then
ngx.say("sub thread: failed to wait on sema: ", err)
else
ngx.say("sub thread: waited successfully.")
end
end
local co = ngx.thread.spawn(handler)
ngx.say("main thread: sleeping for a little while...")
ngx.sleep(0.1) -- wait a bit
ngx.say("main thread: posting to sema...")
sema:post(1)
ngx.say("main thread: end.")
}
}
```
The example location above produces a response output like this:
```
sub thread: waiting on sema...
main thread: sleeping for a little while...
main thread: posting to sema...
main thread: end.
sub thread: waited successfully.
```
[Back to TOC](#table-of-contents)
Synchronizing threads in different contexts
-------------------------------------------
```nginx
location = /t {
content_by_lua_block {
local semaphore = require "ngx.semaphore"
local sema = semaphore.new()
local outputs = {}
local i = 1
local function out(s)
outputs[i] = s
i = i + 1
end
local function handler()
out("timer thread: sleeping for a little while...")
ngx.sleep(0.1) -- wait a bit
out("timer thread: posting on sema...")
sema:post(1)
end
assert(ngx.timer.at(0, handler))
out("main thread: waiting on sema...")
local ok, err = sema:wait(1) -- wait for a second at most
if not ok then
out("main thread: failed to wait on sema: ", err)
else
out("main thread: waited successfully.")
end
out("main thread: end.")
ngx.say(table.concat(outputs, "\n"))
}
}
```
The example location above produces a response body like this
```
main thread: waiting on sema...
timer thread: sleeping for a little while...
timer thread: posting on sema...
main thread: waited successfully.
main thread: end.
```
The same applies to different request contexts as long as these requests are served
by the same nginx worker process.
[Back to TOC](#table-of-contents)
Description
===========
This module provides an efficient semaphore API for the OpenResty/ngx_lua module. With
semaphores, "light threads" (created by [ngx.thread.spawn](https://github.com/openresty/lua-nginx-module#ngxthreadspawn),
[ngx.timer.at](https://github.com/openresty/lua-nginx-module#ngxtimerat), and etc.) can
synchronize among each other very efficiently without constant polling and sleeping.
"Light threads" in different contexts (like in different requests) can share the same
semaphore instance as long as these "light threads" reside in the same NGINX worker
process and the [lua_code_cache](https://github.com/openresty/lua-nginx-module#lua_code_cache)
directive is turned on (which is the default). For inter-process "light thread" synchronization,
it is recommended to use the [lua-resty-lock](https://github.com/openresty/lua-resty-lock) library instead
(which is a bit less efficient than this semaphore API though).
This semaphore API has a pure userland implementation which does not involve any system calls nor
block any operating system threads. It works closely with the event model of NGINX without
introducing any extra delay.
Like other APIs provided by this `lua-resty-core` library, the LuaJIT FFI feature is required.
[Back to TOC](#table-of-contents)
Methods
=======
[Back to TOC](#table-of-contents)
new
---
**syntax:** *sema, err = semaphore_module.new(n?)*
**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;*
Creates and returns a new semaphore instance that has `n` (default to `0`) resources.
For example,
```lua
local semaphore = require "ngx.semaphore"
local sema, err = semaphore.new()
if not sema then
ngx.say("create semaphore failed: ", err)
end
```
Often the semaphore object created is shared on the NGINX worker process by mounting in a custom Lua module, as
documented below:
https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker
[Back to TOC](#table-of-contents)
post
--------
**syntax:** *sema:post(n?)*
**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;*
Releases `n` (default to `1`) "resources" to the semaphore instance.
This will not yield the current running "light thread".
At most `n` "light threads" will be waken up when the current running "light thread" later yields (or terminates).
```lua
-- typically, we get the semaphore instance from upvalue or globally shared data
-- See https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker
local semaphore = require "ngx.semaphore"
local sema = semaphore.new()
sema:post(2) -- releases 2 resources
```
[Back to TOC](#table-of-contents)
wait
----
**syntax:** *ok, err = sema:wait(timeout)*
**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;*
Requests a resource from the semaphore instance.
Returns `true` immediately when there is resources available for the current running "light thread".
Otherwise the current "light thread" will enter the waiting queue and yield execution.
The current "light thread" will be automatically waken up and the `wait` function call
will return `true` when there is resources available for it, or return `nil` and a string describing
the error in case of failure (like `"timeout"`).
The `timeout` argument specifies the maximum time this function call should wait for (in seconds).
When the `timeout` argument is 0, it means "no wait", that is, when there is no readily available
"resources" for the current running "light thread", this `wait` function call returns immediately
`nil` and the error string `"timeout"`.
You can specify millisecond precision in the timeout value by using floating point numbers like 0.001 (which means 1ms).
"Light threads" created by different contexts (like request handlers) can wait on the
same semaphore instance without problem.
See [Synopsis](#synopsis) for code examples.
[Back to TOC](#table-of-contents)
count
--------
**syntax:** *count = sema:count()*
**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;,
content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;*
Returns the number of resources readily available in the `sema` semaphore instance (if any).
When the returned number is negative, it means the number of "light threads" waiting on
this semaphore.
Consider the following example,
```lua
local semaphore = require "ngx.semaphore"
local sema = semaphore.new(0)
ngx.say("count: ", sema:count()) -- count: 0
local function handler(id)
local ok, err = sema:wait(1)
if not ok then
ngx.say("err: ", err)
else
ngx.say("wait success")
end
end
local co1 = ngx.thread.spawn(handler)
local co2 = ngx.thread.spawn(handler)
ngx.say("count: ", sema:count()) -- count: -2
sema:post(1)
ngx.say("count: ", sema:count()) -- count: -1
sema:post(2)
ngx.say("count: ", sema:count()) -- count: 1
```
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Weixie Cui, Kugou Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2015-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

470
lib/ngx/ssl.lua Normal file
View File

@ -0,0 +1,470 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local ffi_gc = ffi.gc
local get_request = base.get_request
local error = error
local tonumber = tonumber
local errmsg = base.get_errmsg_ptr()
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local FFI_DECLINED = base.FFI_DECLINED
local FFI_OK = base.FFI_OK
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_ssl_set_der_certificate
local ngx_lua_ffi_ssl_clear_certs
local ngx_lua_ffi_ssl_set_der_private_key
local ngx_lua_ffi_ssl_raw_server_addr
local ngx_lua_ffi_ssl_server_port
local ngx_lua_ffi_ssl_server_name
local ngx_lua_ffi_ssl_raw_client_addr
local ngx_lua_ffi_cert_pem_to_der
local ngx_lua_ffi_priv_key_pem_to_der
local ngx_lua_ffi_ssl_get_tls1_version
local ngx_lua_ffi_parse_pem_cert
local ngx_lua_ffi_parse_pem_priv_key
local ngx_lua_ffi_set_cert
local ngx_lua_ffi_set_priv_key
local ngx_lua_ffi_free_cert
local ngx_lua_ffi_free_priv_key
local ngx_lua_ffi_ssl_verify_client
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_ssl_set_der_certificate(ngx_http_request_t *r,
const char *data, size_t len, char **err);
int ngx_http_lua_ffi_ssl_clear_certs(ngx_http_request_t *r, char **err);
int ngx_http_lua_ffi_ssl_set_der_private_key(ngx_http_request_t *r,
const char *data, size_t len, char **err);
int ngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr,
size_t *addrlen, int *addrtype, char **err);
int ngx_http_lua_ffi_ssl_server_port(ngx_http_request_t *r,
unsigned short *server_port, char **err);
int ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name,
size_t *namelen, char **err);
int ngx_http_lua_ffi_ssl_raw_client_addr(ngx_http_request_t *r, char **addr,
size_t *addrlen, int *addrtype, char **err);
int ngx_http_lua_ffi_cert_pem_to_der(const unsigned char *pem,
size_t pem_len, unsigned char *der, char **err);
int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,
size_t pem_len, const unsigned char *passphrase,
unsigned char *der, char **err);
int ngx_http_lua_ffi_ssl_get_tls1_version(ngx_http_request_t *r,
char **err);
void *ngx_http_lua_ffi_parse_pem_cert(const unsigned char *pem,
size_t pem_len, char **err);
void *ngx_http_lua_ffi_parse_pem_priv_key(const unsigned char *pem,
size_t pem_len, char **err);
int ngx_http_lua_ffi_set_cert(void *r, void *cdata, char **err);
int ngx_http_lua_ffi_set_priv_key(void *r, void *cdata, char **err);
void ngx_http_lua_ffi_free_cert(void *cdata);
void ngx_http_lua_ffi_free_priv_key(void *cdata);
int ngx_http_lua_ffi_ssl_verify_client(void *r,
void *cdata, int depth, char **err);
]]
ngx_lua_ffi_ssl_set_der_certificate =
C.ngx_http_lua_ffi_ssl_set_der_certificate
ngx_lua_ffi_ssl_clear_certs = C.ngx_http_lua_ffi_ssl_clear_certs
ngx_lua_ffi_ssl_set_der_private_key =
C.ngx_http_lua_ffi_ssl_set_der_private_key
ngx_lua_ffi_ssl_raw_server_addr = C.ngx_http_lua_ffi_ssl_raw_server_addr
ngx_lua_ffi_ssl_server_port = C.ngx_http_lua_ffi_ssl_server_port
ngx_lua_ffi_ssl_server_name = C.ngx_http_lua_ffi_ssl_server_name
ngx_lua_ffi_ssl_raw_client_addr = C.ngx_http_lua_ffi_ssl_raw_client_addr
ngx_lua_ffi_cert_pem_to_der = C.ngx_http_lua_ffi_cert_pem_to_der
ngx_lua_ffi_priv_key_pem_to_der = C.ngx_http_lua_ffi_priv_key_pem_to_der
ngx_lua_ffi_ssl_get_tls1_version = C.ngx_http_lua_ffi_ssl_get_tls1_version
ngx_lua_ffi_parse_pem_cert = C.ngx_http_lua_ffi_parse_pem_cert
ngx_lua_ffi_parse_pem_priv_key = C.ngx_http_lua_ffi_parse_pem_priv_key
ngx_lua_ffi_set_cert = C.ngx_http_lua_ffi_set_cert
ngx_lua_ffi_set_priv_key = C.ngx_http_lua_ffi_set_priv_key
ngx_lua_ffi_free_cert = C.ngx_http_lua_ffi_free_cert
ngx_lua_ffi_free_priv_key = C.ngx_http_lua_ffi_free_priv_key
ngx_lua_ffi_ssl_verify_client = C.ngx_http_lua_ffi_ssl_verify_client
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_ssl_set_der_certificate(ngx_stream_lua_request_t *r,
const char *data, size_t len, char **err);
int ngx_stream_lua_ffi_ssl_clear_certs(ngx_stream_lua_request_t *r,
char **err);
int ngx_stream_lua_ffi_ssl_set_der_private_key(ngx_stream_lua_request_t *r,
const char *data, size_t len, char **err);
int ngx_stream_lua_ffi_ssl_raw_server_addr(ngx_stream_lua_request_t *r,
char **addr, size_t *addrlen, int *addrtype, char **err);
int ngx_stream_lua_ffi_ssl_server_port(ngx_stream_lua_request_t *r,
unsigned short *server_port, char **err);
int ngx_stream_lua_ffi_ssl_server_name(ngx_stream_lua_request_t *r,
char **name, size_t *namelen, char **err);
int ngx_stream_lua_ffi_ssl_raw_client_addr(ngx_stream_lua_request_t *r,
char **addr, size_t *addrlen, int *addrtype, char **err);
int ngx_stream_lua_ffi_cert_pem_to_der(const unsigned char *pem,
size_t pem_len, unsigned char *der, char **err);
int ngx_stream_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,
size_t pem_len, const unsigned char *passphrase,
unsigned char *der, char **err);
int ngx_stream_lua_ffi_ssl_get_tls1_version(ngx_stream_lua_request_t *r,
char **err);
void *ngx_stream_lua_ffi_parse_pem_cert(const unsigned char *pem,
size_t pem_len, char **err);
void *ngx_stream_lua_ffi_parse_pem_priv_key(const unsigned char *pem,
size_t pem_len, char **err);
int ngx_stream_lua_ffi_set_cert(void *r, void *cdata, char **err);
int ngx_stream_lua_ffi_set_priv_key(void *r, void *cdata, char **err);
void ngx_stream_lua_ffi_free_cert(void *cdata);
void ngx_stream_lua_ffi_free_priv_key(void *cdata);
int ngx_stream_lua_ffi_ssl_verify_client(void *r,
void *cdata, int depth, char **err);
]]
ngx_lua_ffi_ssl_set_der_certificate =
C.ngx_stream_lua_ffi_ssl_set_der_certificate
ngx_lua_ffi_ssl_clear_certs = C.ngx_stream_lua_ffi_ssl_clear_certs
ngx_lua_ffi_ssl_set_der_private_key =
C.ngx_stream_lua_ffi_ssl_set_der_private_key
ngx_lua_ffi_ssl_raw_server_addr = C.ngx_stream_lua_ffi_ssl_raw_server_addr
ngx_lua_ffi_ssl_server_port = C.ngx_stream_lua_ffi_ssl_server_port
ngx_lua_ffi_ssl_server_name = C.ngx_stream_lua_ffi_ssl_server_name
ngx_lua_ffi_ssl_raw_client_addr = C.ngx_stream_lua_ffi_ssl_raw_client_addr
ngx_lua_ffi_cert_pem_to_der = C.ngx_stream_lua_ffi_cert_pem_to_der
ngx_lua_ffi_priv_key_pem_to_der = C.ngx_stream_lua_ffi_priv_key_pem_to_der
ngx_lua_ffi_ssl_get_tls1_version = C.ngx_stream_lua_ffi_ssl_get_tls1_version
ngx_lua_ffi_parse_pem_cert = C.ngx_stream_lua_ffi_parse_pem_cert
ngx_lua_ffi_parse_pem_priv_key = C.ngx_stream_lua_ffi_parse_pem_priv_key
ngx_lua_ffi_set_cert = C.ngx_stream_lua_ffi_set_cert
ngx_lua_ffi_set_priv_key = C.ngx_stream_lua_ffi_set_priv_key
ngx_lua_ffi_free_cert = C.ngx_stream_lua_ffi_free_cert
ngx_lua_ffi_free_priv_key = C.ngx_stream_lua_ffi_free_priv_key
ngx_lua_ffi_ssl_verify_client = C.ngx_stream_lua_ffi_ssl_verify_client
end
local _M = { version = base.version }
local charpp = ffi.new("char*[1]")
local intp = ffi.new("int[1]")
local ushortp = ffi.new("unsigned short[1]")
function _M.clear_certs()
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_ssl_clear_certs(r, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
function _M.set_der_cert(data)
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_ssl_set_der_certificate(r, data, #data, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
function _M.set_der_priv_key(data)
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_ssl_set_der_private_key(r, data, #data, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
local addr_types = {
[0] = "unix",
[1] = "inet",
[2] = "inet6",
}
function _M.raw_server_addr()
local r = get_request()
if not r then
error("no request found")
end
local sizep = get_size_ptr()
local rc = ngx_lua_ffi_ssl_raw_server_addr(r, charpp, sizep, intp, errmsg)
if rc == FFI_OK then
local typ = addr_types[intp[0]]
if not typ then
return nil, nil, "unknown address type: " .. intp[0]
end
return ffi_str(charpp[0], sizep[0]), typ
end
return nil, nil, ffi_str(errmsg[0])
end
function _M.server_port()
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_ssl_server_port(r, ushortp, errmsg)
if rc == FFI_OK then
return ushortp[0]
end
return nil, ffi_str(errmsg[0])
end
function _M.server_name()
local r = get_request()
if not r then
error("no request found")
end
local sizep = get_size_ptr()
local rc = ngx_lua_ffi_ssl_server_name(r, charpp, sizep, errmsg)
if rc == FFI_OK then
return ffi_str(charpp[0], sizep[0])
end
if rc == FFI_DECLINED then
return nil
end
return nil, ffi_str(errmsg[0])
end
function _M.raw_client_addr()
local r = get_request()
if not r then
error("no request found")
end
local sizep = get_size_ptr()
local rc = ngx_lua_ffi_ssl_raw_client_addr(r, charpp, sizep, intp, errmsg)
if rc == FFI_OK then
local typ = addr_types[intp[0]]
if not typ then
return nil, nil, "unknown address type: " .. intp[0]
end
return ffi_str(charpp[0], sizep[0]), typ
end
return nil, nil, ffi_str(errmsg[0])
end
function _M.cert_pem_to_der(pem)
local outbuf = get_string_buf(#pem)
local sz = ngx_lua_ffi_cert_pem_to_der(pem, #pem, outbuf, errmsg)
if sz > 0 then
return ffi_str(outbuf, sz)
end
return nil, ffi_str(errmsg[0])
end
function _M.priv_key_pem_to_der(pem, passphrase)
local outbuf = get_string_buf(#pem)
local sz = ngx_lua_ffi_priv_key_pem_to_der(pem, #pem,
passphrase, outbuf, errmsg)
if sz > 0 then
return ffi_str(outbuf, sz)
end
return nil, ffi_str(errmsg[0])
end
local function get_tls1_version()
local r = get_request()
if not r then
error("no request found")
end
local ver = ngx_lua_ffi_ssl_get_tls1_version(r, errmsg)
ver = tonumber(ver)
if ver >= 0 then
return ver
end
-- rc == FFI_ERROR
return nil, ffi_str(errmsg[0])
end
_M.get_tls1_version = get_tls1_version
function _M.parse_pem_cert(pem)
local cert = ngx_lua_ffi_parse_pem_cert(pem, #pem, errmsg)
if cert ~= nil then
return ffi_gc(cert, ngx_lua_ffi_free_cert)
end
return nil, ffi_str(errmsg[0])
end
function _M.parse_pem_priv_key(pem)
local pkey = ngx_lua_ffi_parse_pem_priv_key(pem, #pem, errmsg)
if pkey ~= nil then
return ffi_gc(pkey, ngx_lua_ffi_free_priv_key)
end
return nil, ffi_str(errmsg[0])
end
function _M.set_cert(cert)
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_set_cert(r, cert, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
function _M.set_priv_key(priv_key)
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_set_priv_key(r, priv_key, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
function _M.verify_client(ca_certs, depth)
local r = get_request()
if not r then
error("no request found")
end
if not depth then
depth = -1
end
local rc = ngx_lua_ffi_ssl_verify_client(r, ca_certs, depth, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
do
_M.SSL3_VERSION = 0x0300
_M.TLS1_VERSION = 0x0301
_M.TLS1_1_VERSION = 0x0302
_M.TLS1_2_VERSION = 0x0303
_M.TLS1_3_VERSION = 0x0304
local map = {
[_M.SSL3_VERSION] = "SSLv3",
[_M.TLS1_VERSION] = "TLSv1",
[_M.TLS1_1_VERSION] = "TLSv1.1",
[_M.TLS1_2_VERSION] = "TLSv1.2",
[_M.TLS1_3_VERSION] = "TLSv1.3",
}
function _M.get_tls1_version_str()
local ver, err = get_tls1_version()
if not ver then
return nil, err
end
local ver_str = map[ver]
if not ver_str then
return nil, "unknown version"
end
return ver_str
end
end
return _M

586
lib/ngx/ssl.md Normal file
View File

@ -0,0 +1,586 @@
Name
====
ngx.ssl - Lua API for controlling NGINX downstream SSL handshakes
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [clear_certs](#clear_certs)
* [cert_pem_to_der](#cert_pem_to_der)
* [set_der_cert](#set_der_cert)
* [priv_key_pem_to_der](#priv_key_pem_to_der)
* [set_der_priv_key](#set_der_priv_key)
* [server_name](#server_name)
* [server_port](#server_port)
* [raw_server_addr](#raw_server_addr)
* [raw_client_addr](#raw_client_addr)
* [get_tls1_version](#get_tls1_version)
* [get_tls1_version_str](#get_tls1_version_str)
* [parse_pem_cert](#parse_pem_cert)
* [parse_pem_priv_key](#parse_pem_priv_key)
* [set_cert](#set_cert)
* [set_priv_key](#set_priv_key)
* [verify_client](#verify_client)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is production ready.
Synopsis
========
```nginx
# Note: you do not need the following line if you are using
# OpenResty 1.9.7.2+.
lua_package_path "/path/to/lua-resty-core/lib/?.lua;;";
server {
listen 443 ssl;
server_name test.com;
# useless placeholders: just to shut up NGINX configuration
# loader errors:
ssl_certificate /path/to/fallback.crt;
ssl_certificate_key /path/to/fallback.key;
ssl_certificate_by_lua_block {
local ssl = require "ngx.ssl"
-- clear the fallback certificates and private keys
-- set by the ssl_certificate and ssl_certificate_key
-- directives above:
local ok, err = ssl.clear_certs()
if not ok then
ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates")
return ngx.exit(ngx.ERROR)
end
-- assuming the user already defines the my_load_certificate_chain()
-- herself.
local pem_cert_chain = assert(my_load_certificate_chain())
local der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)
if not der_cert_chain then
ngx.log(ngx.ERR, "failed to convert certificate chain ",
"from PEM to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local ok, err = ssl.set_der_cert(der_cert_chain)
if not ok then
ngx.log(ngx.ERR, "failed to set DER cert: ", err)
return ngx.exit(ngx.ERROR)
end
-- assuming the user already defines the my_load_private_key()
-- function herself.
local pem_pkey = assert(my_load_private_key())
local passphrase = "password" -- or nil
local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey, passphrase)
if not der_pkey then
ngx.log(ngx.ERR, "failed to convert private key ",
"from PEM to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local ok, err = ssl.set_der_priv_key(der_pkey)
if not ok then
ngx.log(ngx.ERR, "failed to set DER private key: ", err)
return ngx.exit(ngx.ERROR)
end
}
location / {
root html;
}
}
```
Description
===========
This Lua module provides API functions to control the SSL handshake process in contexts like
[ssl_certificate_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_certificate_by_lua_block)
(of the [ngx_lua](https://github.com/openresty/lua-nginx-module#readme) module).
For web servers serving many (like millions of) https sites, it is often desired to lazily
load and cache the SSL certificate chain and private key data for the https sites actually
being served by a particular server. This Lua module provides API to support such use cases
in the context of the [ssl_certificate_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_certificate_by_lua_block)
directive.
To load the `ngx.ssl` module in Lua, just write
```lua
local ssl = require "ngx.ssl"
```
[Back to TOC](#table-of-contents)
Methods
=======
clear_certs
-----------
**syntax:** *ok, err = ssl.clear_certs()*
**context:** *ssl_certificate_by_lua&#42;*
Clears any existing SSL certificates and/or private keys set on the current SSL connection.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
[Back to TOC](#table-of-contents)
cert_pem_to_der
---------------
**syntax:** *der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)*
**context:** *any*
Converts the PEM-formatted SSL certificate chain data into the DER format (for later uses
in the [set_der_cert](#set_der_cert)
function, for example).
In case of failures, returns `nil` and a string describing the error.
It is known that the `openssl` command-line utility may not convert the whole SSL
certificate chain from PEM to DER correctly. So always use this Lua function to do
the conversion. You can always use libraries like [lua-resty-lrucache](https://github.com/openresty/lua-resty-lrucache#readme)
and/or ngx_lua APIs like [lua_shared_dict](https://github.com/openresty/lua-nginx-module#lua_shared_dict)
to do the caching of the DER-formatted results, for example.
This function can be called in any context.
[Back to TOC](#table-of-contents)
set_der_cert
------------
**syntax:** *ok, err = ssl.set_der_cert(der_cert_chain)*
**context:** *ssl_certificate_by_lua&#42;*
Sets the DER-formatted SSL certificate chain data for the current SSL connection. Note that
the DER data is
directly in the Lua string argument. *No* external file names are supported here.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
Note that, the SSL certificate chain is usually encoded in the PEM format. So you need
to use the [cert_pem_to_der](#cert_pem_to_der)
function to do the conversion first.
[Back to TOC](#table-of-contents)
priv_key_pem_to_der
-------------------
**syntax:** *der_priv_key, err = ssl.priv_key_pem_to_der(pem_priv_key, passphrase)*
**context:** *any*
Converts the PEM-formatted SSL private key data into the DER format (for later uses
in the [set_der_priv_key](#set_der_priv_key)
function, for example).
The `passphrase` is the passphrase for `pem_priv_key` if the private key is password protected.
In case of failures, returns `nil` and a string describing the error.
Alternatively, you can do the PEM to DER conversion *offline* with the `openssl` command-line utility, like below
```bash
openssl rsa -in key.pem -outform DER -out key.der
```
This function can be called in any context.
[Back to TOC](#table-of-contents)
set_der_priv_key
----------------
**syntax:** *ok, err = ssl.set_der_priv_key(der_priv_key)*
**context:** *ssl_certificate_by_lua&#42;*
Sets the DER-formatted prviate key for the current SSL connection.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
Usually, the private keys are encoded in the PEM format. You can either use the
[priv_key_pem_to_der](#priv_key_pem_to_der) function
to do the PEM to DER conversion or just use
the `openssl` command-line utility offline, like below
```bash
openssl rsa -in key.pem -outform DER -out key.der
```
[Back to TOC](#table-of-contents)
server_name
-----------
**syntax:** *name, err = ssl.server_name()*
**context:** *any*
Returns the TLS SNI (Server Name Indication) name set by the client. Returns `nil`
when the client does not set it.
In case of failures, it returns `nil` *and* a string describing the error.
Usually we use this SNI name as the domain name (like `www.openresty.org`) to
identify the current web site while loading the corresponding SSL certificate
chain and private key for the site.
Please note that not all https clients set the SNI name, so when the SNI name is
missing from the client handshake request, we use the server IP address accessed
by the client to identify the site. See the [raw_server_addr](#raw_server_addr) method
for more details.
This function can be called in any context where downstream https is used.
[Back to TOC](#table-of-contents)
server_port
-----------
**syntax:** port, err = ssl.server_port()
**context:** *any*
Returns the server port. Returns `nil`
when server dont have a port.
In case of failures, it returns `nil` *and* a string describing the error.
This function can be called in any context where downstream https is used.
[Back to TOC](#table-of-contents)
raw_server_addr
---------------
**syntax:** *addr_data, addr_type, err = ssl.raw_server_addr()*
**context:** *any*
Returns the raw server address actually accessed by the client in the current SSL connection.
The first two return values are strings representing the address data and the address type, respectively.
The address values are interpreted differently according to the address type values:
* `unix`
: The address data is a file path for the UNIX domain socket.
* `inet`
: The address data is a binary IPv4 address of 4 bytes long.
* `inet6`
: The address data is a binary IPv6 address of 16 bytes long.
Returns two `nil` values and a Lua string describing the error.
The following code snippet shows how to print out the UNIX domain socket address and
the IPv4 address as human-readable strings:
```lua
local ssl = require "ngx.ssl"
local byte = string.byte
local addr, addrtyp, err = ssl.raw_server_addr()
if not addr then
ngx.log(ngx.ERR, "failed to fetch raw server addr: ", err)
return
end
if addrtyp == "inet" then -- IPv4
ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2),
byte(addr, 3), byte(addr, 4))
print("Using IPv4 address: ", ip)
elseif addrtyp == "unix" then -- UNIX
print("Using unix socket file ", addr)
else -- IPv6
-- leave as an exercise for the readers
end
```
This function can be called in any context where downstream https is used.
[Back to TOC](#table-of-contents)
raw_client_addr
---------------
**syntax:** *addr_data, addr_type, err = ssl.raw_client_addr()*
**context:** *any*
Returns the raw client address of the current SSL connection.
The first two return values are strings representing the address data and the address type, respectively.
The address values are interpreted differently according to the address type values:
* `unix`
: The address data is a file path for the UNIX domain socket.
* `inet`
: The address data is a binary IPv4 address of 4 bytes long.
* `inet6`
: The address data is a binary IPv6 address of 16 bytes long.
Returns two `nil` values and a Lua string describing the error.
The following code snippet shows how to print out the UNIX domain socket address and
the IPv4 address as human-readable strings:
```lua
local ssl = require "ngx.ssl"
local byte = string.byte
local addr, addrtyp, err = ssl.raw_client_addr()
if not addr then
ngx.log(ngx.ERR, "failed to fetch raw client addr: ", err)
return
end
if addrtyp == "inet" then -- IPv4
ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2),
byte(addr, 3), byte(addr, 4))
print("Client IPv4 address: ", ip)
elseif addrtyp == "unix" then -- UNIX
print("Client unix socket file ", addr)
else -- IPv6
-- leave as an exercise for the readers
end
```
This function can be called in any context where downstream https is used.
This function was first introduced in lua-resty-core 0.1.14.
[Back to TOC](#table-of-contents)
get_tls1_version
----------------
**syntax:** *ver, err = ssl.get_tls1_version()*
**context:** *any*
Returns the TLS 1.x version number used by the current SSL connection. Returns `nil` and
a string describing the error otherwise.
Typical return values are:
* `0x0300`(SSLv3)
* `0x0301`(TLSv1)
* `0x0302`(TLSv1.1)
* `0x0303`(TLSv1.2)
* `0x0304`(TLSv1.3)
This function can be called in any context where downstream https is used.
[Back to TOC](#table-of-contents)
get_tls1_version_str
--------------------
**syntax:** *ver, err = ssl.get_tls1_version_str()*
**context:** *any*
Returns the TLS 1.x version string used by the current SSL connection. Returns `nil` and
a string describing the error otherwise.
If the TLS 1.x version number used by the current SSL connection is not
recognized, the return values will be `nil` and the string "unknown version".
Typical return values are:
* `SSLv3`
* `TLSv1`
* `TLSv1.1`
* `TLSv1.2`
* `TLSv1.3`
This function can be called in any context where downstream https is used.
[Back to TOC](#table-of-contents)
parse_pem_cert
--------------
**syntax:** *cert_chain, err = ssl.parse_pem_cert(pem_cert_chain)*
**context:** *any*
Converts the PEM-formated SSL certificate chain data into an opaque cdata pointer (for later uses
in the [set_cert](#set_cert)
function, for example).
In case of failures, returns `nil` and a string describing the error.
You can always use libraries like [lua-resty-lrucache](https://github.com/openresty/lua-resty-lrucache#readme)
to cache the cdata result.
This function can be called in any context.
This function was first added in version `0.1.7`.
[Back to TOC](#table-of-contents)
parse_pem_priv_key
------------------
**syntax:** *priv_key, err = ssl.parse_pem_priv_key(pem_priv_key)*
**context:** *any*
Converts the PEM-formatted SSL private key data into an opaque cdata pointer (for later uses
in the [set_priv_key](#set_priv_key)
function, for example).
In case of failures, returns `nil` and a string describing the error.
This function can be called in any context.
This function was first added in version `0.1.7`.
[Back to TOC](#table-of-contents)
set_cert
--------
**syntax:** *ok, err = ssl.set_cert(cert_chain)*
**context:** *ssl_certificate_by_lua&#42;*
Sets the SSL certificate chain opaque pointer returned by the
[parse_pem_cert](#parse_pem_cert) function for the current SSL connection.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
Note that this `set_cert` function will run slightly faster, in terms of CPU cycles wasted, than the
[set_der_cert](#set_der_cert) variant, since the first function uses opaque cdata pointers
which do not require any additional conversion needed to be performed by the SSL library during the SSL handshake.
This function was first added in version `0.1.7`.
[Back to TOC](#table-of-contents)
set_priv_key
------------
**syntax:** *ok, err = ssl.set_priv_key(priv_key)*
**context:** *ssl_certificate_by_lua&#42;*
Sets the SSL private key opaque pointer returned by the
[parse_pem_priv_key](#parse_pem_priv_key) function for the current SSL connection.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
Note that this `set_priv_key` function will run slightly faster, in terms of CPU cycles wasted, than the
[set_der_priv_key](#set_der_priv_key) variant, since the first function uses opaque cdata pointers
which do not require any additional conversion needed to be performed by the SSL library during the SSL handshake.
This function was first added in version `0.1.7`.
[Back to TOC](#table-of-contents)
verify_client
-------------
**syntax:** *ok, err = ssl.verify_client(ca_certs?, depth?)*
**context:** *ssl_certificate_by_lua&#42;*
Requires a client certificate during TLS handshake.
The `ca_certs` is the CA certificate chain opaque pointer returned by the
[parse_pem_cert](#parse_pem_cert) function for the current SSL connection.
The list of certificates will be sent to clients. Also, they will be added to trusted store.
If omitted, will not send any CA certificate to clients.
The `depth` is the verification depth in the client certificates chain.
If omitted, will use the value specified by `ssl_verify_depth`.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
Note that TLS is not terminated when verification fails. You need to examine Nginx variable `$ssl_client_verify`
later to determine next steps.
This function was first added in version `0.1.20`.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Yichun Zhang &lt;agentzh@gmail.com&gt; (agentzh), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2015-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* the [ngx.ocsp](ocsp.md) module.
* the [ssl_certificate_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_certificate_by_lua_block) directive.
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

232
lib/ngx/ssl/clienthello.lua Normal file
View File

@ -0,0 +1,232 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local ffi = require "ffi"
local bit = require "bit"
local bor = bit.bor
local C = ffi.C
local ffi_str = ffi.string
local get_request = base.get_request
local error = error
local errmsg = base.get_errmsg_ptr()
local get_size_ptr = base.get_size_ptr
local FFI_OK = base.FFI_OK
local subsystem = ngx.config.subsystem
local ngx_phase = ngx.get_phase
local byte = string.byte
local lshift = bit.lshift
local table_insert = table.insert
local ngx_lua_ffi_ssl_get_client_hello_server_name
local ngx_lua_ffi_ssl_get_client_hello_ext
local ngx_lua_ffi_ssl_set_protocols
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_ssl_get_client_hello_server_name(ngx_http_request_t *r,
const char **name, size_t *namelen, char **err);
int ngx_http_lua_ffi_ssl_get_client_hello_ext(ngx_http_request_t *r,
unsigned int type, const unsigned char **out, size_t *outlen,
char **err);
int ngx_http_lua_ffi_ssl_set_protocols(ngx_http_request_t *r,
int protocols, char **err);
]]
ngx_lua_ffi_ssl_get_client_hello_server_name =
C.ngx_http_lua_ffi_ssl_get_client_hello_server_name
ngx_lua_ffi_ssl_get_client_hello_ext =
C.ngx_http_lua_ffi_ssl_get_client_hello_ext
ngx_lua_ffi_ssl_set_protocols = C.ngx_http_lua_ffi_ssl_set_protocols
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_ssl_get_client_hello_server_name(
ngx_stream_lua_request_t *r, const char **name, size_t *namelen,
char **err);
int ngx_stream_lua_ffi_ssl_get_client_hello_ext(
ngx_stream_lua_request_t *r, unsigned int type,
const unsigned char **out, size_t *outlen, char **err);
int ngx_stream_lua_ffi_ssl_set_protocols(ngx_stream_lua_request_t *r,
int protocols, char **err);
]]
ngx_lua_ffi_ssl_get_client_hello_server_name =
C.ngx_stream_lua_ffi_ssl_get_client_hello_server_name
ngx_lua_ffi_ssl_get_client_hello_ext =
C.ngx_stream_lua_ffi_ssl_get_client_hello_ext
ngx_lua_ffi_ssl_set_protocols = C.ngx_stream_lua_ffi_ssl_set_protocols
end
local _M = { version = base.version }
local ccharpp = ffi.new("const char*[1]")
local cucharpp = ffi.new("const unsigned char*[1]")
-- return server_name, err
function _M.get_client_hello_server_name()
local r = get_request()
if not r then
error("no request found")
end
if ngx_phase() ~= "ssl_client_hello" then
error("API disabled in the current context")
end
local sizep = get_size_ptr()
local rc = ngx_lua_ffi_ssl_get_client_hello_server_name(r, ccharpp, sizep,
errmsg)
if rc == FFI_OK then
return ffi_str(ccharpp[0], sizep[0])
end
-- NGX_DECLINED: no sni extension
if rc == -5 then
return nil
end
return nil, ffi_str(errmsg[0])
end
-- return ext, err
function _M.get_client_hello_ext(ext_type)
local r = get_request()
if not r then
error("no request found")
end
if ngx_phase() ~= "ssl_client_hello" then
error("API disabled in the current context")
end
local sizep = get_size_ptr()
local rc = ngx_lua_ffi_ssl_get_client_hello_ext(r, ext_type, cucharpp,
sizep, errmsg)
if rc == FFI_OK then
return ffi_str(cucharpp[0], sizep[0])
end
-- NGX_DECLINED: no extension
if rc == -5 then
return nil
end
return nil, ffi_str(errmsg[0])
end
-- tls.handshake.extension.type supported_version
local supported_versions_type = 43
local versions_map = {
[0x002] = "SSLv2",
[0x300] = "SSLv3",
[0x301] = "TLSv1",
[0x302] = "TLSv1.1",
[0x303] = "TLSv1.2",
[0x304] = "TLSv1.3",
}
-- return types, err
function _M.get_supported_versions()
local r = get_request()
if not r then
error("no request found")
end
if ngx_phase() ~= "ssl_client_hello" then
error("API disabled in the current context")
end
local sizep = get_size_ptr()
local rc = ngx_lua_ffi_ssl_get_client_hello_ext(r, supported_versions_type,
cucharpp, sizep, errmsg)
if rc ~= FFI_OK then
-- NGX_DECLINED: no extension
if rc == -5 then
return nil
end
return nil, ffi_str(errmsg[0])
end
local supported_versions_str = ffi_str(cucharpp[0], sizep[0])
local remain_len = #supported_versions_str
if remain_len == 0 then
return nil
end
local supported_versions_len = byte(supported_versions_str, 1)
remain_len = remain_len - 1
if remain_len ~= supported_versions_len then
return nil
end
local types = {}
while remain_len >= 2 do
local type_hi = byte(supported_versions_str, remain_len)
local type_lo = byte(supported_versions_str, remain_len + 1)
local type_id = lshift(type_hi, 8) + type_lo
if versions_map[type_id] ~= nil then
table_insert(types, versions_map[type_id])
end
remain_len = remain_len - 2
end
return types
end
local prot_map = {
["SSLv2"] = 0x0002,
["SSLv3"] = 0x0004,
["TLSv1"] = 0x0008,
["TLSv1.1"] = 0x0010,
["TLSv1.2"] = 0x0020,
["TLSv1.3"] = 0x0040
}
-- return ok, err
function _M.set_protocols(protocols)
local r = get_request()
if not r then
error("no request found")
end
if ngx_phase() ~= "ssl_client_hello" then
error("API disabled in the current context")
end
local prots = 0
for _, v in ipairs(protocols) do
if not prot_map[v] then
return nil, "invalid protocols failed"
end
prots = bor(prots, prot_map[v])
end
local rc = ngx_lua_ffi_ssl_set_protocols(r, prots, errmsg)
if rc == FFI_OK then
return true
end
return nil, ffi_str(errmsg[0])
end
return _M

273
lib/ngx/ssl/clienthello.md Normal file
View File

@ -0,0 +1,273 @@
Name
====
ngx.ssl.clienthello - Lua API for post-processing SSL client hello message for NGINX downstream SSL connections.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [get_client_hello_server_name](#get_client_hello_server_name)
* [get_client_hello_ext](#get_client_hello_ext)
* [set_protocols](#set_protocols)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered experimental.
Synopsis
========
```nginx
# nginx.conf
server {
listen 443 ssl;
server_name test.com;
ssl_certificate /path/to/cert.crt;
ssl_certificate_key /path/to/key.key;
ssl_client_hello_by_lua_block {
local ssl_clt = require "ngx.ssl.clienthello"
local host, err = ssl_clt.get_client_hello_server_name()
if host == "test.com" then
ssl_clt.set_protocols({"TLSv1", "TLSv1.1"})
elseif host == "test2.com" then
ssl_clt.set_protocols({"TLSv1.2", "TLSv1.3"})
elseif not host then
ngx.log(ngx.ERR, "failed to get the SNI name: ", err)
ngx.exit(ngx.ERROR)
else
ngx.log(ngx.ERR, "unknown SNI name: ", host)
ngx.exit(ngx.ERROR)
end
}
...
}
server {
listen 443 ssl;
server_name test2.com;
ssl_certificate /path/to/cert.crt;
ssl_certificate_key /path/to/key.key;
...
}
```
Description
===========
This Lua module provides API functions for post-processing SSL client hello message for NGINX downstream connections.
It must to be used in the contexts [ssl_client_hello_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_client_hello_by_lua_block).
This Lua API is particularly useful for dynamically setting the SSL protocols according to the SNI.
It is also useful to do some custom operations according to the per-connection information in the client hello message.
For example, one can parse the custom client hello extension and do the corresponding handling in pure Lua.
To load the `ngx.ssl.clienthello` module in Lua, just write
```lua
local ssl_clt = require "ngx.ssl.clienthello"
```
[Back to TOC](#table-of-contents)
Methods
=======
get_client_hello_server_name
--------------
**syntax:** *host, err = ssl_clt.get_client_hello_server_name()*
**context:** *ssl_client_hello_by_lua&#42;*
Returns the TLS SNI (Server Name Indication) name set by the client.
Return `nil` when then the extension does not exist.
In case of errors, it returns `nil` and a string describing the error.
Note that the SNI name is gotten from the raw extensions of the client hello message associated with the current downstream SSL connection.
So this function can only be called in the context of [ssl_client_hello_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_client_hello_by_lua_block).
[Back to TOC](#table-of-contents)
get_supported_versions
--------------
**syntax:** *types, err = ssl_clt.get_supported_versions()*
**context:** *ssl_client_hello_by_lua&#42;*
Returns the table of ssl hello supported versions set by the client.
Return `nil` when then the extension does not exist.
In case of errors, it returns `nil` and a string describing the error.
Note that the types is gotten from the raw extensions of the client hello message associated with the current downstream SSL connection.
So this function can only be called in the context of [ssl_client_hello_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_client_hello_by_lua_block).
[Back to TOC](#table-of-contents)
get_client_hello_ext
----------------------
**syntax:** *ext, err = ssl_clt.get_client_hello_ext(ext_type)*
**context:** *ssl_client_hello_by_lua&#42;*
Returns raw data of arbitrary SSL client hello extension including custom extensions.
Returns `nil` if the specified extension type does not exist.
In case of errors, it returns `nil` and a string describing the error.
Note that the ext is gotten from the raw extensions of the client hello message associated with the current downstream SSL connection.
So this function can only be called in the context of [ssl_client_hello_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_client_hello_by_lua_block).
Example:
Gets server name from raw extension data. The `0` in `ssl_clt.get_client_hello_ext(0)` denotes `TLSEXT_TYPE_server_name`, and the `0` in `byte(ext, 3) ~= 0` denotes `TLSEXT_NAMETYPE_host_name`.
```nginx
# nginx.conf
server {
listen 443 ssl;
server_name test.com;
ssl_client_hello_by_lua_block {
local ssl_clt = require "ngx.ssl.clienthello"
local byte = string.byte
local ext = ssl_clt.get_client_hello_ext(0)
if not ext then
print("failed to get_client_hello_ext(0)")
ngx.exit(ngx.ERROR)
end
local total_len = string.len(ext)
if total_len <= 2 then
print("bad SSL Client Hello Extension")
ngx.exit(ngx.ERROR)
end
local len = byte(ext, 1) * 256 + byte(ext, 2)
if len + 2 ~= total_len then
print("bad SSL Client Hello Extension")
ngx.exit(ngx.ERROR)
end
if byte(ext, 3) ~= 0 then
print("bad SSL Client Hello Extension")
ngx.exit(ngx.ERROR)
end
if total_len <= 5 then
print("bad SSL Client Hello Extension")
ngx.exit(ngx.ERROR)
end
len = byte(ext, 4) * 256 + byte(ext, 5)
if len + 5 > total_len then
print("bad SSL Client Hello Extension")
ngx.exit(ngx.ERROR)
end
local name = string.sub(ext, 6, 6 + len -1)
print("read SNI name from Lua: ", name)
}
ssl_certificate test.crt;
ssl_certificate_key test.key;
}
```
[Back to TOC](#table-of-contents)
set_protocols
----------------------
**syntax:** *ok, err = ssl_clt.set_protocols(protocols)*
**context:** *ssl_client_hello_by_lua&#42;*
Sets the SSL protocols supported by the current downstream SSL connection.
Returns `true` on success, or a `nil` value and a string describing the error otherwise.
Considering it is meaningless to set ssl protocols after the protocol is determined,
so this function may only be called in the context of [ssl_client_hello_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_client_hello_by_lua_block).
Example: `ssl_clt.set_protocols({"TLSv1.1", "TLSv1.2", "TLSv1.3"})`
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Zhefeng Chen &lt;chzf\_zju@163.com&gt; (catbro666)
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2016-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* the [ssl_client_hello_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_client_hello_by_lua_block) directive.
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

109
lib/ngx/ssl/session.lua Normal file
View File

@ -0,0 +1,109 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
base.allows_subsystem('http')
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local get_request = base.get_request
local error = error
local errmsg = base.get_errmsg_ptr()
local get_string_buf = base.get_string_buf
local FFI_ERROR = base.FFI_ERROR
ffi.cdef[[
int ngx_http_lua_ffi_ssl_set_serialized_session(ngx_http_request_t *r,
const unsigned char *buf, int len, char **err);
int ngx_http_lua_ffi_ssl_get_serialized_session(ngx_http_request_t *r,
char *buf, char **err);
int ngx_http_lua_ffi_ssl_get_session_id(ngx_http_request_t *r,
char *buf, char **err);
int ngx_http_lua_ffi_ssl_get_serialized_session_size(ngx_http_request_t *r,
char **err);
int ngx_http_lua_ffi_ssl_get_session_id_size(ngx_http_request_t *r,
char **err);
]]
local _M = { version = base.version }
-- return session, err
function _M.get_serialized_session()
local r = get_request()
if not r then
error("no request found")
end
local len = C.ngx_http_lua_ffi_ssl_get_serialized_session_size(r, errmsg)
if len < 0 then
return nil, ffi_str(errmsg[0])
end
if len > 4096 then
return nil, "session too big to serialize"
end
local buf = get_string_buf(len)
local rc = C.ngx_http_lua_ffi_ssl_get_serialized_session(r, buf, errmsg)
if rc == FFI_ERROR then
return nil, ffi_str(errmsg[0])
end
return ffi_str(buf, len)
end
-- return session_id, err
function _M.get_session_id()
local r = get_request()
if not r then
error("no request found")
end
local len = C.ngx_http_lua_ffi_ssl_get_session_id_size(r, errmsg)
if len < 0 then
return nil, ffi_str(errmsg[0])
end
local buf = get_string_buf(len)
local rc = C.ngx_http_lua_ffi_ssl_get_session_id(r, buf, errmsg)
if rc == FFI_ERROR then
return nil, ffi_str(errmsg[0])
end
return ffi_str(buf, len)
end
-- return ok, err
function _M.set_serialized_session(sess)
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_ssl_set_serialized_session(r, sess, #sess,
errmsg)
if rc == FFI_ERROR then
return nil, ffi_str(errmsg[0])
end
return true
end
return _M

277
lib/ngx/ssl/session.md Normal file
View File

@ -0,0 +1,277 @@
Name
====
ngx.ssl.session - Lua API for manipulating SSL session data and IDs for NGINX downstream SSL connections.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [get_session_id](#get_session_id)
* [get_serialized_session](#get_serialized_session)
* [set_serialized_session](#set_serialized_session)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Author](#author)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is production ready.
Synopsis
========
```nginx
# nginx.conf
# Note: you do not need the following line if you are using
# OpenResty 1.11.2.1+.
lua_package_path "/path/to/lua-resty-core/lib/?.lua;;";
ssl_session_fetch_by_lua_block {
local ssl_sess = require "ngx.ssl.session"
local sess_id, err = ssl_sess.get_session_id()
if not sess_id then
ngx.log(ngx.ERR, "failed to get session ID: ", err)
-- considered a cache miss, and just return...
return
end
-- the user is supposed to implement the my_lookup_ssl_session_by_id
-- Lua function used below. She can look up an external memcached
-- or redis cluster, for example. And she can also introduce a local
-- cache layer at the same time...
local sess, err = my_lookup_ssl_session_by_id(sess_id)
if not sess then
if err then
ngx.log(ngx.ERR, "failed to look up the session by ID ",
sess_id, ": ", err)
return
end
-- cache miss...just return
return
end
local ok, err = ssl_sess.set_serialized_session(sess)
if not ok then
ngx.log(ngx.ERR, "failed to set SSL session for ID ", sess_id,
": ", err)
-- consider it as a cache miss...
return
end
-- done here, SSL session successfully set and should resume accordingly...
}
ssl_session_store_by_lua_block {
local ssl_sess = require "ngx.ssl.session"
local sess_id, err = ssl_sess.get_session_id()
if not sess_id then
ngx.log(ngx.ERR, "failed to get session ID: ", err)
-- just give up
return
end
local sess, err = ssl_sess.get_serialized_session()
if not sess then
ngx.log(ngx.ERR, "failed to get SSL session from the ",
"current connection: ", err)
-- just give up
return
end
-- for the best performance, we should avoid creating a closure
-- dynamically here on the hot code path. Instead, we should
-- put this function in one of our own Lua module files. this
-- example is just for demonstration purposes...
local function save_it(premature, sess_id, sess)
-- the user is supposed to implement the
-- my_save_ssl_session_by_id Lua function used below.
-- She can save to an external memcached
-- or redis cluster, for example. And she can also introduce
-- a local cache layer at the same time...
local sess, err = my_save_ssl_session_by_id(sess_id, sess)
if not sess then
if err then
ngx.log(ngx.ERR, "failed to save the session by ID ",
sess_id, ": ", err)
return ngx.exit(ngx.ERROR)
end
-- cache miss...just return
return
end
end
-- create a 0-delay timer here...
local ok, err = ngx.timer.at(0, save_it, sess_id, sess)
if not ok then
ngx.log(ngx.ERR, "failed to create a 0-delay timer: ", err)
return
end
}
server {
listen 443 ssl;
server_name test.com;
# well, we could configure ssl_certificate_by_lua* here as well...
ssl_certificate /path/to/server-cert.pem;
ssl_certificate_key /path/to/server-priv-key.pem;
}
```
Description
===========
This Lua module provides API functions for manipulating SSL session data and IDs for NGINX
downstream connections. It is mostly for the contexts [ssl_session_fetch_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_session_fetch_by_lua_block)
and [ssl_session_store_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_session_store_by_lua_block).
This Lua API can be used to implement distributed SSL session caching for downstream SSL connections, thus saving a lot of full SSL handshakes which are very expensive.
To load the `ngx.ssl.session` module in Lua, just write
```lua
local ssl_sess = require "ngx.ssl.session"
```
[Back to TOC](#table-of-contents)
Methods
=======
get_session_id
--------------
**syntax:** *id, err = ssl_sess.get_session_id()*
**context:** *ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;*
Fetches the SSL session ID associated with the current downstream SSL connection.
The ID is returned as a Lua string.
In case of errors, it returns `nil` and a string describing the error.
This API function is usually called in the contexts of
[ssl_session_store_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_session_store_by_lua_block)
and [ssl_session_fetch_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_session_fetch_by_lua_block).
[Back to TOC](#table-of-contents)
get_serialized_session
----------------------
**syntax:** *session, err = ssl_sess.get_serialized_session()*
**context:** *ssl_session_store_by_lua&#42;*
Returns the serialized form of the SSL session data of the current SSL connection, in a Lua string.
This session can be cached in [lua-resty-lrucache](https://github.com/openresty/lua-resty-lrucache), [lua_shared_dict](https://github.com/openresty/lua-nginx-module#lua_shared_dict),
and/or external data storage services like `memcached` and `redis`. The SSL session ID returned
by the [get_session_id](#get_session_id) function is usually used as the cache key.
The returned SSL session data can later be loaded into other SSL connections using the same
session ID via the [set_serialized_session](#set_serialized_session) function.
In case of errors, it returns `nil` and a string describing the error.
This API function is usually called in the context of
[ssl_session_store_by_lua*](https://github.com/openresty/lua-nginx-module#ssl_session_store_by_lua_block)
where the SSL handshake has just completed.
[Back to TOC](#table-of-contents)
set_serialized_session
----------------------
**syntax:** *ok, err = ssl_sess.set_serialized_session(session)*
**context:** *ssl_session_fetch_by_lua&#42;*
Sets the serialized SSL session provided as the argument to the current SSL connection.
If the SSL session is successfully set, the current SSL connection can resume the session
directly without going through the full SSL handshake process (which is very expensive in terms of CPU time).
This API is usually used in the context of [ssl_session_fetch_by_lua*](https://github.com/openresty/lua-nginx-module#ssl_session_fetch_by_lua_block)
when a cache hit is found with the current SSL session ID.
The serialized SSL session used as the argument should be originally returned by the
[get_serialized_session](#get_serialized_session) function.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Author
======
Yichun Zhang &lt;agentzh@gmail.com&gt; (agentzh), OpenResty Inc.
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2016-2017, by Yichun "agentzh" Zhang, OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* the [ssl_session_fetch_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_session_fetch_by_lua_block) directive.
* the [ssl_session_store_by_lua*](https://github.com/openresty/lua-nginx-module/#ssl_session_store_by_lua_block) directive.
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

37
lib/resty/core.lua Normal file
View File

@ -0,0 +1,37 @@
-- Copyright (C) Yichun Zhang (agentzh)
local subsystem = ngx.config.subsystem
require "resty.core.var"
require "resty.core.worker"
require "resty.core.regex"
require "resty.core.shdict"
require "resty.core.time"
require "resty.core.hash"
require "resty.core.uri"
require "resty.core.exit"
require "resty.core.base64"
require "resty.core.request"
if subsystem == 'http' then
require "resty.core.response"
require "resty.core.phase"
require "resty.core.ndk"
require "resty.core.socket"
require "resty.core.coroutine"
require "resty.core.param"
end
require "resty.core.misc"
require "resty.core.ctx"
local base = require "resty.core.base"
return {
version = base.version
}

269
lib/resty/core/base.lua Normal file
View File

@ -0,0 +1,269 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local ffi_new = ffi.new
local pcall = pcall
local error = error
local select = select
local ceil = math.ceil
local subsystem = ngx.config.subsystem
local str_buf_size = 4096
local str_buf
local size_ptr
local FREE_LIST_REF = 0
if subsystem == 'http' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 10025
then
error("ngx_http_lua_module 0.10.25 required")
end
elseif subsystem == 'stream' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 13
then
error("ngx_stream_lua_module 0.0.13 required")
end
else
error("ngx_http_lua_module 0.10.25 or "
.. "ngx_stream_lua_module 0.0.13 required")
end
if string.find(jit.version, " 2.0", 1, true) then
ngx.log(ngx.ALERT, "use of lua-resty-core with LuaJIT 2.0 is ",
"not recommended; use LuaJIT 2.1+ instead")
end
local ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function (narr, nrec) return {} end
end
local clear_tab
ok, clear_tab = pcall(require, "table.clear")
if not ok then
local pairs = pairs
clear_tab = function (tab)
for k, _ in pairs(tab) do
tab[k] = nil
end
end
end
-- XXX for now LuaJIT 2.1 cannot compile require()
-- so we make the fast code path Lua only in our own
-- wrapper so that most of the require() calls in hot
-- Lua code paths can be JIT compiled.
do
local orig_require = require
local pkg_loaded = package.loaded
-- the key_sentinel is inserted into package.loaded before
-- the chunk is executed and replaced if the chunk completes normally.
local key_sentinel = pkg_loaded[...]
local function my_require(name)
local mod = pkg_loaded[name]
if mod then
if mod == key_sentinel then
error("loop or previous error loading module '" .. name .. "'")
end
return mod
end
return orig_require(name)
end
getfenv(0).require = my_require
end
if not pcall(ffi.typeof, "ngx_str_t") then
ffi.cdef[[
typedef struct {
size_t len;
const unsigned char *data;
} ngx_str_t;
]]
end
if subsystem == 'http' then
if not pcall(ffi.typeof, "ngx_http_request_t") then
ffi.cdef[[
typedef struct ngx_http_request_s ngx_http_request_t;
]]
end
if not pcall(ffi.typeof, "ngx_http_lua_ffi_str_t") then
ffi.cdef[[
typedef struct {
int len;
const unsigned char *data;
} ngx_http_lua_ffi_str_t;
]]
end
elseif subsystem == 'stream' then
if not pcall(ffi.typeof, "ngx_stream_lua_request_t") then
ffi.cdef[[
typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t;
]]
end
if not pcall(ffi.typeof, "ngx_stream_lua_ffi_str_t") then
ffi.cdef[[
typedef struct {
int len;
const unsigned char *data;
} ngx_stream_lua_ffi_str_t;
]]
end
else
error("unknown subsystem: " .. subsystem)
end
local c_buf_type = ffi.typeof("char[?]")
local _M = new_tab(0, 18)
_M.version = "0.1.27"
_M.new_tab = new_tab
_M.clear_tab = clear_tab
local errmsg
function _M.get_errmsg_ptr()
if not errmsg then
errmsg = ffi_new("char *[1]")
end
return errmsg
end
if not ngx then
error("no existing ngx. table found")
end
function _M.set_string_buf_size(size)
if size <= 0 then
return
end
if str_buf then
str_buf = nil
end
str_buf_size = ceil(size)
end
function _M.get_string_buf_size()
return str_buf_size
end
function _M.get_size_ptr()
if not size_ptr then
size_ptr = ffi_new("size_t[1]")
end
return size_ptr
end
function _M.get_string_buf(size, must_alloc)
-- ngx.log(ngx.ERR, "str buf size: ", str_buf_size)
if size > str_buf_size or must_alloc then
local buf = ffi_new(c_buf_type, size)
return buf
end
if not str_buf then
str_buf = ffi_new(c_buf_type, str_buf_size)
end
return str_buf
end
function _M.ref_in_table(tb, key)
if key == nil then
return -1
end
local ref = tb[FREE_LIST_REF]
if ref and ref ~= 0 then
tb[FREE_LIST_REF] = tb[ref]
else
ref = #tb + 1
end
tb[ref] = key
-- print("ref key_id returned ", ref)
return ref
end
function _M.allows_subsystem(...)
local total = select("#", ...)
for i = 1, total do
if select(i, ...) == subsystem then
return
end
end
error("unsupported subsystem: " .. subsystem, 2)
end
_M.FFI_OK = 0
_M.FFI_NO_REQ_CTX = -100
_M.FFI_BAD_CONTEXT = -101
_M.FFI_ERROR = -1
_M.FFI_AGAIN = -2
_M.FFI_BUSY = -3
_M.FFI_DONE = -4
_M.FFI_DECLINED = -5
_M.FFI_ABORT = -6
do
local exdata
ok, exdata = pcall(require, "thread.exdata")
if ok and exdata then
function _M.get_request()
local r = exdata()
if r ~= nil then
return r
end
end
else
local getfenv = getfenv
function _M.get_request()
return getfenv(0).__ngx_req
end
end
end
return _M

115
lib/resty/core/base64.lua Normal file
View File

@ -0,0 +1,115 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local floor = math.floor
local tostring = tostring
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_encode_base64
local ngx_lua_ffi_decode_base64
if subsystem == "http" then
ffi.cdef[[
size_t ngx_http_lua_ffi_encode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
int no_padding);
int ngx_http_lua_ffi_decode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
size_t *dlen);
]]
ngx_lua_ffi_encode_base64 = C.ngx_http_lua_ffi_encode_base64
ngx_lua_ffi_decode_base64 = C.ngx_http_lua_ffi_decode_base64
elseif subsystem == "stream" then
ffi.cdef[[
size_t ngx_stream_lua_ffi_encode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
int no_padding);
int ngx_stream_lua_ffi_decode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
size_t *dlen);
]]
ngx_lua_ffi_encode_base64 = C.ngx_stream_lua_ffi_encode_base64
ngx_lua_ffi_decode_base64 = C.ngx_stream_lua_ffi_decode_base64
end
local function base64_encoded_length(len, no_padding)
return no_padding and floor((len * 8 + 5) / 6) or
floor((len + 2) / 3) * 4
end
ngx.encode_base64 = function (s, no_padding)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local no_padding_bool = false;
local no_padding_int = 0;
if no_padding then
if no_padding ~= true then
local typ = type(no_padding)
error("bad no_padding: boolean expected, got " .. typ, 2)
end
no_padding_bool = true
no_padding_int = 1;
end
local dlen = base64_encoded_length(slen, no_padding_bool)
local dst = get_string_buf(dlen)
local r_dlen = ngx_lua_ffi_encode_base64(s, slen, dst, no_padding_int)
-- if dlen ~= r_dlen then error("discrepancy in len") end
return ffi_string(dst, r_dlen)
end
local function base64_decoded_length(len)
return floor((len + 3) / 4) * 3
end
ngx.decode_base64 = function (s)
if type(s) ~= 'string' then
error("string argument only", 2)
end
local slen = #s
local dlen = base64_decoded_length(slen)
-- print("dlen: ", tonumber(dlen))
local dst = get_string_buf(dlen)
local pdlen = get_size_ptr()
local ok = ngx_lua_ffi_decode_base64(s, slen, dst, pdlen)
if ok == 0 then
return nil
end
return ffi_string(dst, pdlen[0])
end
return {
version = base.version
}

View File

@ -0,0 +1,29 @@
local base = require "resty.core.base"
local get_request = base.get_request
do
local keys = {'create', 'yield', 'resume', 'status', 'wrap'}
local errmsg = base.get_errmsg_ptr()
local get_raw_phase = ngx.get_raw_phase
for _, key in ipairs(keys) do
local std = coroutine['_' .. key]
local ours = coroutine['__' .. key]
coroutine[key] = function (...)
local r = get_request()
if r ~= nil then
local ctx = get_raw_phase(r, errmsg)
if ctx ~= 0x020 and ctx ~= 0x040 then
return ours(...)
end
end
return std(...)
end
end
package.loaded.coroutine = coroutine
end
return {
version = base.version
}

147
lib/resty/core/ctx.lua Normal file
View File

@ -0,0 +1,147 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local debug = require "debug"
local base = require "resty.core.base"
local misc = require "resty.core.misc"
local C = ffi.C
local register_getter = misc.register_ngx_magic_key_getter
local register_setter = misc.register_ngx_magic_key_setter
local registry = debug.getregistry()
local new_tab = base.new_tab
local ref_in_table = base.ref_in_table
local get_request = base.get_request
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_OK = base.FFI_OK
local error = error
local setmetatable = setmetatable
local type = type
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_get_ctx_ref
local ngx_lua_ffi_set_ctx_ref
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r, int *in_ssl_phase,
int *ssl_ctx_ref);
int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref);
]]
ngx_lua_ffi_get_ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref
ngx_lua_ffi_set_ctx_ref = C.ngx_http_lua_ffi_set_ctx_ref
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r,
int *in_ssl_phase, int *ssl_ctx_ref);
int ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref);
]]
ngx_lua_ffi_get_ctx_ref = C.ngx_stream_lua_ffi_get_ctx_ref
ngx_lua_ffi_set_ctx_ref = C.ngx_stream_lua_ffi_set_ctx_ref
end
local _M = {
_VERSION = base.version
}
-- use a new ctxs table to make LuaJIT JIT compiler happy to generate more
-- efficient machine code.
local ctxs = {}
registry.ngx_lua_ctx_tables = ctxs
local get_ctx_table
do
local in_ssl_phase = ffi.new("int[1]")
local ssl_ctx_ref = ffi.new("int[1]")
function get_ctx_table(ctx)
local r = get_request()
if not r then
error("no request found")
end
local ctx_ref = ngx_lua_ffi_get_ctx_ref(r, in_ssl_phase, ssl_ctx_ref)
if ctx_ref == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if ctx_ref < 0 then
ctx_ref = ssl_ctx_ref[0]
if ctx_ref > 0 and ctxs[ctx_ref] then
if in_ssl_phase[0] ~= 0 then
return ctxs[ctx_ref]
end
if not ctx then
ctx = new_tab(0, 4)
end
ctx = setmetatable(ctx, ctxs[ctx_ref])
else
if in_ssl_phase[0] ~= 0 then
if not ctx then
ctx = new_tab(1, 4)
end
-- to avoid creating another table, we assume the users
-- won't overwrite the `__index` key
ctx.__index = ctx
elseif not ctx then
ctx = new_tab(0, 4)
end
end
ctx_ref = ref_in_table(ctxs, ctx)
if ngx_lua_ffi_set_ctx_ref(r, ctx_ref) ~= FFI_OK then
return nil
end
return ctx
end
return ctxs[ctx_ref]
end
end
register_getter("ctx", get_ctx_table)
_M.get_ctx_table = get_ctx_table
local function set_ctx_table(ctx)
local ctx_type = type(ctx)
if ctx_type ~= "table" then
error("ctx should be a table while getting a " .. ctx_type)
end
local r = get_request()
if not r then
error("no request found")
end
local ctx_ref = ngx_lua_ffi_get_ctx_ref(r, nil, nil)
if ctx_ref == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if ctx_ref < 0 then
ctx_ref = ref_in_table(ctxs, ctx)
ngx_lua_ffi_set_ctx_ref(r, ctx_ref)
return
end
ctxs[ctx_ref] = ctx
end
register_setter("ctx", set_ctx_table)
return _M

66
lib/resty/core/exit.lua Normal file
View File

@ -0,0 +1,66 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local error = error
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local get_request = base.get_request
local co_yield = coroutine._yield
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_exit
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status,
unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_exit = C.ngx_http_lua_ffi_exit
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status,
unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_exit = C.ngx_stream_lua_ffi_exit
end
local ERR_BUF_SIZE = 128
local FFI_DONE = base.FFI_DONE
ngx.exit = function (rc)
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
local r = get_request()
if r == nil then
error("no request found")
end
errlen[0] = ERR_BUF_SIZE
rc = ngx_lua_ffi_exit(r, rc, err, errlen)
if rc == 0 then
-- print("yielding...")
return co_yield()
end
if rc == FFI_DONE then
return
end
error(ffi_string(err, errlen[0]), 2)
end
return {
version = base.version
}

154
lib/resty/core/hash.lua Normal file
View File

@ -0,0 +1,154 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_new = ffi.new
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local tostring = tostring
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_md5
local ngx_lua_ffi_md5_bin
local ngx_lua_ffi_sha1_bin
local ngx_lua_ffi_crc32_long
local ngx_lua_ffi_crc32_short
if subsystem == "http" then
ffi.cdef[[
void ngx_http_lua_ffi_md5_bin(const unsigned char *src, size_t len,
unsigned char *dst);
void ngx_http_lua_ffi_md5(const unsigned char *src, size_t len,
unsigned char *dst);
int ngx_http_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
unsigned char *dst);
unsigned int ngx_http_lua_ffi_crc32_long(const unsigned char *src,
size_t len);
unsigned int ngx_http_lua_ffi_crc32_short(const unsigned char *src,
size_t len);
]]
ngx_lua_ffi_md5 = C.ngx_http_lua_ffi_md5
ngx_lua_ffi_md5_bin = C.ngx_http_lua_ffi_md5_bin
ngx_lua_ffi_sha1_bin = C.ngx_http_lua_ffi_sha1_bin
ngx_lua_ffi_crc32_short = C.ngx_http_lua_ffi_crc32_short
ngx_lua_ffi_crc32_long = C.ngx_http_lua_ffi_crc32_long
elseif subsystem == "stream" then
ffi.cdef[[
void ngx_stream_lua_ffi_md5_bin(const unsigned char *src, size_t len,
unsigned char *dst);
void ngx_stream_lua_ffi_md5(const unsigned char *src, size_t len,
unsigned char *dst);
int ngx_stream_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
unsigned char *dst);
unsigned int ngx_stream_lua_ffi_crc32_long(const unsigned char *src,
size_t len);
unsigned int ngx_stream_lua_ffi_crc32_short(const unsigned char *src,
size_t len);
]]
ngx_lua_ffi_md5 = C.ngx_stream_lua_ffi_md5
ngx_lua_ffi_md5_bin = C.ngx_stream_lua_ffi_md5_bin
ngx_lua_ffi_sha1_bin = C.ngx_stream_lua_ffi_sha1_bin
ngx_lua_ffi_crc32_short = C.ngx_stream_lua_ffi_crc32_short
ngx_lua_ffi_crc32_long = C.ngx_stream_lua_ffi_crc32_long
end
local MD5_DIGEST_LEN = 16
local md5_buf = ffi_new("unsigned char[?]", MD5_DIGEST_LEN)
ngx.md5_bin = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
ngx_lua_ffi_md5_bin(s, #s, md5_buf)
return ffi_string(md5_buf, MD5_DIGEST_LEN)
end
local MD5_HEX_DIGEST_LEN = MD5_DIGEST_LEN * 2
local md5_hex_buf = ffi_new("unsigned char[?]", MD5_HEX_DIGEST_LEN)
ngx.md5 = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
ngx_lua_ffi_md5(s, #s, md5_hex_buf)
return ffi_string(md5_hex_buf, MD5_HEX_DIGEST_LEN)
end
local SHA_DIGEST_LEN = 20
local sha_buf = ffi_new("unsigned char[?]", SHA_DIGEST_LEN)
ngx.sha1_bin = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local ok = ngx_lua_ffi_sha1_bin(s, #s, sha_buf)
if ok == 0 then
error("SHA-1 support missing in Nginx")
end
return ffi_string(sha_buf, SHA_DIGEST_LEN)
end
ngx.crc32_short = function (s)
if type(s) ~= "string" then
if not s then
s = ""
else
s = tostring(s)
end
end
return ngx_lua_ffi_crc32_short(s, #s)
end
ngx.crc32_long = function (s)
if type(s) ~= "string" then
if not s then
s = ""
else
s = tostring(s)
end
end
return ngx_lua_ffi_crc32_long(s, #s)
end
return {
version = base.version
}

258
lib/resty/core/misc.lua Normal file
View File

@ -0,0 +1,258 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
local ffi = require "ffi"
local os = require "os"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ngx = ngx
local type = type
local error = error
local rawget = rawget
local rawset = rawset
local tonumber = tonumber
local setmetatable = setmetatable
local FFI_OK = base.FFI_OK
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local new_tab = base.new_tab
local get_request = base.get_request
local get_size_ptr = base.get_size_ptr
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_get_resp_status
local ngx_lua_ffi_get_conf_env
local ngx_magic_key_getters
local ngx_magic_key_setters
local _M = new_tab(0, 3)
local ngx_mt = new_tab(0, 2)
if subsystem == "http" then
ngx_magic_key_getters = new_tab(0, 4)
ngx_magic_key_setters = new_tab(0, 2)
elseif subsystem == "stream" then
ngx_magic_key_getters = new_tab(0, 2)
ngx_magic_key_setters = new_tab(0, 1)
end
local function register_getter(key, func)
ngx_magic_key_getters[key] = func
end
_M.register_ngx_magic_key_getter = register_getter
local function register_setter(key, func)
ngx_magic_key_setters[key] = func
end
_M.register_ngx_magic_key_setter = register_setter
ngx_mt.__index = function (tb, key)
local f = ngx_magic_key_getters[key]
if f then
return f()
end
return rawget(tb, key)
end
ngx_mt.__newindex = function (tb, key, ctx)
local f = ngx_magic_key_setters[key]
if f then
return f(ctx)
end
return rawset(tb, key, ctx)
end
setmetatable(ngx, ngx_mt)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r);
int ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int r);
int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r);
int ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r);
int ngx_http_lua_ffi_get_conf_env(const unsigned char *name,
unsigned char **env_buf,
size_t *name_len);
int ngx_http_lua_ffi_req_is_internal(ngx_http_request_t *r);
]]
ngx_lua_ffi_get_resp_status = C.ngx_http_lua_ffi_get_resp_status
ngx_lua_ffi_get_conf_env = C.ngx_http_lua_ffi_get_conf_env
-- ngx.status
local function set_status(status)
local r = get_request()
if not r then
error("no request found")
end
if type(status) ~= 'number' then
status = tonumber(status)
end
local rc = C.ngx_http_lua_ffi_set_resp_status(r, status)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
end
register_setter("status", set_status)
-- ngx.is_subrequest
local function is_subreq()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_is_subrequest(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc == 1
end
register_getter("is_subrequest", is_subreq)
-- ngx.headers_sent
local function headers_sent()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_headers_sent(r)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc == 1
end
register_getter("headers_sent", headers_sent)
-- ngx.req.is_internal
function ngx.req.is_internal()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_req_is_internal(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context")
end
return rc == 1
end
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_get_resp_status(ngx_stream_lua_request_t *r);
int ngx_stream_lua_ffi_get_conf_env(const unsigned char *name,
unsigned char **env_buf,
size_t *name_len);
]]
ngx_lua_ffi_get_resp_status = C.ngx_stream_lua_ffi_get_resp_status
ngx_lua_ffi_get_conf_env = C.ngx_stream_lua_ffi_get_conf_env
end
-- ngx.status
local function get_status()
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_get_resp_status(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc
end
register_getter("status", get_status)
do
local _getenv = os.getenv
local env_ptr = ffi_new("unsigned char *[1]")
os.getenv = function (name)
local r = get_request()
if r then
-- past init_by_lua* phase now
os.getenv = _getenv
env_ptr = nil
return os.getenv(name)
end
local size = get_string_buf_size()
env_ptr[0] = get_string_buf(size)
local name_len_ptr = get_size_ptr()
local rc = ngx_lua_ffi_get_conf_env(name, env_ptr, name_len_ptr)
if rc == FFI_OK then
return ffi_str(env_ptr[0] + name_len_ptr[0] + 1)
end
-- FFI_DECLINED
local value = _getenv(name)
if value ~= nil then
return value
end
return nil
end
end
_M._VERSION = base.version
return _M

92
lib/resty/core/ndk.lua Normal file
View File

@ -0,0 +1,92 @@
-- Copyright (C) by OpenResty Inc.
local ffi = require 'ffi'
local base = require "resty.core.base"
base.allows_subsystem('http')
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_new = ffi.new
local ffi_str = ffi.string
local FFI_OK = base.FFI_OK
local new_tab = base.new_tab
local get_string_buf = base.get_string_buf
local get_request = base.get_request
local setmetatable = setmetatable
local type = type
local tostring = tostring
local error = error
local _M = {
version = base.version
}
ffi.cdef[[
typedef void * ndk_set_var_value_pt;
int ngx_http_lua_ffi_ndk_lookup_directive(const unsigned char *var_data,
size_t var_len, ndk_set_var_value_pt *func);
int ngx_http_lua_ffi_ndk_set_var_get(ngx_http_request_t *r,
ndk_set_var_value_pt func, const unsigned char *arg_data, size_t arg_len,
ngx_http_lua_ffi_str_t *value);
]]
local func_p = ffi_new("void*[1]")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local function ndk_set_var_get(self, var)
if type(var) ~= "string" then
var = tostring(var)
end
if C.ngx_http_lua_ffi_ndk_lookup_directive(var, #var, func_p) ~= FFI_OK then
error('ndk.set_var: directive "' .. var
.. '" not found or does not use ndk_set_var_value', 2)
end
local func = func_p[0]
return function (arg)
local r = get_request()
if not r then
error("no request found")
end
if type(arg) ~= "string" then
arg = tostring(arg)
end
local buf = get_string_buf(ffi_str_size)
local value = ffi_cast(ffi_str_type, buf)
local rc = C.ngx_http_lua_ffi_ndk_set_var_get(r, func, arg, #arg, value)
if rc ~= FFI_OK then
error("calling directive " .. var .. " failed with code " .. rc, 2)
end
return ffi_str(value.data, value.len)
end
end
local function ndk_set_var_set()
error("not allowed", 2)
end
if ndk then
local mt = new_tab(0, 2)
mt.__newindex = ndk_set_var_set
mt.__index = ndk_set_var_get
ndk.set_var = setmetatable(new_tab(0, 0), mt)
end
return _M

103
lib/resty/core/param.lua Normal file
View File

@ -0,0 +1,103 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
require "resty.core.phase" -- for ngx.get_phase
local C = ffi.C
local ffi_str = ffi.string
local FFI_AGAIN = base.FFI_AGAIN
local FFI_OK = base.FFI_OK
local get_request = base.get_request
local get_string_buf = base.get_string_buf
local getmetatable = getmetatable
local ngx = ngx
local ngx_phase = ngx.get_phase
local _M = {
version = base.version
}
ffi.cdef[[
typedef unsigned char u_char;
void ngx_http_lua_ffi_get_setby_param(ngx_http_request_t *r, int idx,
u_char **data, size_t *len);
int ngx_http_lua_ffi_get_body_filter_param_eof(ngx_http_request_t *r);
int ngx_http_lua_ffi_get_body_filter_param_body(ngx_http_request_t *r,
u_char **data_p, size_t *len_p);
int ngx_http_lua_ffi_copy_body_filter_param_body(ngx_http_request_t *r,
u_char *data);
]]
local data_p = ffi.new("unsigned char*[1]")
local len_p = ffi.new("size_t[1]")
local function get_setby_param(r, idx)
C.ngx_http_lua_ffi_get_setby_param(r, idx, data_p, len_p)
if len_p[0] == 0 then
return nil
end
return ffi_str(data_p[0], len_p[0])
end
local function get_body_filter_param(r, idx)
if idx == 1 then
data_p[0] = nil
local rc = C.ngx_http_lua_ffi_get_body_filter_param_body(r, data_p,
len_p)
if rc == FFI_AGAIN then
local buf = get_string_buf(len_p[0])
assert(C.ngx_http_lua_ffi_copy_body_filter_param_body(r, buf)
== FFI_OK)
return ffi_str(buf, len_p[0])
end
if len_p[0] == 0 then
return ""
end
return ffi_str(data_p[0], len_p[0])
elseif idx == 2 then
local rc = C.ngx_http_lua_ffi_get_body_filter_param_eof(r)
return rc == 1
else
return nil
end
end
local function get_param(tb, idx)
local r = get_request()
if not r then
error("no request found")
end
local phase = ngx_phase()
if phase == "set" then
return get_setby_param(r, idx)
end
if phase == "body_filter" then
return get_body_filter_param(r, idx)
end
error("API disabled in the current context")
end
do
local mt = getmetatable(ngx.arg)
mt.__index = get_param
end
return _M

71
lib/resty/core/phase.lua Normal file
View File

@ -0,0 +1,71 @@
local ffi = require 'ffi'
local base = require "resty.core.base"
local C = ffi.C
local FFI_ERROR = base.FFI_ERROR
local get_request = base.get_request
local error = error
local tostring = tostring
ffi.cdef[[
int ngx_http_lua_ffi_get_phase(ngx_http_request_t *r, char **err)
]]
local errmsg = base.get_errmsg_ptr()
local context_names = {
[0x0001] = "set",
[0x0002] = "rewrite",
[0x0004] = "access",
[0x0008] = "content",
[0x0010] = "log",
[0x0020] = "header_filter",
[0x0040] = "body_filter",
[0x0080] = "timer",
[0x0100] = "init_worker",
[0x0200] = "balancer",
[0x0400] = "ssl_cert",
[0x0800] = "ssl_session_store",
[0x1000] = "ssl_session_fetch",
[0x2000] = "exit_worker",
[0x4000] = "ssl_client_hello",
[0x8000] = "server_rewrite",
}
function ngx.get_phase()
local r = get_request()
-- if we have no request object, assume we are called from the "init" phase
if not r then
return "init"
end
local context = C.ngx_http_lua_ffi_get_phase(r, errmsg)
if context == FFI_ERROR then -- NGX_ERROR
error(errmsg, 2)
end
local phase = context_names[context]
if not phase then
error("unknown phase: " .. tostring(context))
end
return phase
end
function ngx.get_raw_phase(r)
local context = C.ngx_http_lua_ffi_get_phase(r, errmsg)
if context == FFI_ERROR then -- NGX_ERROR
error(errmsg, 2)
end
return context
end
return {
version = base.version
}

1213
lib/resty/core/regex.lua Normal file

File diff suppressed because it is too large Load Diff

462
lib/resty/core/request.lua Normal file
View File

@ -0,0 +1,462 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local utils = require "resty.core.utils"
local subsystem = ngx.config.subsystem
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_DECLINED = base.FFI_DECLINED
local FFI_OK = base.FFI_OK
local clear_tab = base.clear_tab
local new_tab = base.new_tab
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_new = ffi.new
local ffi_str = ffi.string
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local setmetatable = setmetatable
local lower = string.lower
local find = string.find
local rawget = rawget
local ngx = ngx
local get_request = base.get_request
local type = type
local error = error
local tostring = tostring
local tonumber = tonumber
local str_replace_char = utils.str_replace_char
local _M = {
version = base.version
}
local ngx_lua_ffi_req_start_time
if subsystem == "stream" then
ffi.cdef[[
double ngx_stream_lua_ffi_req_start_time(ngx_stream_lua_request_t *r);
]]
ngx_lua_ffi_req_start_time = C.ngx_stream_lua_ffi_req_start_time
elseif subsystem == "http" then
ffi.cdef[[
double ngx_http_lua_ffi_req_start_time(ngx_http_request_t *r);
]]
ngx_lua_ffi_req_start_time = C.ngx_http_lua_ffi_req_start_time
end
function ngx.req.start_time()
local r = get_request()
if not r then
error("no request found")
end
return tonumber(ngx_lua_ffi_req_start_time(r))
end
if subsystem == "stream" then
return _M
end
local errmsg = base.get_errmsg_ptr()
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
ffi.cdef[[
typedef struct {
ngx_http_lua_ffi_str_t key;
ngx_http_lua_ffi_str_t value;
} ngx_http_lua_ffi_table_elt_t;
int ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r,
int max, int *truncated);
int ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r,
ngx_http_lua_ffi_table_elt_t *out, int count, int raw);
int ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r,
int max, int *truncated);
size_t ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r,
unsigned char *buf, ngx_http_lua_ffi_table_elt_t *out, int count);
int ngx_http_lua_ffi_req_get_method(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r,
unsigned char **name, size_t *len);
int ngx_http_lua_ffi_req_set_method(ngx_http_request_t *r, int method);
int ngx_http_lua_ffi_req_set_header(ngx_http_request_t *r,
const unsigned char *key, size_t key_len, const unsigned char *value,
size_t value_len, ngx_http_lua_ffi_str_t *mvals, size_t mvals_len,
int override, char **errmsg);
]]
local table_elt_type = ffi.typeof("ngx_http_lua_ffi_table_elt_t*")
local table_elt_size = ffi.sizeof("ngx_http_lua_ffi_table_elt_t")
local truncated = ffi.new("int[1]")
local req_headers_mt = {
__index = function (tb, key)
key = lower(key)
local value = rawget(tb, key)
if value == nil and find(key, '_', 1, true) then
value = rawget(tb, (str_replace_char(key, '_', '-')))
end
return value
end
}
function ngx.req.get_headers(max_headers, raw)
local r = get_request()
if not r then
error("no request found")
end
if not max_headers then
max_headers = -1
end
if not raw then
raw = 0
else
raw = 1
end
local n = C.ngx_http_lua_ffi_req_get_headers_count(r, max_headers,
truncated)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
local headers = {}
if raw == 0 then
headers = setmetatable(headers, req_headers_mt)
end
return headers
end
local raw_buf = get_string_buf(n * table_elt_size)
local buf = ffi_cast(table_elt_type, raw_buf)
local rc = C.ngx_http_lua_ffi_req_get_headers(r, buf, n, raw)
if rc == 0 then
local headers = new_tab(0, n)
for i = 0, n - 1 do
local h = buf[i]
local key = h.key
key = ffi_str(key.data, key.len)
local value = h.value
value = ffi_str(value.data, value.len)
local existing = headers[key]
if existing then
if type(existing) == "table" then
existing[#existing + 1] = value
else
headers[key] = {existing, value}
end
else
headers[key] = value
end
end
if raw == 0 then
headers = setmetatable(headers, req_headers_mt)
end
if truncated[0] ~= 0 then
return headers, "truncated"
end
return headers
end
return nil
end
function ngx.req.get_uri_args(max_args, tab)
local r = get_request()
if not r then
error("no request found")
end
if not max_args then
max_args = -1
end
if tab then
clear_tab(tab)
end
local n = C.ngx_http_lua_ffi_req_get_uri_args_count(r, max_args, truncated)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return tab or {}
end
local args_len = C.ngx_http_lua_ffi_req_get_querystring_len(r)
local strbuf = get_string_buf(args_len + n * table_elt_size)
local kvbuf = ffi_cast(table_elt_type, strbuf + args_len)
local nargs = C.ngx_http_lua_ffi_req_get_uri_args(r, strbuf, kvbuf, n)
local args = tab or new_tab(0, nargs)
for i = 0, nargs - 1 do
local arg = kvbuf[i]
local key = arg.key
key = ffi_str(key.data, key.len)
local value = arg.value
local len = value.len
if len == -1 then
value = true
else
value = ffi_str(value.data, len)
end
local existing = args[key]
if existing then
if type(existing) == "table" then
existing[#existing + 1] = value
else
args[key] = {existing, value}
end
else
args[key] = value
end
end
if truncated[0] ~= 0 then
return args, "truncated"
end
return args
end
do
local methods = {
[0x0002] = "GET",
[0x0004] = "HEAD",
[0x0008] = "POST",
[0x0010] = "PUT",
[0x0020] = "DELETE",
[0x0040] = "MKCOL",
[0x0080] = "COPY",
[0x0100] = "MOVE",
[0x0200] = "OPTIONS",
[0x0400] = "PROPFIND",
[0x0800] = "PROPPATCH",
[0x1000] = "LOCK",
[0x2000] = "UNLOCK",
[0x4000] = "PATCH",
[0x8000] = "TRACE",
}
local namep = ffi_new("unsigned char *[1]")
function ngx.req.get_method()
local r = get_request()
if not r then
error("no request found")
end
do
local id = C.ngx_http_lua_ffi_req_get_method(r)
if id == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
local method = methods[id]
if method then
return method
end
end
local sizep = get_size_ptr()
local rc = C.ngx_http_lua_ffi_req_get_method_name(r, namep, sizep)
if rc ~= 0 then
return nil
end
return ffi_str(namep[0], sizep[0])
end
end -- do
function ngx.req.set_method(method)
local r = get_request()
if not r then
error("no request found")
end
if type(method) ~= "number" then
error("bad method number", 2)
end
local rc = C.ngx_http_lua_ffi_req_set_method(r, method)
if rc == FFI_OK then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if rc == FFI_DECLINED then
error("unsupported HTTP method: " .. method, 2)
end
error("unknown error: " .. rc)
end
do
local function set_req_header(name, value, override)
local r = get_request()
if not r then
error("no request found", 3)
end
if name == nil then
error("bad 'name' argument: string expected, got nil", 3)
end
if type(name) ~= "string" then
name = tostring(name)
end
local rc
if value == nil then
if not override then
error("bad 'value' argument: string or table expected, got nil",
3)
end
rc = C.ngx_http_lua_ffi_req_set_header(r, name, #name, nil, 0, nil,
0, 1, errmsg)
else
local sval, sval_len, mvals, mvals_len, buf
local value_type = type(value)
if value_type == "table" then
mvals_len = #value
if mvals_len == 0 and not override then
error("bad 'value' argument: non-empty table expected", 3)
end
buf = get_string_buf(ffi_str_size * mvals_len)
mvals = ffi_cast(ffi_str_type, buf)
for i = 1, mvals_len do
local s = value[i]
if type(s) ~= "string" then
s = tostring(s)
value[i] = s
end
local str = mvals[i - 1]
str.data = s
str.len = #s
end
sval_len = 0
else
if value_type ~= "string" then
sval = tostring(value)
else
sval = value
end
sval_len = #sval
mvals_len = 0
end
rc = C.ngx_http_lua_ffi_req_set_header(r, name, #name, sval,
sval_len, mvals, mvals_len,
override and 1 or 0, errmsg)
end
if rc == FFI_OK or rc == FFI_DECLINED then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 3)
end
-- rc == FFI_ERROR
error(ffi_str(errmsg[0]))
end
_M.set_req_header = set_req_header
function ngx.req.set_header(name, value)
set_req_header(name, value, true) -- override
end
end -- do
function ngx.req.clear_header(name)
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
name = tostring(name)
end
local rc = C.ngx_http_lua_ffi_req_set_header(r, name, #name, nil, 0, nil, 0,
1, errmsg)
if rc == FFI_OK or rc == FFI_DECLINED then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
-- rc == FFI_ERROR
error(ffi_str(errmsg[0]))
end
return _M

238
lib/resty/core/response.lua Normal file
View File

@ -0,0 +1,238 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_str = ffi.string
local new_tab = base.new_tab
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_DECLINED = base.FFI_DECLINED
local get_string_buf = base.get_string_buf
local setmetatable = setmetatable
local type = type
local tostring = tostring
local get_request = base.get_request
local error = error
local ngx = ngx
local _M = {
version = base.version
}
local MAX_HEADER_VALUES = 100
local errmsg = base.get_errmsg_ptr()
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
ffi.cdef[[
int ngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r,
const char *key_data, size_t key_len, int is_nil,
const char *sval, size_t sval_len, ngx_http_lua_ffi_str_t *mvals,
size_t mvals_len, int override, char **errmsg);
int ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r,
const unsigned char *key, size_t key_len,
unsigned char *key_buf, ngx_http_lua_ffi_str_t *values,
int max_nvalues, char **errmsg);
]]
local ngx_lua_ffi_set_resp_header
local MACOS = jit and jit.os == "OSX"
if MACOS then
ffi.cdef[[
typedef struct {
ngx_http_request_t *r;
const char *key_data;
size_t key_len;
int is_nil;
const char *sval;
size_t sval_len;
void *mvals;
size_t mvals_len;
int override;
char **errmsg;
} ngx_http_lua_set_resp_header_params_t;
int ngx_http_lua_ffi_set_resp_header_macos(
ngx_http_lua_set_resp_header_params_t *p);
]]
local set_params = ffi.new("ngx_http_lua_set_resp_header_params_t")
ngx_lua_ffi_set_resp_header = function(r, key, key_len, is_nil,
sval, sval_len, mvals,
mvals_len, override, err)
set_params.r = r
set_params.key_data = key
set_params.key_len = key_len
set_params.is_nil = is_nil
set_params.sval = sval
set_params.sval_len = sval_len
set_params.mvals = mvals
set_params.mvals_len = mvals_len
set_params.override = override
set_params.errmsg = err
return C.ngx_http_lua_ffi_set_resp_header_macos(set_params)
end
else
ngx_lua_ffi_set_resp_header = function(r, key, key_len, is_nil,
sval, sval_len, mvals,
mvals_len, override, err)
return C.ngx_http_lua_ffi_set_resp_header(r, key, key_len, is_nil,
sval, sval_len, mvals,
mvals_len, override, err)
end
end
local function set_resp_header(tb, key, value, no_override)
local r = get_request()
if not r then
error("no request found")
end
if type(key) ~= "string" then
key = tostring(key)
end
local rc
if value == nil then
if no_override then
error("invalid header value", 3)
end
rc = ngx_lua_ffi_set_resp_header(r, key, #key, true, nil, 0, nil,
0, 1, errmsg)
else
local sval, sval_len, mvals, mvals_len, buf
if type(value) == "table" then
mvals_len = #value
if mvals_len == 0 and no_override then
return
end
buf = get_string_buf(ffi_str_size * mvals_len)
mvals = ffi_cast(ffi_str_type, buf)
for i = 1, mvals_len do
local s = value[i]
if type(s) ~= "string" then
s = tostring(s)
value[i] = s
end
local str = mvals[i - 1]
str.data = s
str.len = #s
end
sval_len = 0
else
if type(value) ~= "string" then
sval = tostring(value)
else
sval = value
end
sval_len = #sval
mvals_len = 0
end
local override_int = no_override and 0 or 1
rc = ngx_lua_ffi_set_resp_header(r, key, #key, false, sval,
sval_len, mvals, mvals_len,
override_int, errmsg)
end
if rc == 0 or rc == FFI_DECLINED then
return
end
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
-- rc == FFI_ERROR
error(ffi_str(errmsg[0]), 2)
end
_M.set_resp_header = set_resp_header
local function get_resp_header(tb, key)
local r = get_request()
if not r then
error("no request found")
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
local key_buf = get_string_buf(key_len + ffi_str_size * MAX_HEADER_VALUES)
local values = ffi_cast(ffi_str_type, key_buf + key_len)
local n = C.ngx_http_lua_ffi_get_resp_header(r, key, key_len, key_buf,
values, MAX_HEADER_VALUES,
errmsg)
-- print("retval: ", n)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return nil
end
if n == 1 then
local v = values[0]
return ffi_str(v.data, v.len)
end
if n > 0 then
local ret = new_tab(n, 0)
for i = 1, n do
local v = values[i - 1]
ret[i] = ffi_str(v.data, v.len)
end
return ret
end
-- n == FFI_ERROR
error(ffi_str(errmsg[0]), 2)
end
do
local mt = new_tab(0, 2)
mt.__newindex = set_resp_header
mt.__index = get_resp_header
ngx.header = setmetatable(new_tab(0, 0), mt)
end
return _M

932
lib/resty/core/shdict.lua Normal file
View File

@ -0,0 +1,932 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local _M = {
version = base.version
}
local ngx_shared = ngx.shared
if not ngx_shared then
return _M
end
local ffi_new = ffi.new
local ffi_str = ffi.string
local C = ffi.C
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local get_size_ptr = base.get_size_ptr
local tonumber = tonumber
local tostring = tostring
local next = next
local type = type
local error = error
local getmetatable = getmetatable
local FFI_DECLINED = base.FFI_DECLINED
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_shdict_get
local ngx_lua_ffi_shdict_incr
local ngx_lua_ffi_shdict_store
local ngx_lua_ffi_shdict_flush_all
local ngx_lua_ffi_shdict_get_ttl
local ngx_lua_ffi_shdict_set_expire
local ngx_lua_ffi_shdict_capacity
local ngx_lua_ffi_shdict_free_space
local ngx_lua_ffi_shdict_udata_to_zone
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_shdict_get(void *zone, const unsigned char *key,
size_t key_len, int *value_type, unsigned char **str_value_buf,
size_t *str_value_len, double *num_value, int *user_flags,
int get_stale, int *is_stale, char **errmsg);
int ngx_http_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
size_t key_len, double *value, char **err, int has_init,
double init, long init_ttl, int *forcible);
int ngx_http_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
int ngx_http_lua_ffi_shdict_flush_all(void *zone);
long ngx_http_lua_ffi_shdict_get_ttl(void *zone,
const unsigned char *key, size_t key_len);
int ngx_http_lua_ffi_shdict_set_expire(void *zone,
const unsigned char *key, size_t key_len, long exptime);
size_t ngx_http_lua_ffi_shdict_capacity(void *zone);
void *ngx_http_lua_ffi_shdict_udata_to_zone(void *zone_udata);
]]
ngx_lua_ffi_shdict_get = function(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags,
get_stale, is_stale, errmsg)
return C.ngx_http_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags,
get_stale, is_stale, errmsg)
end
ngx_lua_ffi_shdict_incr = function(zone, key,
key_len, value, err, has_init,
init, init_ttl, forcible)
return C.ngx_http_lua_ffi_shdict_incr(zone, key,
key_len, value, err, has_init,
init, init_ttl, forcible)
end
ngx_lua_ffi_shdict_store = function(zone, op,
key, key_len, value_type,
str_value_buf, str_value_len,
num_value, exptime, user_flags,
errmsg, forcible)
return C.ngx_http_lua_ffi_shdict_store(zone, op,
key, key_len, value_type,
str_value_buf, str_value_len,
num_value, exptime, user_flags,
errmsg, forcible)
end
ngx_lua_ffi_shdict_flush_all = C.ngx_http_lua_ffi_shdict_flush_all
ngx_lua_ffi_shdict_get_ttl = C.ngx_http_lua_ffi_shdict_get_ttl
ngx_lua_ffi_shdict_set_expire = C.ngx_http_lua_ffi_shdict_set_expire
ngx_lua_ffi_shdict_capacity = C.ngx_http_lua_ffi_shdict_capacity
ngx_lua_ffi_shdict_udata_to_zone =
C.ngx_http_lua_ffi_shdict_udata_to_zone
if not pcall(function ()
return C.ngx_http_lua_ffi_shdict_free_space
end)
then
ffi.cdef[[
size_t ngx_http_lua_ffi_shdict_free_space(void *zone);
]]
end
pcall(function ()
ngx_lua_ffi_shdict_free_space = C.ngx_http_lua_ffi_shdict_free_space
end)
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_shdict_get(void *zone, const unsigned char *key,
size_t key_len, int *value_type, unsigned char **str_value_buf,
size_t *str_value_len, double *num_value, int *user_flags,
int get_stale, int *is_stale, char **errmsg);
int ngx_stream_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
size_t key_len, double *value, char **err, int has_init,
double init, long init_ttl, int *forcible);
int ngx_stream_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
int ngx_stream_lua_ffi_shdict_flush_all(void *zone);
long ngx_stream_lua_ffi_shdict_get_ttl(void *zone,
const unsigned char *key, size_t key_len);
int ngx_stream_lua_ffi_shdict_set_expire(void *zone,
const unsigned char *key, size_t key_len, long exptime);
size_t ngx_stream_lua_ffi_shdict_capacity(void *zone);
void *ngx_stream_lua_ffi_shdict_udata_to_zone(void *zone_udata);
]]
ngx_lua_ffi_shdict_get = function(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags,
get_stale, is_stale, errmsg)
return C.ngx_stream_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags,
get_stale, is_stale, errmsg)
end
ngx_lua_ffi_shdict_incr = function(zone, key,
key_len, value, err, has_init,
init, init_ttl, forcible)
return C.ngx_stream_lua_ffi_shdict_incr(zone, key,
key_len, value, err, has_init,
init, init_ttl, forcible)
end
ngx_lua_ffi_shdict_store = function(zone, op,
key, key_len, value_type,
str_value_buf, str_value_len,
num_value, exptime, user_flags,
errmsg, forcible)
return C.ngx_stream_lua_ffi_shdict_store(zone, op,
key, key_len, value_type,
str_value_buf, str_value_len,
num_value, exptime, user_flags,
errmsg, forcible)
end
ngx_lua_ffi_shdict_flush_all = C.ngx_stream_lua_ffi_shdict_flush_all
ngx_lua_ffi_shdict_get_ttl = C.ngx_stream_lua_ffi_shdict_get_ttl
ngx_lua_ffi_shdict_set_expire = C.ngx_stream_lua_ffi_shdict_set_expire
ngx_lua_ffi_shdict_capacity = C.ngx_stream_lua_ffi_shdict_capacity
ngx_lua_ffi_shdict_udata_to_zone =
C.ngx_stream_lua_ffi_shdict_udata_to_zone
if not pcall(function ()
return C.ngx_stream_lua_ffi_shdict_free_space
end)
then
ffi.cdef[[
size_t ngx_stream_lua_ffi_shdict_free_space(void *zone);
]]
end
-- ngx_stream_lua is only compatible with NGINX >= 1.13.6, meaning it
-- cannot lack support for ngx_stream_lua_ffi_shdict_free_space.
ngx_lua_ffi_shdict_free_space = C.ngx_stream_lua_ffi_shdict_free_space
else
error("unknown subsystem: " .. subsystem)
end
local MACOS = jit and jit.os == "OSX"
if MACOS and subsystem == 'http' then
ffi.cdef[[
typedef struct {
void *zone;
const unsigned char *key;
size_t key_len;
int *value_type;
unsigned char **str_value_buf;
size_t *str_value_len;
double *num_value;
int *user_flags;
int get_stale;
int *is_stale;
char **errmsg;
} ngx_http_lua_shdict_get_params_t;
typedef struct {
void *zone;
int op;
const unsigned char *key;
size_t key_len;
int value_type;
const unsigned char *str_value_buf;
size_t str_value_len;
double num_value;
long exptime;
int user_flags;
char **errmsg;
int *forcible;
} ngx_http_lua_shdict_store_params_t;
typedef struct {
void *zone;
const unsigned char *key;
size_t key_len;
double *num_value;
char **errmsg;
int has_init;
double init;
long init_ttl;
int *forcible;
} ngx_http_lua_shdict_incr_params_t;
int ngx_http_lua_ffi_shdict_get_macos(
ngx_http_lua_shdict_get_params_t *p);
int ngx_http_lua_ffi_shdict_store_macos(
ngx_http_lua_shdict_store_params_t *p);
int ngx_http_lua_ffi_shdict_incr_macos(
ngx_http_lua_shdict_incr_params_t *p);
]]
local get_params = ffi_new("ngx_http_lua_shdict_get_params_t")
local incr_params = ffi_new("ngx_http_lua_shdict_incr_params_t")
local store_params = ffi_new("ngx_http_lua_shdict_store_params_t")
ngx_lua_ffi_shdict_get = function(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags,
get_stale, is_stale, errmsg)
get_params.zone = zone
get_params.key = key
get_params.key_len = key_len
get_params.value_type = value_type
get_params.str_value_buf = str_value_buf
get_params.str_value_len = value_len
get_params.num_value = num_value
get_params.user_flags = user_flags
get_params.get_stale = get_stale
get_params.is_stale = is_stale
get_params.errmsg = errmsg
return C.ngx_http_lua_ffi_shdict_get_macos(get_params)
end
ngx_lua_ffi_shdict_incr = function(zone, key,
key_len, value, err, has_init,
init, init_ttl, forcible)
incr_params.zone = zone
incr_params.key = key
incr_params.key_len = key_len
incr_params.num_value = value
incr_params.errmsg = err
incr_params.has_init = has_init
incr_params.init = init
incr_params.init_ttl = init_ttl
incr_params.forcible = forcible
return C.ngx_http_lua_ffi_shdict_incr_macos(incr_params)
end
ngx_lua_ffi_shdict_store = function(zone, op,
key, key_len, value_type,
str_value_buf, str_value_len,
num_value, exptime, user_flags,
errmsg, forcible)
store_params.zone = zone
store_params.op = op
store_params.key = key
store_params.key_len = key_len
store_params.value_type = value_type
store_params.str_value_buf = str_value_buf
store_params.str_value_len = str_value_len
store_params.num_value = num_value
store_params.exptime = exptime
store_params.user_flags = user_flags
store_params.errmsg = errmsg
store_params.forcible = forcible
return C.ngx_http_lua_ffi_shdict_store_macos(store_params)
end
end
if MACOS and subsystem == 'stream' then
ffi.cdef[[
typedef struct {
void *zone;
const unsigned char *key;
size_t key_len;
int *value_type;
unsigned char **str_value_buf;
size_t *str_value_len;
double *num_value;
int *user_flags;
int get_stale;
int *is_stale;
char **errmsg;
} ngx_stream_lua_shdict_get_params_t;
typedef struct {
void *zone;
int op;
const unsigned char *key;
size_t key_len;
int value_type;
const unsigned char *str_value_buf;
size_t str_value_len;
double num_value;
long exptime;
int user_flags;
char **errmsg;
int *forcible;
} ngx_stream_lua_shdict_store_params_t;
typedef struct {
void *zone;
const unsigned char *key;
size_t key_len;
double *num_value;
char **errmsg;
int has_init;
double init;
long init_ttl;
int *forcible;
} ngx_stream_lua_shdict_incr_params_t;
int ngx_stream_lua_ffi_shdict_get_macos(
ngx_stream_lua_shdict_get_params_t *p);
int ngx_stream_lua_ffi_shdict_store_macos(
ngx_stream_lua_shdict_store_params_t *p);
int ngx_stream_lua_ffi_shdict_incr_macos(
ngx_stream_lua_shdict_incr_params_t *p);
]]
local get_params = ffi_new("ngx_stream_lua_shdict_get_params_t")
local store_params = ffi_new("ngx_stream_lua_shdict_store_params_t")
local incr_params = ffi_new("ngx_stream_lua_shdict_incr_params_t")
ngx_lua_ffi_shdict_get = function(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags,
get_stale, is_stale, errmsg)
get_params.zone = zone
get_params.key = key
get_params.key_len = key_len
get_params.value_type = value_type
get_params.str_value_buf = str_value_buf
get_params.str_value_len = value_len
get_params.num_value = num_value
get_params.user_flags = user_flags
get_params.get_stale = get_stale
get_params.is_stale = is_stale
get_params.errmsg = errmsg
return C.ngx_stream_lua_ffi_shdict_get_macos(get_params)
end
ngx_lua_ffi_shdict_incr = function(zone, key,
key_len, value, err, has_init,
init, init_ttl, forcible)
incr_params.zone = zone
incr_params.key = key
incr_params.key_len = key_len
incr_params.num_value = value
incr_params.errmsg = err
incr_params.has_init = has_init
incr_params.init = init
incr_params.init_ttl = init_ttl
incr_params.forcible = forcible
return C.ngx_stream_lua_ffi_shdict_incr_macos(incr_params)
end
ngx_lua_ffi_shdict_store = function(zone, op,
key, key_len, value_type,
str_value_buf, str_value_len,
num_value, exptime, user_flags,
errmsg, forcible)
store_params.zone = zone
store_params.op = op
store_params.key = key
store_params.key_len = key_len
store_params.value_type = value_type
store_params.str_value_buf = str_value_buf
store_params.str_value_len = str_value_len
store_params.num_value = num_value
store_params.exptime = exptime
store_params.user_flags = user_flags
store_params.errmsg = errmsg
store_params.forcible = forcible
return C.ngx_stream_lua_ffi_shdict_store_macos(store_params)
end
end
if not pcall(function () return C.free end) then
ffi.cdef[[
void free(void *ptr);
]]
end
local value_type = ffi_new("int[1]")
local user_flags = ffi_new("int[1]")
local num_value = ffi_new("double[1]")
local is_stale = ffi_new("int[1]")
local forcible = ffi_new("int[1]")
local str_value_buf = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()
local function check_zone(zone)
if not zone or type(zone) ~= "table" then
error("bad \"zone\" argument", 3)
end
zone = zone[1]
if type(zone) ~= "userdata" then
error("bad \"zone\" argument", 3)
end
zone = ngx_lua_ffi_shdict_udata_to_zone(zone)
if zone == nil then
error("bad \"zone\" argument", 3)
end
return zone
end
local function shdict_store(zone, op, key, value, exptime, flags)
zone = check_zone(zone)
if not exptime then
exptime = 0
elseif exptime < 0 then
error('bad "exptime" argument', 2)
end
if not flags then
flags = 0
end
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local str_val_buf
local str_val_len = 0
local num_val = 0
local valtyp = type(value)
-- print("value type: ", valtyp)
-- print("exptime: ", exptime)
if valtyp == "string" then
valtyp = 4 -- LUA_TSTRING
str_val_buf = value
str_val_len = #value
elseif valtyp == "number" then
valtyp = 3 -- LUA_TNUMBER
num_val = value
elseif value == nil then
valtyp = 0 -- LUA_TNIL
elseif valtyp == "boolean" then
valtyp = 1 -- LUA_TBOOLEAN
num_val = value and 1 or 0
else
return nil, "bad value type"
end
local rc = ngx_lua_ffi_shdict_store(zone, op, key, key_len,
valtyp, str_val_buf,
str_val_len, num_val,
exptime * 1000, flags, errmsg,
forcible)
-- print("rc == ", rc)
if rc == 0 then -- NGX_OK
return true, nil, forcible[0] == 1
end
-- NGX_DECLINED or NGX_ERROR
return false, ffi_str(errmsg[0]), forcible[0] == 1
end
local function shdict_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0, key, value, exptime, flags)
end
local function shdict_safe_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0004, key, value, exptime, flags)
end
local function shdict_add(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0001, key, value, exptime, flags)
end
local function shdict_safe_add(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0005, key, value, exptime, flags)
end
local function shdict_replace(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0002, key, value, exptime, flags)
end
local function shdict_delete(zone, key)
return shdict_set(zone, key, nil)
end
local function shdict_get(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local size = get_string_buf_size()
local buf = get_string_buf(size)
str_value_buf[0] = buf
local value_len = get_size_ptr()
value_len[0] = size
local rc = ngx_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags, 0,
is_stale, errmsg)
if rc ~= 0 then
if errmsg[0] ~= nil then
return nil, ffi_str(errmsg[0])
end
error("failed to get the key")
end
local typ = value_type[0]
if typ == 0 then -- LUA_TNIL
return nil
end
local flags = tonumber(user_flags[0])
local val
if typ == 4 then -- LUA_TSTRING
if str_value_buf[0] ~= buf then
-- ngx.say("len: ", tonumber(value_len[0]))
buf = str_value_buf[0]
val = ffi_str(buf, value_len[0])
C.free(buf)
else
val = ffi_str(buf, value_len[0])
end
elseif typ == 3 then -- LUA_TNUMBER
val = tonumber(num_value[0])
elseif typ == 1 then -- LUA_TBOOLEAN
val = (tonumber(buf[0]) ~= 0)
else
error("unknown value type: " .. typ)
end
if flags ~= 0 then
return val, flags
end
return val
end
local function shdict_get_stale(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local size = get_string_buf_size()
local buf = get_string_buf(size)
str_value_buf[0] = buf
local value_len = get_size_ptr()
value_len[0] = size
local rc = ngx_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags, 1,
is_stale, errmsg)
if rc ~= 0 then
if errmsg[0] ~= nil then
return nil, ffi_str(errmsg[0])
end
error("failed to get the key")
end
local typ = value_type[0]
if typ == 0 then -- LUA_TNIL
return nil
end
local flags = tonumber(user_flags[0])
local val
if typ == 4 then -- LUA_TSTRING
if str_value_buf[0] ~= buf then
-- ngx.say("len: ", tonumber(value_len[0]))
buf = str_value_buf[0]
val = ffi_str(buf, value_len[0])
C.free(buf)
else
val = ffi_str(buf, value_len[0])
end
elseif typ == 3 then -- LUA_TNUMBER
val = tonumber(num_value[0])
elseif typ == 1 then -- LUA_TBOOLEAN
val = (tonumber(buf[0]) ~= 0)
else
error("unknown value type: " .. typ)
end
if flags ~= 0 then
return val, flags, is_stale[0] == 1
end
return val, nil, is_stale[0] == 1
end
local function shdict_incr(zone, key, value, init, init_ttl)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
if type(value) ~= "number" then
value = tonumber(value)
end
num_value[0] = value
if init then
local typ = type(init)
if typ ~= "number" then
init = tonumber(init)
if not init then
error("bad init arg: number expected, got " .. typ, 2)
end
end
end
if init_ttl ~= nil then
local typ = type(init_ttl)
if typ ~= "number" then
init_ttl = tonumber(init_ttl)
if not init_ttl then
error("bad init_ttl arg: number expected, got " .. typ, 2)
end
end
if init_ttl < 0 then
error('bad "init_ttl" argument', 2)
end
if not init then
error('must provide "init" when providing "init_ttl"', 2)
end
else
init_ttl = 0
end
local rc = ngx_lua_ffi_shdict_incr(zone, key, key_len, num_value,
errmsg, init and 1 or 0,
init or 0, init_ttl * 1000,
forcible)
if rc ~= 0 then -- ~= NGX_OK
return nil, ffi_str(errmsg[0])
end
if not init then
return tonumber(num_value[0])
end
return tonumber(num_value[0]), nil, forcible[0] == 1
end
local function shdict_flush_all(zone)
zone = check_zone(zone)
ngx_lua_ffi_shdict_flush_all(zone)
end
local function shdict_ttl(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local rc = ngx_lua_ffi_shdict_get_ttl(zone, key, key_len)
if rc == FFI_DECLINED then
return nil, "not found"
end
return tonumber(rc) / 1000
end
local function shdict_expire(zone, key, exptime)
zone = check_zone(zone)
if not exptime then
error('bad "exptime" argument', 2)
end
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local rc = ngx_lua_ffi_shdict_set_expire(zone, key, key_len,
exptime * 1000)
if rc == FFI_DECLINED then
return nil, "not found"
end
-- NGINX_OK/FFI_OK
return true
end
local function shdict_capacity(zone)
zone = check_zone(zone)
return tonumber(ngx_lua_ffi_shdict_capacity(zone))
end
local shdict_free_space
if ngx_lua_ffi_shdict_free_space then
shdict_free_space = function (zone)
zone = check_zone(zone)
return tonumber(ngx_lua_ffi_shdict_free_space(zone))
end
else
shdict_free_space = function ()
error("'shm:free_space()' not supported in NGINX < 1.11.7", 2)
end
end
local _, dict = next(ngx_shared, nil)
if dict then
local mt = getmetatable(dict)
if mt then
mt = mt.__index
if mt then
mt.get = shdict_get
mt.get_stale = shdict_get_stale
mt.incr = shdict_incr
mt.set = shdict_set
mt.safe_set = shdict_safe_set
mt.add = shdict_add
mt.safe_add = shdict_safe_add
mt.replace = shdict_replace
mt.delete = shdict_delete
mt.flush_all = shdict_flush_all
mt.ttl = shdict_ttl
mt.expire = shdict_expire
mt.capacity = shdict_capacity
mt.free_space = shdict_free_space
end
end
end
return _M

272
lib/resty/core/socket.lua Normal file
View File

@ -0,0 +1,272 @@
local base = require "resty.core.base"
base.allows_subsystem("http")
local debug = require "debug"
local ffi = require "ffi"
local error = error
local assert = assert
local tonumber = tonumber
local tostring = tostring
local type = type
local select = select
local registry = debug.getregistry()
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_gc = ffi.gc
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local get_request = base.get_request
local co_yield = coroutine._yield
local option_index = {
["keepalive"] = 1,
["reuseaddr"] = 2,
["tcp-nodelay"] = 3,
["sndbuf"] = 4,
["rcvbuf"] = 5,
}
ffi.cdef[[
typedef struct ngx_http_lua_socket_tcp_upstream_s
ngx_http_lua_socket_tcp_upstream_t;
int
ngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u,
int opt, int *val, unsigned char *err, size_t *errlen);
int
ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u,
int opt, int val, unsigned char *err, size_t *errlen);
int
ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, void *sess,
int enable_session_reuse, ngx_str_t *server_name, int verify,
int ocsp_status_req, void *chain, void *pkey, char **errmsg);
int
ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, void **sess, char **errmsg,
int *openssl_error_code);
void
ngx_http_lua_ffi_ssl_free_session(void *sess);
]]
local output_value_buf = ffi_new("int[1]")
local ERR_BUF_SIZE = 4096
local FFI_OK = base.FFI_OK
local FFI_ERROR = base.FFI_ERROR
local FFI_DONE = base.FFI_DONE
local FFI_AGAIN = base.FFI_AGAIN
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local SOCKET_CTX_INDEX = 1
local SOCKET_CLIENT_CERT_INDEX = 6
local SOCKET_CLIENT_PKEY_INDEX = 7
local function get_tcp_socket(cosocket)
local tcp_socket = cosocket[SOCKET_CTX_INDEX]
if not tcp_socket then
error("socket is never created nor connected")
end
return tcp_socket
end
local function getoption(cosocket, option)
local tcp_socket = get_tcp_socket(cosocket)
if option == nil then
return nil, 'missing the "option" argument'
end
if option_index[option] == nil then
return nil, "unsupported option " .. tostring(option)
end
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_socket_tcp_getoption(tcp_socket,
option_index[option],
output_value_buf,
err,
errlen)
if rc ~= FFI_OK then
return nil, ffi_str(err, errlen[0])
end
return tonumber(output_value_buf[0])
end
local function setoption(cosocket, option, value)
local tcp_socket = get_tcp_socket(cosocket)
if option == nil then
return nil, 'missing the "option" argument'
end
if value == nil then
return nil, 'missing the "value" argument'
end
if option_index[option] == nil then
return nil, "unsupported option " .. tostring(option)
end
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local rc = C.ngx_http_lua_ffi_socket_tcp_setoption(tcp_socket,
option_index[option],
value,
err,
errlen)
if rc ~= FFI_OK then
return nil, ffi_str(err, errlen[0])
end
return true
end
local errmsg = base.get_errmsg_ptr()
local session_ptr = ffi_new("void *[1]")
local server_name_str = ffi_new("ngx_str_t[1]")
local openssl_error_code = ffi_new("int[1]")
local function setclientcert(cosocket, cert, pkey)
if not cert and not pkey then
cosocket[SOCKET_CLIENT_CERT_INDEX] = nil
cosocket[SOCKET_CLIENT_PKEY_INDEX] = nil
return true
end
if not cert or not pkey then
return nil,
"client certificate must be supplied with corresponding " ..
"private key"
end
if type(cert) ~= "cdata" then
return nil, "bad cert arg: cdata expected, got " .. type(cert)
end
if type(pkey) ~= "cdata" then
return nil, "bad pkey arg: cdata expected, got " .. type(pkey)
end
cosocket[SOCKET_CLIENT_CERT_INDEX] = cert
cosocket[SOCKET_CLIENT_PKEY_INDEX] = pkey
return true
end
local function sslhandshake(cosocket, reused_session, server_name, ssl_verify,
send_status_req, ...)
local n = select("#", ...)
if not cosocket or n > 0 then
error("ngx.socket sslhandshake: expecting 1 ~ 5 arguments " ..
"(including the object), but seen " .. (cosocket and 5 + n or 0))
end
local r = get_request()
if not r then
error("no request found", 2)
end
session_ptr[0] = type(reused_session) == "cdata" and reused_session or nil
if server_name then
server_name_str[0].data = server_name
server_name_str[0].len = #server_name
else
server_name_str[0].data = nil
server_name_str[0].len = 0
end
local u = get_tcp_socket(cosocket)
local rc = C.ngx_http_lua_ffi_socket_tcp_sslhandshake(r, u,
session_ptr[0],
reused_session ~= false,
server_name_str,
ssl_verify and 1 or 0,
send_status_req and 1 or 0,
cosocket[SOCKET_CLIENT_CERT_INDEX],
cosocket[SOCKET_CLIENT_PKEY_INDEX],
errmsg)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found", 2)
end
while true do
if rc == FFI_ERROR then
if openssl_error_code[0] ~= 0 then
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
end
return nil, ffi_str(errmsg[0])
end
if rc == FFI_DONE then
return reused_session
end
if rc == FFI_OK then
if reused_session == false then
return true
end
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)
assert(rc == FFI_OK)
if session_ptr[0] == nil then
return session_ptr[0]
end
return ffi_gc(session_ptr[0], C.ngx_http_lua_ffi_ssl_free_session)
end
assert(rc == FFI_AGAIN)
co_yield()
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)
end
end
do
local method_table = registry.__tcp_cosocket_mt
method_table.getoption = getoption
method_table.setoption = setoption
method_table.setclientcert = setclientcert
method_table.sslhandshake = sslhandshake
end
return { version = base.version }

183
lib/resty/core/time.lua Normal file
View File

@ -0,0 +1,183 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local error = error
local tonumber = tonumber
local type = type
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local time_val = ffi_new("long[1]")
local get_string_buf = base.get_string_buf
local ngx = ngx
local FFI_ERROR = base.FFI_ERROR
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_now
local ngx_lua_ffi_time
local ngx_lua_ffi_monotonic_msec
local ngx_lua_ffi_today
local ngx_lua_ffi_localtime
local ngx_lua_ffi_utctime
local ngx_lua_ffi_update_time
if subsystem == 'http' then
ffi.cdef[[
double ngx_http_lua_ffi_now(void);
long ngx_http_lua_ffi_time(void);
long ngx_http_lua_ffi_monotonic_msec(void);
void ngx_http_lua_ffi_today(unsigned char *buf);
void ngx_http_lua_ffi_localtime(unsigned char *buf);
void ngx_http_lua_ffi_utctime(unsigned char *buf);
void ngx_http_lua_ffi_update_time(void);
int ngx_http_lua_ffi_cookie_time(unsigned char *buf, long t);
void ngx_http_lua_ffi_http_time(unsigned char *buf, long t);
void ngx_http_lua_ffi_parse_http_time(const unsigned char *str, size_t len,
long *time);
]]
ngx_lua_ffi_now = C.ngx_http_lua_ffi_now
ngx_lua_ffi_time = C.ngx_http_lua_ffi_time
ngx_lua_ffi_monotonic_msec = C.ngx_http_lua_ffi_monotonic_msec
ngx_lua_ffi_today = C.ngx_http_lua_ffi_today
ngx_lua_ffi_localtime = C.ngx_http_lua_ffi_localtime
ngx_lua_ffi_utctime = C.ngx_http_lua_ffi_utctime
ngx_lua_ffi_update_time = C.ngx_http_lua_ffi_update_time
elseif subsystem == 'stream' then
ffi.cdef[[
double ngx_stream_lua_ffi_now(void);
long ngx_stream_lua_ffi_time(void);
long ngx_stream_lua_ffi_monotonic_msec(void);
void ngx_stream_lua_ffi_today(unsigned char *buf);
void ngx_stream_lua_ffi_localtime(unsigned char *buf);
void ngx_stream_lua_ffi_utctime(unsigned char *buf);
void ngx_stream_lua_ffi_update_time(void);
]]
ngx_lua_ffi_now = C.ngx_stream_lua_ffi_now
ngx_lua_ffi_time = C.ngx_stream_lua_ffi_time
ngx_lua_ffi_monotonic_msec = C.ngx_stream_lua_ffi_monotonic_msec
ngx_lua_ffi_today = C.ngx_stream_lua_ffi_today
ngx_lua_ffi_localtime = C.ngx_stream_lua_ffi_localtime
ngx_lua_ffi_utctime = C.ngx_stream_lua_ffi_utctime
ngx_lua_ffi_update_time = C.ngx_stream_lua_ffi_update_time
end
function ngx.now()
local now = tonumber(ngx_lua_ffi_now())
return now
end
function ngx.time()
local time = tonumber(ngx_lua_ffi_time())
return time
end
local function monotonic_msec()
local msec = tonumber(ngx_lua_ffi_monotonic_msec())
return msec
end
local function monotonic_time()
local msec = tonumber(ngx_lua_ffi_monotonic_msec())
local time = msec / 1000
return time
end
function ngx.update_time()
ngx_lua_ffi_update_time()
end
function ngx.today()
-- the format of today is 2010-11-19
local today_buf_size = 10
local buf = get_string_buf(today_buf_size)
ngx_lua_ffi_today(buf)
return ffi_str(buf, today_buf_size)
end
function ngx.localtime()
-- the format of localtime is 2010-11-19 20:56:31
local localtime_buf_size = 19
local buf = get_string_buf(localtime_buf_size)
ngx_lua_ffi_localtime(buf)
return ffi_str(buf, localtime_buf_size)
end
function ngx.utctime()
-- the format of utctime is 2010-11-19 20:56:31
local utctime_buf_size = 19
local buf = get_string_buf(utctime_buf_size)
ngx_lua_ffi_utctime(buf)
return ffi_str(buf, utctime_buf_size)
end
if subsystem == 'http' then
function ngx.cookie_time(sec)
if type(sec) ~= "number" then
error("number argument only", 2)
end
-- the format of cookie time is Mon, 28-Sep-2038 06:00:00 GMT
-- or Mon, 28-Sep-18 06:00:00 GMT
local cookie_time_buf_size = 29
local buf = get_string_buf(cookie_time_buf_size)
local used_size = C.ngx_http_lua_ffi_cookie_time(buf, sec)
return ffi_str(buf, used_size)
end
function ngx.http_time(sec)
if type(sec) ~= "number" then
error("number argument only", 2)
end
-- the format of http time is Mon, 28 Sep 1970 06:00:00 GMT
local http_time_buf_size = 29
local buf = get_string_buf(http_time_buf_size)
C.ngx_http_lua_ffi_http_time(buf, sec)
return ffi_str(buf, http_time_buf_size)
end
function ngx.parse_http_time(time_str)
if type(time_str) ~= "string" then
error("string argument only", 2)
end
C.ngx_http_lua_ffi_parse_http_time(time_str, #time_str, time_val)
local res = time_val[0]
if res == FFI_ERROR then
return nil
end
local time = tonumber(res)
return time
end
end
return {
version = base.version,
monotonic_msec = monotonic_msec,
monotonic_time = monotonic_time
}

145
lib/resty/core/time.md Normal file
View File

@ -0,0 +1,145 @@
Name
====
`resty.core.time` - utility functions for time operations.
Table of Contents
=================
* [Name](#name)
* [Status](#status)
* [Synopsis](#synopsis)
* [Description](#description)
* [Methods](#methods)
* [monotonic_msec](#monotonic_msec)
* [monotonic_time](#monotonic_time)
* [Community](#community)
* [English Mailing List](#english-mailing-list)
* [Chinese Mailing List](#chinese-mailing-list)
* [Bugs and Patches](#bugs-and-patches)
* [Copyright and License](#copyright-and-license)
* [See Also](#see-also)
Status
======
This Lua module is currently considered production ready.
Synopsis
========
```nginx
location = /t {
content_by_lua_block {
local time = require "resty.core.time"
ngx.say(time.monotonic_time())
ngx.say(time.monotonic_msec())
}
}
```
The value get by `"resty.core.time".monotonic_time` should equal to the value from /proc/uptime.
[Back to TOC](#table-of-contents)
Description
===========
This module provides utility functions for the time operations.
[Back to TOC](#table-of-contents)
Methods
=======
monotonic_msec
--------------
**syntax:** *monotonic_msec()*
Returns the elapsed time in microseconds from the machine boot for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library).
```lua
local cur_msec = require "resty.core.time".monotonic_msec
ngx.say(cur_msec())
```
This api was first introduced in lua-resty-core v0.1.25.
[Back to TOC](#table-of-contents)
monotonic_time
--------------
**syntax:** *monotonic_time()*
Returns a floating-point number for the elapsed time in seconds (including milliseconds as the decimal part) from the machine boot for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library).
```lua
local cur_time = require "resty.core.time".monotonic_time
ngx.say(cur_time())
```
This api was first introduced in lua-resty-core v0.1.25.
[Back to TOC](#table-of-contents)
Community
=========
[Back to TOC](#table-of-contents)
English Mailing List
--------------------
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list
is for English speakers.
[Back to TOC](#table-of-contents)
Chinese Mailing List
--------------------
The [openresty](https://groups.google.com/group/openresty) mailing list is for
Chinese speakers.
[Back to TOC](#table-of-contents)
Bugs and Patches
================
Please report bugs or submit patches by
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-core/issues),
1. or posting to the [OpenResty community](#community).
[Back to TOC](#table-of-contents)
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2018, by OpenResty Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Back to TOC](#table-of-contents)
See Also
========
* the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
* OpenResty: https://openresty.org
[Back to TOC](#table-of-contents)

115
lib/resty/core/uri.lua Normal file
View File

@ -0,0 +1,115 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local tostring = tostring
local get_string_buf = base.get_string_buf
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_escape_uri
local ngx_lua_ffi_unescape_uri
local ngx_lua_ffi_uri_escaped_length
local NGX_ESCAPE_URI = 0
local NGX_ESCAPE_URI_COMPONENT = 2
local NGX_ESCAPE_MAIL_AUTH = 6
if subsystem == "http" then
ffi.cdef[[
size_t ngx_http_lua_ffi_uri_escaped_length(const unsigned char *src,
size_t len, int type);
void ngx_http_lua_ffi_escape_uri(const unsigned char *src, size_t len,
unsigned char *dst, int type);
size_t ngx_http_lua_ffi_unescape_uri(const unsigned char *src,
size_t len, unsigned char *dst);
]]
ngx_lua_ffi_escape_uri = C.ngx_http_lua_ffi_escape_uri
ngx_lua_ffi_unescape_uri = C.ngx_http_lua_ffi_unescape_uri
ngx_lua_ffi_uri_escaped_length = C.ngx_http_lua_ffi_uri_escaped_length
elseif subsystem == "stream" then
ffi.cdef[[
size_t ngx_stream_lua_ffi_uri_escaped_length(const unsigned char *src,
size_t len, int type);
void ngx_stream_lua_ffi_escape_uri(const unsigned char *src, size_t len,
unsigned char *dst, int type);
size_t ngx_stream_lua_ffi_unescape_uri(const unsigned char *src,
size_t len, unsigned char *dst);
]]
ngx_lua_ffi_escape_uri = C.ngx_stream_lua_ffi_escape_uri
ngx_lua_ffi_unescape_uri = C.ngx_stream_lua_ffi_unescape_uri
ngx_lua_ffi_uri_escaped_length = C.ngx_stream_lua_ffi_uri_escaped_length
end
ngx.escape_uri = function (s, esc_type)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
if esc_type == nil then
esc_type = NGX_ESCAPE_URI_COMPONENT
else
if type(esc_type) ~= 'number' then
error("\"type\" is not a number", 3)
end
if esc_type < NGX_ESCAPE_URI or esc_type > NGX_ESCAPE_MAIL_AUTH then
error("\"type\" " .. esc_type .. " out of range", 3)
end
end
local slen = #s
local dlen = ngx_lua_ffi_uri_escaped_length(s, slen, esc_type)
-- print("dlen: ", tonumber(dlen))
if dlen == slen then
return s
end
local dst = get_string_buf(dlen)
ngx_lua_ffi_escape_uri(s, slen, dst, esc_type)
return ffi_string(dst, dlen)
end
ngx.unescape_uri = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local dlen = slen
local dst = get_string_buf(dlen)
dlen = ngx_lua_ffi_unescape_uri(s, slen, dst)
return ffi_string(dst, dlen)
end
return {
version = base.version,
}

46
lib/resty/core/utils.lua Normal file
View File

@ -0,0 +1,46 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_str = ffi.string
local ffi_copy = ffi.copy
local byte = string.byte
local str_find = string.find
local get_string_buf = base.get_string_buf
local subsystem = ngx.config.subsystem
local _M = {
version = base.version
}
if subsystem == "http" then
ffi.cdef[[
void ngx_http_lua_ffi_str_replace_char(unsigned char *buf, size_t len,
const unsigned char find, const unsigned char replace);
]]
function _M.str_replace_char(str, find, replace)
if not str_find(str, find, nil, true) then
return str
end
local len = #str
local buf = get_string_buf(len)
ffi_copy(buf, str, len)
C.ngx_http_lua_ffi_str_replace_char(buf, len, byte(find),
byte(replace))
return ffi_str(buf, len)
end
end
return _M

160
lib/resty/core/var.lua Normal file
View File

@ -0,0 +1,160 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local type = type
local error = error
local tostring = tostring
local setmetatable = setmetatable
local get_request = base.get_request
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_var_get
local ngx_lua_ffi_var_set
local ERR_BUF_SIZE = 256
ngx.var = new_tab(0, 0)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_var_get(ngx_http_request_t *r,
const char *name_data, size_t name_len, char *lowcase_buf,
int capture_id, char **value, size_t *value_len, char **err);
int ngx_http_lua_ffi_var_set(ngx_http_request_t *r,
const unsigned char *name_data, size_t name_len,
unsigned char *lowcase_buf, const unsigned char *value,
size_t value_len, unsigned char *errbuf, size_t *errlen);
]]
ngx_lua_ffi_var_get = C.ngx_http_lua_ffi_var_get
ngx_lua_ffi_var_set = C.ngx_http_lua_ffi_var_set
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r,
const char *name_data, size_t name_len, char *lowcase_buf,
int capture_id, char **value, size_t *value_len, char **err);
int ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r,
const unsigned char *name_data, size_t name_len,
unsigned char *lowcase_buf, const unsigned char *value,
size_t value_len, unsigned char *errbuf, size_t *errlen);
]]
ngx_lua_ffi_var_get = C.ngx_stream_lua_ffi_var_get
ngx_lua_ffi_var_set = C.ngx_stream_lua_ffi_var_set
end
local value_ptr = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()
local function var_get(self, name)
local r = get_request()
if not r then
error("no request found")
end
local value_len = get_size_ptr()
local rc
if type(name) == "number" then
rc = ngx_lua_ffi_var_get(r, nil, 0, nil, name, value_ptr, value_len,
errmsg)
else
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local lowcase_buf = get_string_buf(name_len)
rc = ngx_lua_ffi_var_get(r, name, name_len, lowcase_buf, 0, value_ptr,
value_len, errmsg)
end
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return ffi_str(value_ptr[0], value_len[0])
end
if rc == -5 then -- NGX_DECLINED
return nil
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errmsg[0]), 2)
end
end
local function var_set(self, name, value)
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local lowcase_buf = get_string_buf(name_len + ERR_BUF_SIZE)
local value_len
if value == nil then
value_len = 0
else
if type(value) ~= 'string' then
value = tostring(value)
end
value_len = #value
end
local errbuf = lowcase_buf + name_len
local rc = ngx_lua_ffi_var_set(r, name, name_len, lowcase_buf, value,
value_len, errbuf, errlen)
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errbuf, errlen[0]), 2)
end
end
do
local mt = new_tab(0, 2)
mt.__index = var_get
mt.__newindex = var_set
setmetatable(ngx.var, mt)
end
return {
version = base.version
}

137
lib/resty/core/worker.lua Normal file
View File

@ -0,0 +1,137 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local jit = require "jit"
local base = require "resty.core.base"
local ffi_cast = ffi.cast
local C = ffi.C
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local ngx_lua_ffi_worker_id
local ngx_lua_ffi_worker_pid
local ngx_lua_ffi_worker_pids
local ngx_lua_ffi_worker_count
local ngx_lua_ffi_worker_exiting
local ffi_intp_type = ffi.typeof("int *")
local ffi_int_size = ffi.sizeof("int")
local is_not_windows = jit.os ~= "Windows"
if is_not_windows then
ngx.worker = new_tab(0, 5)
else
ngx.worker = new_tab(0, 4)
end
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_worker_id(void);
int ngx_http_lua_ffi_worker_pid(void);
int ngx_http_lua_ffi_worker_count(void);
int ngx_http_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_http_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_http_lua_ffi_worker_pid
ngx_lua_ffi_worker_count = C.ngx_http_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_http_lua_ffi_worker_exiting
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_worker_id(void);
int ngx_stream_lua_ffi_worker_pid(void);
int ngx_stream_lua_ffi_worker_count(void);
int ngx_stream_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_stream_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_stream_lua_ffi_worker_pid
ngx_lua_ffi_worker_count = C.ngx_stream_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_stream_lua_ffi_worker_exiting
end
function ngx.worker.exiting()
return ngx_lua_ffi_worker_exiting() ~= 0
end
function ngx.worker.pid()
return ngx_lua_ffi_worker_pid()
end
if is_not_windows then
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len);
]]
ngx_lua_ffi_worker_pids = C.ngx_http_lua_ffi_worker_pids
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_worker_pids(int *pids, size_t *pids_len);
]]
ngx_lua_ffi_worker_pids = C.ngx_stream_lua_ffi_worker_pids
end
function ngx.worker.pids()
if ngx.get_phase() == "init" or ngx.get_phase() == "init_worker" then
return nil, "API disabled in the current context"
end
local pids = {}
local size_ptr = get_size_ptr()
-- the old and the new workers coexist during reloading
local worker_cnt = ngx.worker.count() * 4
if worker_cnt == 0 then
return pids
end
size_ptr[0] = worker_cnt
local pids_ptr = get_string_buf(worker_cnt * ffi_int_size)
local intp_buf = ffi_cast(ffi_intp_type, pids_ptr)
local res = ngx_lua_ffi_worker_pids(intp_buf, size_ptr)
if res == 0 then
for i = 1, tonumber(size_ptr[0]) do
pids[i] = intp_buf[i - 1]
end
end
return pids
end
end
function ngx.worker.id()
local id = ngx_lua_ffi_worker_id()
if id < 0 then
return nil
end
return id
end
function ngx.worker.count()
return ngx_lua_ffi_worker_count()
end
return {
_VERSION = base.version
}

51
t/TestCore.pm Normal file
View File

@ -0,0 +1,51 @@
package t::TestCore;
use Test::Nginx::Socket::Lua -Base;
use Cwd qw(cwd);
$ENV{TEST_NGINX_HOTLOOP} ||= 10;
$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;
our $pwd = cwd();
our $lua_package_path = './lib/?.lua;./t/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;';
our $init_by_lua_block = <<_EOC_;
local verbose = false
if verbose then
local dump = require "jit.dump"
dump.on("b", "$Test::Nginx::Util::ErrLogFile")
else
local v = require "jit.v"
v.on("$Test::Nginx::Util::ErrLogFile")
end
require "resty.core"
jit.opt.start("hotloop=$ENV{TEST_NGINX_HOTLOOP}")
-- jit.off()
_EOC_
our $HttpConfig = <<_EOC_;
lua_package_path '$lua_package_path';
init_by_lua_block {
$t::TestCore::init_by_lua_block
}
_EOC_
our @EXPORT = qw(
$pwd
$lua_package_path
$init_by_lua_block
$HttpConfig
);
add_block_preprocessor(sub {
my $block = shift;
if (!defined $block->http_config) {
$block->set_value("http_config", $HttpConfig);
}
});
1;

50
t/TestCore/Stream.pm Normal file
View File

@ -0,0 +1,50 @@
package t::TestCore::Stream;
use Test::Nginx::Socket::Lua::Stream -Base;
use Cwd qw(cwd);
$ENV{TEST_NGINX_HOTLOOP} ||= 10;
our $pwd = cwd();
our $lua_package_path = './lib/?.lua;../lua-resty-lrucache/lib/?.lua;;';
our $init_by_lua_block = <<_EOC_;
local verbose = false
if verbose then
local dump = require "jit.dump"
dump.on("b", "$Test::Nginx::Util::ErrLogFile")
else
local v = require "jit.v"
v.on("$Test::Nginx::Util::ErrLogFile")
end
require "resty.core"
jit.opt.start("hotloop=$ENV{TEST_NGINX_HOTLOOP}")
-- jit.off()
_EOC_
our $StreamConfig = <<_EOC_;
lua_package_path '$lua_package_path';
init_by_lua_block {
$t::TestCore::Stream::init_by_lua_block
}
_EOC_
our @EXPORT = qw(
$pwd
$lua_package_path
$init_by_lua_block
$StreamConfig
);
add_block_preprocessor(sub {
my $block = shift;
if (!defined $block->stream_config) {
$block->set_value("stream_config", $StreamConfig);
}
});
1;

392
t/balancer-timeout.t Normal file
View File

@ -0,0 +1,392 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
BEGIN {
if (!defined $ENV{LD_PRELOAD}) {
$ENV{LD_PRELOAD} = '';
}
if ($ENV{LD_PRELOAD} !~ /\bmockeagain\.so\b/) {
$ENV{LD_PRELOAD} = "mockeagain.so $ENV{LD_PRELOAD}";
}
if (defined $ENV{MOCKEAGAIN} && $ENV{MOCKEAGAIN} eq 'r') {
$ENV{MOCKEAGAIN} = 'rw';
} else {
$ENV{MOCKEAGAIN} = 'w';
}
$ENV{TEST_NGINX_EVENT_TYPE} = 'poll';
$ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1;
}
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_on();
#workers(2);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 4);
$ENV{TEST_NGINX_LUA_PACKAGE_PATH} = $t::TestCore::lua_package_path;
#worker_connections(1024);
#no_diff();
no_long_string();
run_tests();
__DATA__
=== TEST 1: set_timeouts
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
assert(b.set_timeouts(1.234, 5.678, 7.689))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body
fake origin
--- grep_error_log eval: qr/event timer add: \d+: (?:1234|5678|7689):/
--- grep_error_log_out eval
qr/\Aevent timer add: \d+: 1234:
event timer add: \d+: 5678:
event timer add: \d+: 7689:
\z/
--- no_error_log
[warn]
=== TEST 2: set_timeouts (nil connect timeout)
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_connect_timeout 1234ms;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
assert(b.set_timeouts(nil, 5.678, 7.689))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body
fake origin
--- grep_error_log eval: qr/event timer add: \d+: (?:1234|5678|7689):/
--- grep_error_log_out eval
qr/\Aevent timer add: \d+: 1234:
event timer add: \d+: 5678:
event timer add: \d+: 7689:
\z/
--- no_error_log
[warn]
=== TEST 3: set_timeouts (nil send timeout)
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_send_timeout 5678ms;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
assert(b.set_timeouts(1.234, nil, 7.689))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body
fake origin
--- grep_error_log eval: qr/event timer add: \d+: (?:1234|5678|7689):/
--- grep_error_log_out eval
qr/\Aevent timer add: \d+: 1234:
event timer add: \d+: 5678:
event timer add: \d+: 7689:
\z/
--- no_error_log
[warn]
=== TEST 4: set_timeouts (nil read timeout)
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_read_timeout 7689ms;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
assert(b.set_timeouts(1.234, 5.678, nil))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body
fake origin
--- grep_error_log eval: qr/event timer add: \d+: (?:1234|5678|7689):/
--- grep_error_log_out eval
qr/\Aevent timer add: \d+: 1234:
event timer add: \d+: 5678:
event timer add: \d+: 7689:
\z/
--- no_error_log
[warn]
=== TEST 5: set connect timeout to 0
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_timeouts(0, 1.234, 5.678))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? balancer_by_lua\(nginx.conf:\d+\):4: bad connect timeout/
--- no_error_log
[warn]
=== TEST 6: set connect timeout to -1
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_timeouts(-1, 1.234, 5.678))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? balancer_by_lua\(nginx.conf:\d+\):4: bad connect timeout/
--- no_error_log
[warn]
=== TEST 7: set send timeout to 0
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_timeouts(1.234, 0, 5.678))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? balancer_by_lua\(nginx.conf:\d+\):4: bad send timeout/
--- no_error_log
[warn]
=== TEST 8: set send timeout to -1
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_timeouts(1.234, -1, 5.678))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? balancer_by_lua\(nginx.conf:\d+\):4: bad send timeout/
--- no_error_log
[warn]
=== TEST 9: set read timeout to -1
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_timeouts(1.234, 5.678, -1))
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
echo "fake origin";
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? balancer_by_lua\(nginx.conf:\d+\):4: bad read timeout/
--- no_error_log
[warn]
=== TEST 10: set_timeouts called in a wrong context
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
--- config
location = /t {
content_by_lua_block {
local balancer = require "ngx.balancer"
local ok, err = balancer.set_timeouts(1, 1, 1)
if not ok then
ngx.say("failed to call: ", err)
return
end
ngx.say("unexpected success!")
}
}
--- request
GET /t
--- response_body
failed to call: no upstream found
--- no_error_log
[error]
[alert]
=== TEST 11: set_timeouts called with a non-numerical parameter
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local balancer = require "ngx.balancer"
local ok, err = balancer.set_timeouts("1.234", 1, 1)
if not ok then
ngx.log(ngx.ERR, "failed to call: ", err)
end
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? bad connect timeout/
--- no_error_log
[alert]

884
t/balancer.t Normal file
View File

@ -0,0 +1,884 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_on();
#workers(2);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 4 + 6);
$ENV{TEST_NGINX_LUA_PACKAGE_PATH} = "$t::TestCore::lua_package_path";
#worker_connections(1024);
#no_diff();
no_long_string();
run_tests();
__DATA__
=== TEST 1: set current peer (separate addr and port)
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_current_peer("127.0.0.3", 12345))
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- error_log eval
[
'[lua] balancer_by_lua(nginx.conf:29):2: hello from balancer by lua! while connecting to upstream,',
qr{connect\(\) failed .*?, upstream: "http://127\.0\.0\.3:12345/t"},
]
--- no_error_log
[warn]
=== TEST 2: set current peer & next upstream (3 tries)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 10;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
if ngx.ctx.tries < 2 then
local ok, err = b.set_more_tries(1)
if not ok then
return error("failed to set more tries: ", err)
elseif err then
ngx.log(ngx.WARN, "set more tries: ", err)
end
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.3", 12345))
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- grep_error_log eval: qr{connect\(\) failed .*, upstream: "http://.*?"}
--- grep_error_log_out eval
qr#^(?:connect\(\) failed .*?, upstream: "http://127.0.0.3:12345/t"\n){3}$#
--- no_error_log
[warn]
=== TEST 3: set current peer & next upstream (no retries)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.3", 12345))
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- grep_error_log eval: qr{connect\(\) failed .*, upstream: "http://.*?"}
--- grep_error_log_out eval
qr#^(?:connect\(\) failed .*?, upstream: "http://127.0.0.3:12345/t"\n){1}$#
--- no_error_log
[warn]
=== TEST 4: set current peer & next upstream (3 tries exceeding the limit)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 2;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
if ngx.ctx.tries < 2 then
local ok, err = b.set_more_tries(1)
if not ok then
return error("failed to set more tries: ", err)
elseif err then
ngx.log(ngx.WARN, "set more tries: ", err)
end
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.3", 12345))
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- grep_error_log eval: qr{connect\(\) failed .*, upstream: "http://.*?"}
--- grep_error_log_out eval
qr#^(?:connect\(\) failed .*?, upstream: "http://127.0.0.3:12345/t"\n){2}$#
--- error_log
set more tries: reduced tries due to limit
=== TEST 5: get last peer failure status (404)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 10;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
local state, status = b.get_last_failure()
print("last peer failure: ", state, " ", status)
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
if ngx.ctx.tries < 2 then
local ok, err = b.set_more_tries(1)
if not ok then
return error("failed to set more tries: ", err)
elseif err then
ngx.log(ngx.WARN, "set more tries: ", err)
end
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
return 404;
}
--- request
GET /t
--- response_body_like: 404 Not Found
--- error_code: 404
--- grep_error_log eval: qr{last peer failure: \S+ \S+}
--- grep_error_log_out
last peer failure: nil nil
last peer failure: next 404
last peer failure: next 404
--- no_error_log
[warn]
=== TEST 6: get last peer failure status (500)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 10;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
local state, status = b.get_last_failure()
print("last peer failure: ", state, " ", status)
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
if ngx.ctx.tries < 2 then
local ok, err = b.set_more_tries(1)
if not ok then
return error("failed to set more tries: ", err)
elseif err then
ngx.log(ngx.WARN, "set more tries: ", err)
end
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
return 500;
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- grep_error_log eval: qr{last peer failure: \S+ \S+}
--- grep_error_log_out
last peer failure: nil nil
last peer failure: failed 500
last peer failure: failed 500
--- no_error_log
[warn]
=== TEST 7: get last peer failure status (503)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 10;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
local state, status = b.get_last_failure()
print("last peer failure: ", state, " ", status)
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
if ngx.ctx.tries < 2 then
local ok, err = b.set_more_tries(1)
if not ok then
return error("failed to set more tries: ", err)
elseif err then
ngx.log(ngx.WARN, "set more tries: ", err)
end
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
return 503;
}
--- request
GET /t
--- response_body_like: 503 Service Temporarily Unavailable
--- error_code: 503
--- grep_error_log eval: qr{last peer failure: \S+ \S+}
--- grep_error_log_out eval
qr{\Alast peer failure: nil nil
last peer failure: failed 50[23]
last peer failure: failed 50[23]
\z}
--- no_error_log
[warn]
=== TEST 8: get last peer failure status (connect failed)
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 10;
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
local state, status = b.get_last_failure()
print("last peer failure: ", state, " ", status)
if not ngx.ctx.tries then
ngx.ctx.tries = 0
end
if ngx.ctx.tries < 2 then
local ok, err = b.set_more_tries(1)
if not ok then
return error("failed to set more tries: ", err)
elseif err then
ngx.log(ngx.WARN, "set more tries: ", err)
end
end
ngx.ctx.tries = ngx.ctx.tries + 1
assert(b.set_current_peer("127.0.0.3", 12345))
}
}
--- config
location = /t {
proxy_pass http://backend/back;
}
location = /back {
return 404;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- grep_error_log eval: qr{last peer failure: \S+ \S+}
--- grep_error_log_out
last peer failure: nil nil
last peer failure: failed 502
last peer failure: failed 502
--- no_error_log
[warn]
=== TEST 9: set current peer (port embedded in addr)
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_current_peer("127.0.0.3:12345"))
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- error_log eval
[
'[lua] balancer_by_lua(nginx.conf:29):2: hello from balancer by lua! while connecting to upstream,',
qr{connect\(\) failed .*?, upstream: "http://127\.0\.0\.3:12345/t"},
]
--- no_error_log
[warn]
=== TEST 10: keepalive before balancer
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
keepalive 10;
balancer_by_lua_block {
print("hello from balancer by lua!")
local b = require "ngx.balancer"
assert(b.set_current_peer("127.0.0.3:12345"))
}
}
--- config
location = /t {
proxy_pass http://backend;
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- grep_error_log eval: qr/load balancing method redefined in/
--- grep_error_log_out eval
[
"load balancing method redefined in
",
"",
]
--- error_code: 502
--- error_log eval
[
'[lua] balancer_by_lua(nginx.conf:30):2: hello from balancer by lua! while connecting to upstream,',
qr{connect\(\) failed .*?, upstream: "http://127\.0\.0\.3:12345/t"},
]
--- no_error_log
[crit]
=== TEST 11: keepalive after balancer
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
}
keepalive 1;
}
--- config
location = /t {
content_by_lua_block {
local res0 = ngx.location.capture("/tt")
local res1 = ngx.location.capture("/tt")
local res2 = ngx.location.capture("/tt")
if res2.status == ngx.HTTP_OK then
ngx.print(res2.body)
end
}
}
location = /tt {
proxy_pass http://backend/back;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location = /back {
echo "hello keepalive!";
}
--- request
GET /t
--- response_body
hello keepalive!
--- error_code: 200
--- grep_error_log eval: qr{\S+ keepalive peer:.*?connection}
--- grep_error_log_out eval
["free keepalive peer: saving connection
get keepalive peer: using connection
free keepalive peer: saving connection
get keepalive peer: using connection
free keepalive peer: saving connection
",
"get keepalive peer: using connection
free keepalive peer: saving connection
get keepalive peer: using connection
free keepalive peer: saving connection
get keepalive peer: using connection
free keepalive peer: saving connection
",
]
--- no_error_log
[warn]
=== TEST 12: set_current_peer called in a wrong context
--- wait: 0.2
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 127.0.0.1:$TEST_NGINX_SERVER_PORT;
balancer_by_lua_block {
print("hello from balancer by lua!")
}
}
--- config
location = /fake {
echo ok;
}
location = /t {
proxy_pass http://backend/fake;
log_by_lua_block {
local balancer = require "ngx.balancer"
local ok, err = balancer.set_current_peer("127.0.0.1", 1234)
if not ok then
ngx.log(ngx.ERR, "failed to call: ", err)
return
end
ngx.log(ngx.ALERT, "unexpected success")
}
}
--- request
GET /t
--- response_body
ok
--- error_log eval
qr/\[error\] .*? log_by_lua.*? failed to call: API disabled in the current context/
--- no_error_log
[alert]
=== TEST 13: get_last_failure called in a wrong context
--- wait: 0.2
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 127.0.0.1:$TEST_NGINX_SERVER_PORT;
balancer_by_lua_block {
print("hello from balancer by lua!")
}
}
--- config
location = /fake {
echo ok;
}
location = /t {
proxy_pass http://backend/fake;
log_by_lua_block {
local balancer = require "ngx.balancer"
local state, status, err = balancer.get_last_failure()
if not state and err then
ngx.log(ngx.ERR, "failed to call: ", err)
return
end
ngx.log(ngx.ALERT, "unexpected success")
}
}
--- request
GET /t
--- response_body
ok
--- error_log eval
qr/\[error\] .*? log_by_lua.*? failed to call: API disabled in the current context/
--- no_error_log
[alert]
=== TEST 14: set_more_tries called in a wrong context
--- wait: 0.2
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 127.0.0.1:$TEST_NGINX_SERVER_PORT;
balancer_by_lua_block {
print("hello from balancer by lua!")
}
}
--- config
location = /fake {
echo ok;
}
location = /t {
proxy_pass http://backend/fake;
log_by_lua_block {
local balancer = require "ngx.balancer"
local ok, err = balancer.set_more_tries(1)
if not ok then
ngx.log(ngx.ERR, "failed to call: ", err)
return
end
ngx.log(ngx.ALERT, "unexpected success")
}
}
--- request
GET /t
--- response_body
ok
--- error_log eval
qr/\[error\] .*? log_by_lua.*? failed to call: API disabled in the current context/
--- no_error_log
[alert]
=== TEST 15: hot loop when proxy_upstream_next error is hit and keepalive is used.
github issue openresty/lua-nginx-module#693
--- skip_nginx: 4: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
print("hello from balancer by lua!")
assert(b.set_current_peer("127.0.0.1", $TEST_NGINX_SERVER_PORT))
}
keepalive 1;
}
--- config
location /t {
rewrite ^/t(.*) $1 break;
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location = /back {
return 200;
}
location = /main {
echo_location /t/back;
echo_location /t/bad;
}
location = /bad {
content_by_lua_block {
ngx.exit(444)
}
}
--- request
GET /main
--- no_error_log
[alert]
--- ignore_response
--- grep_error_log eval: qr{hello from balancer by lua!}
--- grep_error_log_out
hello from balancer by lua!
hello from balancer by lua!
hello from balancer by lua!
--- error_log eval
qr/\[error] .*? upstream prematurely closed connection while reading response header from upstream/
=== TEST 16: https (keepalive)
--- skip_nginx: 5: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
print("hello from balancer by lua!")
assert(b.set_current_peer("127.0.0.1", $TEST_NGINX_RAND_PORT_1))
}
keepalive 1;
}
server {
listen $TEST_NGINX_RAND_PORT_1 ssl;
ssl_certificate ../../cert/test.crt;
ssl_certificate_key ../../cert/test.key;
server_tokens off;
location = /back {
return 200 "ok";
}
}
--- config
location /t {
proxy_pass https://backend/back;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
--- request
GET /t
--- no_error_log
[alert]
[error]
--- response_body chomp
ok
--- grep_error_log eval: qr{hello from balancer by lua!}
--- grep_error_log_out
hello from balancer by lua!
--- no_check_leak
=== TEST 17: https (no keepalive)
--- skip_nginx: 5: < 1.7.5
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
local b = require "ngx.balancer"
print("hello from balancer by lua!")
assert(b.set_current_peer("127.0.0.1", $TEST_NGINX_RAND_PORT_2))
}
}
server {
listen $TEST_NGINX_RAND_PORT_2 ssl;
ssl_certificate ../../cert/test.crt;
ssl_certificate_key ../../cert/test.key;
server_tokens off;
location = /back {
return 200 "ok";
}
}
--- config
location /t {
proxy_pass https://backend/back;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
--- request
GET /t
--- no_error_log
[alert]
[error]
--- response_body chomp
ok
--- grep_error_log eval: qr{hello from balancer by lua!}
--- grep_error_log_out
hello from balancer by lua!
--- no_check_leak
=== TEST 18: test ngx.var.upstream_addr after using more than one set_current_peer
--- wait: 0.2
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
proxy_next_upstream_tries 3;
upstream backend {
server 127.0.0.1:$TEST_NGINX_SERVER_PORT;
balancer_by_lua_block {
local balancer = require "ngx.balancer"
if ngx.ctx.tries == nil then
balancer.set_more_tries(1)
ngx.ctx.tries = 1
balancer.set_current_peer("127.0.0.3", 12345)
else
balancer.set_current_peer("127.0.0.3", 12346)
end
}
}
--- config
location = /t {
proxy_pass http://backend;
log_by_lua_block {
ngx.log(ngx.INFO, "ngx.var.upstream_addr is " .. ngx.var.upstream_addr)
}
}
--- request
GET /t
--- response_body_like: 502 Bad Gateway
--- error_code: 502
--- error_log eval
qr/log_by_lua\(nginx.conf:\d+\):\d+: ngx.var.upstream_addr is 127.0.0.3:12345, 127.0.0.3:12346/
--- no_error_log
[alert]
=== TEST 19: recreate upstream module requests with header change
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
upstream backend {
server 0.0.0.1;
balancer_by_lua_block {
print("here")
local b = require "ngx.balancer"
if ngx.ctx.balancer_run then
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
ngx.var.test = "second"
assert(b.recreate_request())
else
ngx.ctx.balancer_run = true
assert(b.set_current_peer("127.0.0.3", 12345))
assert(b.set_more_tries(1))
end
}
}
--- config
location = /t {
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
proxy_next_upstream_tries 2;
set $test "first";
proxy_set_header X-Test $test;
proxy_pass http://backend/upstream;
}
location = /upstream {
return 200 "value is: $http_x_test";
}
--- request
GET /t
--- response_body: value is: second
--- error_log
connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1
--- no_error_log
[warn]
[crit]

View File

@ -0,0 +1,42 @@
-----BEGIN CERTIFICATE-----
MIICijCCAfOgAwIBAgICEAQwDQYJKoZIhvcNAQEFBQAwTTELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UE
AwwMU2lnbmluZy1DQS0yMCAXDTE0MDkyMDA1Mjc0NloYDzIxMTQwODI3MDUyNzQ2
WjBJMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UECgwJ
T3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
jQAwgYkCgYEA2FOsrFw/+YColkuwWNtaht7KMALZGcj2FMVAyUHru3rR4fmWO1TV
6L+sUFpJEdqZYETgJWhANnz2zrScuVjW6udEmGPronL46Wm0Sk1ohkHKZ1hh5nDo
CP6twnVZJA7wLxpwg4yjd2ToTdXFKGKpU9GhIvU2Q6dGAKqXVHLUckcCAwEAAaN7
MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYBAD=BAD=BAD=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICdjCCAd+gAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQ
BgNVBAoMCU9wZW5SZXN0eTEQMA4GA1UEAwwHUm9vdCBDQTAgFw0xNDA5MjAwNTA5
MDVaGA8yMTE0MDgyNzA1MDkwNVowTTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
bGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UEAwwMU2lnbmluZy1D
QS0xMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5mj22Md22ivGfYSV5cPbq
S2oPDHLqRfxNUc/1cYiUnPkEQJn9LRcVOt5fcEoGeRP7gUmt2llEEoF0ndgZPk7o
xwDu+ZaBer8J5oiw47LoyuNyI+SGg0HKs0nA9XaK17X8oxIbKwu0VxAkl0C+yxfn
xd6TG1mU/zQ/zU0UdgkO8wIDAQABo1AwTjAdBgNVHQ4EFgQUEleOLJvKyY34iLFN
7qZt85nDr+EwHwYDVR0jBBgwFoAUVmXJi2VVJy6rFPAmRr27nqErQVgwDAYDVR0T
BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAe+28+Er1FEVlS1WD/fHOeMs52+gu2
Slho25KkoNJjJCecasVs+oTUtYCTsHmPM8YGmUmBmfRSur3/bvVpP2XgWVHOFmYv
ObUx/xgqpI4Ud3uiLFRL8KUsgxLE1RxKX3sxJu1jutWD4rUdw/M0oLrd7ofucHGu
G8WXmwimnK3Awg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICYzCCAcygAwIBAgICEAMwDQYJKoZIhvcNAQEFBQAwTTELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UE
AwwMU2lnbmluZy1DQS0xMCAXDTE0MDkyMDA1MjUwNFoYDzIxMTQwODI3MDUyNTA0
WjBNMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UECgwJ
T3BlblJlc3R5MRUwEwYDVQQDDAxTaWduaW5nLUNBLTIwgZ8wDQYJKoZIhvcNAQEB
BQADgY0AMIGJAoGBAKTQrhaoj50s7hL1DF4pZZvMm2dvQCTXRP/U3o3UNhzhNyvf
/2k1bQtPrpoW56nGJNOOpMMvJdjzZnOOhI6cpsf5zoy3nWAmhUyP9EMXr52UGvUh
exwrnO7+Ssptx8/uKgIoH24TlIU/UKMDGL1s+bWdN7knYSl10zl3XoNBqowhAgMB
AAGjUDBOMB0GA1UdDgQWBBQ5d3ejTpKL4iUgcmQ1CnqHqFip+DAfBgNVHSMEGDAW
gBQSV44sm8rJjfiIsU3upm3zmcOv4TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
BQUAA4GBADtLtjFRcprvQmBemGBx1yZKRvEOHwi+5htf4v0oVI2xxQlvBMtp3Dle
Z+CRnxCUvDWQSmX+WL3pnRjwssQsbgUApGNZaoXPDig6rTQcHowIz6x5GOYrFkmc
CwlmUClTeASePSdAxAxy1ozWsZz18viMnAsN4Uub7MllDB7+JweW
-----END CERTIFICATE-----

View File

@ -0,0 +1,39 @@
-----BEGIN CERTIFICATE-----
MIICijCCAfOgAwIBAgICEAQwDQYJKoZIhvcNAQEFBQAwTTELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UE
AwwMU2lnbmluZy1DQS0yMCAXDTE0MDkyMDA1Mjc0NloYDzIxMTQwODI3MDUyNzQ2
WjBJMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UECgwJ
T3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
jQAwgYkCgYEA2FOsrFw/+YColkuwWNtaht7KMALZGcj2FMVAyUHru3rR4fmWO1TV
6L+sUFpJEdqZYETgJWhANnz2zrScuVjW6udEmGPronL46Wm0Sk1ohkHKZ1hh5nDo
CP6twnVZJA7wLxpwg4yjd2ToTdXFKGKpU9GhIvU2Q6dGAKqXVHLUckcCAwEAAaN7
MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQg
Q2VydGlmaWNhdGUwHQYDVR0OBBYEFB/bwNk8S3eomqwzH3twxM+6yAfdMB8GA1Ud
IwQYMBaAFDl3d6NOkoviJSByZDUKeoeoWKn4MA0GCSqGSIb3DQEBBQUAA4GBAB7N
g2ax2+pcN368MURScgOum0QgLK0AIKXcz53IyI/fzyQmnEOD9NL/69nkfSXPH7iq
Y1gDudpSQvj+LnHMj94mNM3aXHo7ZAcYJ6FhtlgylhCX8n8AxERDt53iMWlPwpXF
ozLRwADG71i5D+YIOg3JwBT3JoxDE1Ubk3Fyx60v
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICdjCCAd+gAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQ
BgNVBAoMCU9wZW5SZXN0eTEQMA4GA1UEAwwHUm9vdCBDQTAgFw0xNDA5MjAwNTA5
MDVaGA8yMTE0MDgyNzA1MDkwNVowTTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
bGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UEAwwMU2lnbmluZy1D
QS0xMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5mj22Md22ivGfYSV5cPbq
S2oPDHLqRfxNUc/1cYiUnPkEQJn9LRcVOt5fcEoGeRP7gUmt2llEEoF0ndgZPk7o
xwDu+ZaBer8J5oiw47LoyuNyI+SGg0HKs0nA9XaK17X8oxIbKwu0VxAkl0C+yxfn
xd6TG1mU/zQ/zU0UdgkO8wIDAQABo1AwTjAdBgNVHQ4EFgQUEleOLJvKyY34iLFN
7qZt85nDr+EwHwYDVR0jBBgwFoAUVmXJi2VVJy6rFPAmRr27nqErQVgwDAYDVR0T
BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAe+28+Er1FEVlS1WD/fHOeMs52+gu2
Slho25KkoNJjJCecasVs+oTUtYCTsHmPM8YGmUmBmfRSur3/bvVpP2XgWVHOFmYv
ObUx/xgqpI4Ud3uiLFRL8KUsgxLE1RxKX3sxJu1jutWD4rUdw/M0oLrd7ofucHGu
G8WXmwimnK3Awg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICYzCCAcygAwIBAgICEAMwDQYJKoZIhvcNAQEFBQAwTTELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UE
AwwMU2lnbmluZy1DQS0xMCAXDTE0MDkyMDA1MjUwNFoYDzIxMTQwODI3MDUyNTA0
WjBNMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UECgwJ
BAD=BAD=BAD
-----END CERTIFICATE-----

BIN
t/cert/chain/chain.der Normal file

Binary file not shown.

172
t/cert/chain/chain.pem Normal file
View File

@ -0,0 +1,172 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4100 (0x1004)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, O=OpenResty, CN=Signing-CA-2
Validity
Not Before: Sep 20 05:27:46 2014 GMT
Not After : Aug 27 05:27:46 2114 GMT
Subject: C=US, ST=California, O=OpenResty, CN=test.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:d8:53:ac:ac:5c:3f:f9:80:a8:96:4b:b0:58:db:
5a:86:de:ca:30:02:d9:19:c8:f6:14:c5:40:c9:41:
eb:bb:7a:d1:e1:f9:96:3b:54:d5:e8:bf:ac:50:5a:
49:11:da:99:60:44:e0:25:68:40:36:7c:f6:ce:b4:
9c:b9:58:d6:ea:e7:44:98:63:eb:a2:72:f8:e9:69:
b4:4a:4d:68:86:41:ca:67:58:61:e6:70:e8:08:fe:
ad:c2:75:59:24:0e:f0:2f:1a:70:83:8c:a3:77:64:
e8:4d:d5:c5:28:62:a9:53:d1:a1:22:f5:36:43:a7:
46:00:aa:97:54:72:d4:72:47
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
1F:DB:C0:D9:3C:4B:77:A8:9A:AC:33:1F:7B:70:C4:CF:BA:C8:07:DD
X509v3 Authority Key Identifier:
keyid:39:77:77:A3:4E:92:8B:E2:25:20:72:64:35:0A:7A:87:A8:58:A9:F8
Signature Algorithm: sha1WithRSAEncryption
1e:cd:83:66:b1:db:ea:5c:37:7e:bc:31:44:52:72:03:ae:9b:
44:20:2c:ad:00:20:a5:dc:cf:9d:c8:c8:8f:df:cf:24:26:9c:
43:83:f4:d2:ff:eb:d9:e4:7d:25:cf:1f:b8:aa:63:58:03:b9:
da:52:42:f8:fe:2e:71:cc:8f:de:26:34:cd:da:5c:7a:3b:64:
07:18:27:a1:61:b6:58:32:96:10:97:f2:7f:00:c4:44:43:b7:
9d:e2:31:69:4f:c2:95:c5:a3:32:d1:c0:00:c6:ef:58:b9:0f:
e6:08:3a:0d:c9:c0:14:f7:26:8c:43:13:55:1b:93:71:72:c7:
ad:2f
-----BEGIN CERTIFICATE-----
MIICijCCAfOgAwIBAgICEAQwDQYJKoZIhvcNAQEFBQAwTTELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UE
AwwMU2lnbmluZy1DQS0yMCAXDTE0MDkyMDA1Mjc0NloYDzIxMTQwODI3MDUyNzQ2
WjBJMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UECgwJ
T3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
jQAwgYkCgYEA2FOsrFw/+YColkuwWNtaht7KMALZGcj2FMVAyUHru3rR4fmWO1TV
6L+sUFpJEdqZYETgJWhANnz2zrScuVjW6udEmGPronL46Wm0Sk1ohkHKZ1hh5nDo
CP6twnVZJA7wLxpwg4yjd2ToTdXFKGKpU9GhIvU2Q6dGAKqXVHLUckcCAwEAAaN7
MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQg
Q2VydGlmaWNhdGUwHQYDVR0OBBYEFB/bwNk8S3eomqwzH3twxM+6yAfdMB8GA1Ud
IwQYMBaAFDl3d6NOkoviJSByZDUKeoeoWKn4MA0GCSqGSIb3DQEBBQUAA4GBAB7N
g2ax2+pcN368MURScgOum0QgLK0AIKXcz53IyI/fzyQmnEOD9NL/69nkfSXPH7iq
Y1gDudpSQvj+LnHMj94mNM3aXHo7ZAcYJ6FhtlgylhCX8n8AxERDt53iMWlPwpXF
ozLRwADG71i5D+YIOg3JwBT3JoxDE1Ubk3Fyx60v
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4098 (0x1002)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=San Francisco, O=OpenResty, CN=Root CA
Validity
Not Before: Sep 20 05:09:05 2014 GMT
Not After : Aug 27 05:09:05 2114 GMT
Subject: C=US, ST=California, O=OpenResty, CN=Signing-CA-1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:b9:9a:3d:b6:31:dd:b6:8a:f1:9f:61:25:79:70:
f6:ea:4b:6a:0f:0c:72:ea:45:fc:4d:51:cf:f5:71:
88:94:9c:f9:04:40:99:fd:2d:17:15:3a:de:5f:70:
4a:06:79:13:fb:81:49:ad:da:59:44:12:81:74:9d:
d8:19:3e:4e:e8:c7:00:ee:f9:96:81:7a:bf:09:e6:
88:b0:e3:b2:e8:ca:e3:72:23:e4:86:83:41:ca:b3:
49:c0:f5:76:8a:d7:b5:fc:a3:12:1b:2b:0b:b4:57:
10:24:97:40:be:cb:17:e7:c5:de:93:1b:59:94:ff:
34:3f:cd:4d:14:76:09:0e:f3
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
12:57:8E:2C:9B:CA:C9:8D:F8:88:B1:4D:EE:A6:6D:F3:99:C3:AF:E1
X509v3 Authority Key Identifier:
keyid:56:65:C9:8B:65:55:27:2E:AB:14:F0:26:46:BD:BB:9E:A1:2B:41:58
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
1e:fb:6f:3e:12:bd:45:11:59:52:d5:60:ff:7c:73:9e:32:ce:
76:fa:0b:b6:4a:58:68:db:92:a4:a0:d2:63:24:27:9c:6a:c5:
6c:fa:84:d4:b5:80:93:b0:79:8f:33:c6:06:99:49:81:99:f4:
52:ba:bd:ff:6e:f5:69:3f:65:e0:59:51:ce:16:66:2f:39:b5:
31:ff:18:2a:a4:8e:14:77:7b:a2:2c:54:4b:f0:a5:2c:83:12:
c4:d5:1c:4a:5f:7b:31:26:ed:63:ba:d5:83:e2:b5:1d:c3:f3:
34:a0:ba:dd:ee:87:ee:70:71:ae:1b:c5:97:9b:08:a6:9c:ad:
c0:c2
-----BEGIN CERTIFICATE-----
MIICdjCCAd+gAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQ
BgNVBAoMCU9wZW5SZXN0eTEQMA4GA1UEAwwHUm9vdCBDQTAgFw0xNDA5MjAwNTA5
MDVaGA8yMTE0MDgyNzA1MDkwNVowTTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
bGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UEAwwMU2lnbmluZy1D
QS0xMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5mj22Md22ivGfYSV5cPbq
S2oPDHLqRfxNUc/1cYiUnPkEQJn9LRcVOt5fcEoGeRP7gUmt2llEEoF0ndgZPk7o
xwDu+ZaBer8J5oiw47LoyuNyI+SGg0HKs0nA9XaK17X8oxIbKwu0VxAkl0C+yxfn
xd6TG1mU/zQ/zU0UdgkO8wIDAQABo1AwTjAdBgNVHQ4EFgQUEleOLJvKyY34iLFN
7qZt85nDr+EwHwYDVR0jBBgwFoAUVmXJi2VVJy6rFPAmRr27nqErQVgwDAYDVR0T
BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAe+28+Er1FEVlS1WD/fHOeMs52+gu2
Slho25KkoNJjJCecasVs+oTUtYCTsHmPM8YGmUmBmfRSur3/bvVpP2XgWVHOFmYv
ObUx/xgqpI4Ud3uiLFRL8KUsgxLE1RxKX3sxJu1jutWD4rUdw/M0oLrd7ofucHGu
G8WXmwimnK3Awg==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4099 (0x1003)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, O=OpenResty, CN=Signing-CA-1
Validity
Not Before: Sep 20 05:25:04 2014 GMT
Not After : Aug 27 05:25:04 2114 GMT
Subject: C=US, ST=California, O=OpenResty, CN=Signing-CA-2
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:a4:d0:ae:16:a8:8f:9d:2c:ee:12:f5:0c:5e:29:
65:9b:cc:9b:67:6f:40:24:d7:44:ff:d4:de:8d:d4:
36:1c:e1:37:2b:df:ff:69:35:6d:0b:4f:ae:9a:16:
e7:a9:c6:24:d3:8e:a4:c3:2f:25:d8:f3:66:73:8e:
84:8e:9c:a6:c7:f9:ce:8c:b7:9d:60:26:85:4c:8f:
f4:43:17:af:9d:94:1a:f5:21:7b:1c:2b:9c:ee:fe:
4a:ca:6d:c7:cf:ee:2a:02:28:1f:6e:13:94:85:3f:
50:a3:03:18:bd:6c:f9:b5:9d:37:b9:27:61:29:75:
d3:39:77:5e:83:41:aa:8c:21
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
39:77:77:A3:4E:92:8B:E2:25:20:72:64:35:0A:7A:87:A8:58:A9:F8
X509v3 Authority Key Identifier:
keyid:12:57:8E:2C:9B:CA:C9:8D:F8:88:B1:4D:EE:A6:6D:F3:99:C3:AF:E1
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
3b:4b:b6:31:51:72:9a:ef:42:60:5e:98:60:71:d7:26:4a:46:
f1:0e:1f:08:be:e6:1b:5f:e2:fd:28:54:8d:b1:c5:09:6f:04:
cb:69:dc:39:5e:67:e0:91:9f:10:94:bc:35:90:4a:65:fe:58:
bd:e9:9d:18:f0:b2:c4:2c:6e:05:00:a4:63:59:6a:85:cf:0e:
28:3a:ad:34:1c:1e:8c:08:cf:ac:79:18:e6:2b:16:49:9c:0b:
09:66:50:29:53:78:04:9e:3d:27:40:c4:0c:72:d6:8c:d6:b1:
9c:f5:f2:f8:8c:9c:0b:0d:e1:4b:9b:ec:c9:65:0c:1e:fe:27:
07:96
-----BEGIN CERTIFICATE-----
MIICYzCCAcygAwIBAgICEAMwDQYJKoZIhvcNAQEFBQAwTTELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEVMBMGA1UE
AwwMU2lnbmluZy1DQS0xMCAXDTE0MDkyMDA1MjUwNFoYDzIxMTQwODI3MDUyNTA0
WjBNMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UECgwJ
T3BlblJlc3R5MRUwEwYDVQQDDAxTaWduaW5nLUNBLTIwgZ8wDQYJKoZIhvcNAQEB
BQADgY0AMIGJAoGBAKTQrhaoj50s7hL1DF4pZZvMm2dvQCTXRP/U3o3UNhzhNyvf
/2k1bQtPrpoW56nGJNOOpMMvJdjzZnOOhI6cpsf5zoy3nWAmhUyP9EMXr52UGvUh
exwrnO7+Ssptx8/uKgIoH24TlIU/UKMDGL1s+bWdN7knYSl10zl3XoNBqowhAgMB
AAGjUDBOMB0GA1UdDgQWBBQ5d3ejTpKL4iUgcmQ1CnqHqFip+DAfBgNVHSMEGDAW
gBQSV44sm8rJjfiIsU3upm3zmcOv4TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
BQUAA4GBADtLtjFRcprvQmBemGBx1yZKRvEOHwi+5htf4v0oVI2xxQlvBMtp3Dle
Z+CRnxCUvDWQSmX+WL3pnRjwssQsbgUApGNZaoXPDig6rTQcHowIz6x5GOYrFkmc
CwlmUClTeASePSdAxAxy1ozWsZz18viMnAsN4Uub7MllDB7+JweW
-----END CERTIFICATE-----

16
t/cert/chain/root-ca.crt Normal file
View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICkDCCAfmgAwIBAgIJAK3s1yAQ5tdfMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
c2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEDAOBgNVBAMMB1Jvb3QgQ0EwIBcNMTQw
OTIwMDM1NTU0WhgPMjExNDA4MjcwMzU1NTRaMGAxCzAJBgNVBAYTAlVTMRMwEQYD
VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQK
DAlPcGVuUmVzdHkxEDAOBgNVBAMMB1Jvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBAN7CcpCjiafBdl1KaExRcuutAF0/eq4/ht7L4/i0nPDzikscFJ/O
aVyH3UpUF/KMq+72vom2bEbUeRROr1rL/JRe9raGlQtvdovHZt6f4c3/Coihtupp
9BXYrBCU4P+Bxai5gtTXGFvLC2a72qKcXDNeH+NxpIaemfPxSvemCYUXAgMBAAGj
UDBOMB0GA1UdDgQWBBRWZcmLZVUnLqsU8CZGvbueoStBWDAfBgNVHSMEGDAWgBRW
ZcmLZVUnLqsU8CZGvbueoStBWDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
A4GBAGjMH6qkY+61311DERFhDuYzMSSZjH53qzFseq/chlIMGjrgJIMy6rl7T0AU
2hjvW+FOyhf5NqRrAQDTTuLbtXZ/ygiUformE8lR/SNRY/DVj1yarQkWUC5UpqOs
GWG1VW9DHQAMFVkYwPO3XKeTXpEFOxPLHtXBYcVemCT4zo42
-----END CERTIFICATE-----

View File

@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDYU6ysXD/5gKiWS7BY21qG3sowAtkZyPYUxUDJQeu7etHh+ZY7
VNXov6xQWkkR2plgROAlaEA2fPbOtJy5WNbq50SYY+uicvjpabRKTWiGQcpnWGHm
cOgI/q3CdVkkDvAvGnCDjKN3ZOhN1cUoYqlT0aEi9TZDp0YAqpdUctRyRwIDAQAB
AoGBAIl/5elIWYGFPaMKSPSxuECxq2II7WVuTru1BRDnTabE0lMICW185tohuqz4
NimbAJIoNTCRqv73Pwjz1AobZb6Nm7TDaahhstak6IlTYKcjXVBuM/UU4G13Kz/f
hNVblv2cCn9CkeTNOvPZjYJXw/c4XlHasjDMMh8S83Q9095BAkEA+6oPzEiSsdo5
BAD=BAD=BAD=
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDYU6ysXD/5gKiWS7BY21qG3sowAtkZyPYUxUDJQeu7etHh+ZY7
VNXov6xQWkkR2plgROAlaEA2fPbOtJy5WNbq50SYY+uicvjpabRKTWiGQcpnWGHm
cOgI/q3CdVkkDvAvGnCDjKN3ZOhN1cUoYqlT0aEi9TZDp0YAqpdUctRyRwIDAQAB
AoGBAIl/5elIWYGFPaMKSPSxuECxq2II7WVuTru1BRDnTabE0lMICW185tohuqz4
NimbAJIoNTCRqv73Pwjz1AobZb6Nm7TDaahhstak6IlTYKcjXVBuM/UU4G13Kz/f
hNVblv2cCn9CkeTNOvPZjYJXw/c4XlHasjDMMh8S83Q9095BAkEA+6oPzEiSsdo5
RX9D0EV+Uv4ID08johKbcZdGbsp+mo+PQ9CYOlE67QcKf8J4Hp2SFmq7mpTvvS7F
tA/a2WwJswJBANwNwsJre3QPJmJCBAGsIrPrw9rFKLiT0/ajyhT7kKfG4Rw9t55S
lY9VPFOxAJF9lDo4QiFUHi/8Htvd0B78wx0CQFh5cRRgbzIXhgrosu6Ff+Otayf2
qpBP+lX02M4aYmf0EGnG672U0SKDVy2TMKeSvckjvNCbi6z2xIqJCGdnlAECQFTh
+f6E91oNfgDo9iKvA7PjfeklpE+OtnStOYZeg640SSFbrTilIovnlR2zaUS17DeI
+/lfOUXJOx4UsfNCDQECQD7nndBJDJeSggFSJKcZ0RI59NVG8eGRSX7/3ycbq6+t
guGI7WBvhDH4jNNL8jhuE+XuJuhhzOwP85872AFgIgw=
-----END RSA PRIVATE KEY-----

183
t/cert/ocsp/chain.pem Normal file
View File

@ -0,0 +1,183 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4 (0x4)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=test.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:c7:bd:50:99:71:46:af:93:22:85:ab:74:8b:5b:
19:74:af:3e:ad:d2:e1:17:3e:cb:5b:36:9c:8a:38:
bd:1b:47:2d:8b:92:55:1d:fe:a6:72:92:78:00:de:
30:cb:a3:10:b5:92:aa:b8:e0:7b:44:9a:f5:99:89:
36:f4:84:20:81:e3:5c:76:00:9d:76:e7:b9:41:ab:
74:b6:14:9f:b2:94:b3:b6:48:a8:92:dc:09:e3:3d:
04:e3:5f:0f:5b:50:ad:0c:59:3a:88:06:39:2d:34:
a6:52:2f:58:6f:53:1b:df:9f:98:ea:82:8d:52:60:
b1:ef:6b:e9:f5:ad:29:87:45
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
67:DF:28:25:D1:F8:83:36:28:EE:DB:41:63:E4:E0:3A:32:0D:EA:30
X509v3 Authority Key Identifier:
keyid:B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
DirName:/C=US/ST=California/L=Default City/O=OpenResty/CN=signing-ca-1
serial:03
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888/ocsp?foo=1
Signature Algorithm: sha1WithRSAEncryption
37:29:3f:ed:d9:47:9a:51:36:a3:5b:00:85:66:de:51:4d:48:
2d:f8:bc:f1:5e:b4:fd:30:48:f0:25:ee:77:57:9c:f1:4b:0a:
4f:7e:96:1a:f8:48:76:23:46:8d:d6:f2:5e:1e:08:52:12:53:
08:07:9f:75:db:77:22:2e:7e:89:c2:2c:66:85:6b:df:e9:77:
ca:23:6d:9a:af:87:8a:8c:27:37:1e:9e:55:92:8e:8a:a9:93:
24:41:a8:96:01:c0:65:93:8e:3d:7a:6c:bf:ed:c8:2a:f8:26:
cc:00:17:b7:27:ca:85:6c:2e:d5:2a:0a:8d:f3:88:e8:26:48:
e3:e8
-----BEGIN CERTIFICATE-----
MIIDaTCCAtKgAwIBAgIBBDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMjAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MREwDwYDVQQDEwh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEAx71QmXFGr5Mihat0i1sZdK8+rdLhFz7LWzaciji9G0cti5JVHf6mcpJ4
AN4wy6MQtZKquOB7RJr1mYk29IQggeNcdgCddue5Qat0thSfspSztkioktwJ4z0E
418PW1CtDFk6iAY5LTSmUi9Yb1Mb35+Y6oKNUmCx72vp9a0ph0UCAwEAAaOCASsw
ggEnMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVk
IENlcnRpZmljYXRlMB0GA1UdDgQWBBRn3ygl0fiDNiju20Fj5OA6Mg3qMDCBjgYD
VR0jBIGGMIGDgBSzC/V9URZRfig3w6IPHS8QwFGjs6FopGYwZDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTES
MBAGA1UEChMJT3BlblJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTGCAQMwPAYI
KwYBBQUHAQEEMDAuMCwGCCsGAQUFBzABhiBodHRwOi8vMTI3LjAuMC4xOjg4ODgv
b2NzcD9mb289MTANBgkqhkiG9w0BAQUFAAOBgQA3KT/t2UeaUTajWwCFZt5RTUgt
+LzxXrT9MEjwJe53V5zxSwpPfpYa+Eh2I0aN1vJeHghSElMIB59123ciLn6Jwixm
hWvf6XfKI22ar4eKjCc3Hp5Vko6KqZMkQaiWAcBlk449emy/7cgq+CbMABe3J8qF
bC7VKgqN84joJkjj6A==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-1
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:d3:24:1c:92:a5:bb:00:d9:b1:fb:2b:1d:7a:32:
a1:6c:49:eb:3c:2d:29:80:d6:65:8b:17:3a:f0:4b:
dc:0c:57:fb:d5:31:68:a5:e4:54:86:55:f9:1b:a8:
d7:7d:32:01:3b:cf:5c:38:2b:f5:bc:d3:8b:c8:b6:
ab:76:65:32:e6:4b:d5:e4:fd:d1:92:c8:33:6a:74:
f3:c7:ec:97:c3:c7:9f:e4:d5:55:75:b8:bd:39:ec:
2d:1f:c6:54:c8:2b:2d:17:e0:05:77:28:44:f7:dd:
e1:6e:f0:59:05:51:f5:b9:b4:fe:be:ad:40:a6:d5:
9a:c1:64:e0:9b:dd:67:e5:f1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
X509v3 Authority Key Identifier:
keyid:D2:30:71:56:50:A6:BC:21:C5:A1:A1:AB:11:A7:08:5B:EB:3A:A4:27
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
0c:61:c0:c7:11:c2:f0:39:f0:76:9d:4f:43:d4:90:54:1f:26:
3d:54:3d:77:5f:c0:b3:4a:c2:1b:b6:18:d2:12:8d:24:4d:76:
f5:07:0b:14:3e:17:2d:42:ee:85:30:db:e3:4d:81:67:59:97:
0a:b3:bb:c5:27:ea:69:c6:ee:99:5c:44:36:53:3e:c4:47:68:
f8:fe:c6:53:38:fb:e7:9a:0c:3c:6c:78:93:29:d2:49:7d:29:
d0:61:6e:81:9b:d6:ec:1a:e2:3e:62:62:41:bc:6d:4d:33:91:
76:20:5e:32:70:08:3e:24:72:fe:b1:8a:83:57:04:19:b5:cb:
99:b7
-----BEGIN CERTIFICATE-----
MIICkDCCAfmgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMTAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowZDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTIwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBANMkHJKluwDZsfsrHXoyoWxJ6zwtKYDWZYsXOvBL3AxX+9UxaKXk
VIZV+Ruo130yATvPXDgr9bzTi8i2q3ZlMuZL1eT90ZLIM2p088fsl8PHn+TVVXW4
vTnsLR/GVMgrLRfgBXcoRPfd4W7wWQVR9bm0/r6tQKbVmsFk4JvdZ+XxAgMBAAGj
UDBOMB0GA1UdDgQWBBSzC/V9URZRfig3w6IPHS8QwFGjszAfBgNVHSMEGDAWgBTS
MHFWUKa8IcWhoasRpwhb6zqkJzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
A4GBAAxhwMcRwvA58HadT0PUkFQfJj1UPXdfwLNKwhu2GNISjSRNdvUHCxQ+Fy1C
7oUw2+NNgWdZlwqzu8Un6mnG7plcRDZTPsRHaPj+xlM4++eaDDxseJMp0kl9KdBh
boGb1uwa4j5iYkG8bU0zkXYgXjJwCD4kcv6xioNXBBm1y5m3
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=root-ca
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:a0:3e:1a:4f:6c:b9:3d:ab:0f:02:de:da:82:92:
ee:a2:69:88:80:ed:f2:b6:98:bc:c6:ee:d3:47:82:
4a:e7:d3:7f:55:68:5c:6d:9e:aa:ba:59:e3:5b:7f:
32:4f:79:44:4a:4f:13:e4:2e:3f:1f:98:10:a4:72:
d5:f0:e7:44:8e:d4:a7:b9:fb:54:be:b6:fa:f7:dc:
9c:29:93:d4:9f:a1:5b:18:6e:68:93:91:1b:8c:a0:
4f:02:52:e9:9d:e8:98:f3:fd:67:da:78:4b:4f:d8:
2d:90:83:5c:0b:e5:fe:48:27:e4:ec:bb:99:26:06:
8e:34:fe:93:e4:d2:fc:97:57
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D2:30:71:56:50:A6:BC:21:C5:A1:A1:AB:11:A7:08:5B:EB:3A:A4:27
X509v3 Authority Key Identifier:
keyid:1D:2F:09:60:EB:E4:EA:B5:0B:52:A9:5C:5E:09:2B:DD:34:70:CF:BA
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
a6:16:2f:fc:13:67:5e:ce:0e:79:cb:b0:91:52:9b:9e:b5:9f:
e1:fa:7d:78:f4:2a:93:f3:94:62:45:17:87:b9:0a:59:b9:a3:
a9:75:51:ca:f0:04:6c:01:d1:3a:a9:dd:66:7d:27:7b:1e:4f:
48:3a:25:ea:a5:01:32:fc:87:4b:08:da:f8:f5:62:88:e8:b9:
94:c7:cb:ee:33:08:ab:2f:52:f4:4a:14:4f:ac:2d:a2:f8:de:
c9:6f:95:b7:91:23:b9:ec:95:90:de:86:21:f5:6f:1b:cf:13:
47:77:78:dd:7a:16:e9:8b:cc:df:3d:45:8a:76:af:15:d1:9a:
37:a2
-----BEGIN CERTIFICATE-----
MIICizCCAfSgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxEDAOBgNVBAMTB3Jvb3QtY2EwIBcNMTQxMDE2MDMyNzA5
WhgPMjExNDA5MjIwMzI3MDlaMGQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxp
Zm9ybmlhMRUwEwYDVQQHEwxEZWZhdWx0IENpdHkxEjAQBgNVBAoTCU9wZW5SZXN0
eTEVMBMGA1UEAxMMc2lnbmluZy1jYS0xMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQCgPhpPbLk9qw8C3tqCku6iaYiA7fK2mLzG7tNHgkrn039VaFxtnqq6WeNb
fzJPeURKTxPkLj8fmBCkctXw50SO1Ke5+1S+tvr33Jwpk9SfoVsYbmiTkRuMoE8C
Uumd6Jjz/WfaeEtP2C2Qg1wL5f5IJ+Tsu5kmBo40/pPk0vyXVwIDAQABo1AwTjAd
BgNVHQ4EFgQU0jBxVlCmvCHFoaGrEacIW+s6pCcwHwYDVR0jBBgwFoAUHS8JYOvk
6rULUqlcXgkr3TRwz7owDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCm
Fi/8E2dezg55y7CRUpuetZ/h+n149CqT85RiRReHuQpZuaOpdVHK8ARsAdE6qd1m
fSd7Hk9IOiXqpQEy/IdLCNr49WKI6LmUx8vuMwirL1L0ShRPrC2i+N7Jb5W3kSO5
7JWQ3oYh9W8bzxNHd3jdehbpi8zfPUWKdq8V0Zo3og==
-----END CERTIFICATE-----

BIN
t/cert/ocsp/ocsp-req.der Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
t/cert/ocsp/ocsp-resp.der Normal file

Binary file not shown.

View File

@ -0,0 +1,183 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 8 (0x8)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=revoked-test.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:ca:50:23:9a:59:70:ea:00:47:ff:72:05:29:9b:
5d:6d:4b:73:37:a4:ff:38:20:4b:5b:ac:1f:3b:34:
f5:12:f8:8b:0e:02:bc:bd:14:34:39:6f:7d:5b:1f:
d4:15:e7:64:2e:65:fb:b1:a8:aa:f6:96:d3:e6:2b:
00:0e:f3:8a:ef:99:ab:3e:e6:5d:eb:6d:a6:4a:d0:
aa:ff:a9:d6:9a:41:f0:66:22:0a:38:9c:28:4f:1f:
0d:cf:a2:79:96:f9:fc:3d:1e:83:70:f5:97:6e:07:
cf:a2:17:87:0d:2a:41:19:3a:44:96:89:e7:0d:cb:
88:20:86:e1:de:08:8b:0d:db
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
FB:98:2B:56:90:69:E1:B4:2B:C2:DB:25:7C:13:87:D5:D7:BC:70:B6
X509v3 Authority Key Identifier:
keyid:B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
DirName:/C=US/ST=California/L=Default City/O=OpenResty/CN=signing-ca-1
serial:03
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888/ocsp?foo=1
Signature Algorithm: sha1WithRSAEncryption
43:77:33:e9:cc:b1:42:35:94:0a:57:a5:dd:94:21:c0:cc:42:
04:81:bd:b2:ac:4d:10:68:f3:fe:33:0a:8e:b9:3e:e9:f2:44:
aa:1c:e7:3e:e8:e0:57:40:41:ef:4a:b1:32:b0:f2:75:7c:aa:
77:d2:64:9d:ba:a1:12:ea:f9:83:31:ba:9f:83:58:1c:38:e9:
d0:a6:dd:04:72:85:d1:2d:c7:3b:b2:71:ef:e4:f6:57:0c:6a:
b6:fc:e5:13:2d:be:a6:c1:f4:4b:4d:c8:69:cc:7c:2e:25:c1:
8e:80:9e:19:c3:17:b2:21:a7:af:e8:2f:f1:d4:bb:8c:a3:39:
be:49
-----BEGIN CERTIFICATE-----
MIIDcTCCAtqgAwIBAgIBCDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMjAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowaDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MRkwFwYDVQQDExByZXZva2VkLXRlc3QuY29tMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDKUCOaWXDqAEf/cgUpm11tS3M3pP84IEtbrB87NPUS+IsO
Ary9FDQ5b31bH9QV52QuZfuxqKr2ltPmKwAO84rvmas+5l3rbaZK0Kr/qdaaQfBm
Igo4nChPHw3PonmW+fw9HoNw9ZduB8+iF4cNKkEZOkSWiecNy4gghuHeCIsN2wID
AQABo4IBKzCCAScwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH
ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFPuYK1aQaeG0K8LbJXwTh9XX
vHC2MIGOBgNVHSMEgYYwgYOAFLML9X1RFlF+KDfDog8dLxDAUaOzoWikZjBkMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVs
dCBDaXR5MRIwEAYDVQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2Et
MYIBAzA8BggrBgEFBQcBAQQwMC4wLAYIKwYBBQUHMAGGIGh0dHA6Ly8xMjcuMC4w
LjE6ODg4OC9vY3NwP2Zvbz0xMA0GCSqGSIb3DQEBBQUAA4GBAEN3M+nMsUI1lApX
pd2UIcDMQgSBvbKsTRBo8/4zCo65PunyRKoc5z7o4FdAQe9KsTKw8nV8qnfSZJ26
oRLq+YMxup+DWBw46dCm3QRyhdEtxzuyce/k9lcMarb85RMtvqbB9EtNyGnMfC4l
wY6AnhnDF7Ihp6/oL/HUu4yjOb5J
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-1
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:d3:24:1c:92:a5:bb:00:d9:b1:fb:2b:1d:7a:32:
a1:6c:49:eb:3c:2d:29:80:d6:65:8b:17:3a:f0:4b:
dc:0c:57:fb:d5:31:68:a5:e4:54:86:55:f9:1b:a8:
d7:7d:32:01:3b:cf:5c:38:2b:f5:bc:d3:8b:c8:b6:
ab:76:65:32:e6:4b:d5:e4:fd:d1:92:c8:33:6a:74:
f3:c7:ec:97:c3:c7:9f:e4:d5:55:75:b8:bd:39:ec:
2d:1f:c6:54:c8:2b:2d:17:e0:05:77:28:44:f7:dd:
e1:6e:f0:59:05:51:f5:b9:b4:fe:be:ad:40:a6:d5:
9a:c1:64:e0:9b:dd:67:e5:f1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
X509v3 Authority Key Identifier:
keyid:D2:30:71:56:50:A6:BC:21:C5:A1:A1:AB:11:A7:08:5B:EB:3A:A4:27
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
0c:61:c0:c7:11:c2:f0:39:f0:76:9d:4f:43:d4:90:54:1f:26:
3d:54:3d:77:5f:c0:b3:4a:c2:1b:b6:18:d2:12:8d:24:4d:76:
f5:07:0b:14:3e:17:2d:42:ee:85:30:db:e3:4d:81:67:59:97:
0a:b3:bb:c5:27:ea:69:c6:ee:99:5c:44:36:53:3e:c4:47:68:
f8:fe:c6:53:38:fb:e7:9a:0c:3c:6c:78:93:29:d2:49:7d:29:
d0:61:6e:81:9b:d6:ec:1a:e2:3e:62:62:41:bc:6d:4d:33:91:
76:20:5e:32:70:08:3e:24:72:fe:b1:8a:83:57:04:19:b5:cb:
99:b7
-----BEGIN CERTIFICATE-----
MIICkDCCAfmgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMTAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowZDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTIwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBANMkHJKluwDZsfsrHXoyoWxJ6zwtKYDWZYsXOvBL3AxX+9UxaKXk
VIZV+Ruo130yATvPXDgr9bzTi8i2q3ZlMuZL1eT90ZLIM2p088fsl8PHn+TVVXW4
vTnsLR/GVMgrLRfgBXcoRPfd4W7wWQVR9bm0/r6tQKbVmsFk4JvdZ+XxAgMBAAGj
UDBOMB0GA1UdDgQWBBSzC/V9URZRfig3w6IPHS8QwFGjszAfBgNVHSMEGDAWgBTS
MHFWUKa8IcWhoasRpwhb6zqkJzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
A4GBAAxhwMcRwvA58HadT0PUkFQfJj1UPXdfwLNKwhu2GNISjSRNdvUHCxQ+Fy1C
7oUw2+NNgWdZlwqzu8Un6mnG7plcRDZTPsRHaPj+xlM4++eaDDxseJMp0kl9KdBh
boGb1uwa4j5iYkG8bU0zkXYgXjJwCD4kcv6xioNXBBm1y5m3
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=root-ca
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:a0:3e:1a:4f:6c:b9:3d:ab:0f:02:de:da:82:92:
ee:a2:69:88:80:ed:f2:b6:98:bc:c6:ee:d3:47:82:
4a:e7:d3:7f:55:68:5c:6d:9e:aa:ba:59:e3:5b:7f:
32:4f:79:44:4a:4f:13:e4:2e:3f:1f:98:10:a4:72:
d5:f0:e7:44:8e:d4:a7:b9:fb:54:be:b6:fa:f7:dc:
9c:29:93:d4:9f:a1:5b:18:6e:68:93:91:1b:8c:a0:
4f:02:52:e9:9d:e8:98:f3:fd:67:da:78:4b:4f:d8:
2d:90:83:5c:0b:e5:fe:48:27:e4:ec:bb:99:26:06:
8e:34:fe:93:e4:d2:fc:97:57
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D2:30:71:56:50:A6:BC:21:C5:A1:A1:AB:11:A7:08:5B:EB:3A:A4:27
X509v3 Authority Key Identifier:
keyid:1D:2F:09:60:EB:E4:EA:B5:0B:52:A9:5C:5E:09:2B:DD:34:70:CF:BA
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
a6:16:2f:fc:13:67:5e:ce:0e:79:cb:b0:91:52:9b:9e:b5:9f:
e1:fa:7d:78:f4:2a:93:f3:94:62:45:17:87:b9:0a:59:b9:a3:
a9:75:51:ca:f0:04:6c:01:d1:3a:a9:dd:66:7d:27:7b:1e:4f:
48:3a:25:ea:a5:01:32:fc:87:4b:08:da:f8:f5:62:88:e8:b9:
94:c7:cb:ee:33:08:ab:2f:52:f4:4a:14:4f:ac:2d:a2:f8:de:
c9:6f:95:b7:91:23:b9:ec:95:90:de:86:21:f5:6f:1b:cf:13:
47:77:78:dd:7a:16:e9:8b:cc:df:3d:45:8a:76:af:15:d1:9a:
37:a2
-----BEGIN CERTIFICATE-----
MIICizCCAfSgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxEDAOBgNVBAMTB3Jvb3QtY2EwIBcNMTQxMDE2MDMyNzA5
WhgPMjExNDA5MjIwMzI3MDlaMGQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxp
Zm9ybmlhMRUwEwYDVQQHEwxEZWZhdWx0IENpdHkxEjAQBgNVBAoTCU9wZW5SZXN0
eTEVMBMGA1UEAxMMc2lnbmluZy1jYS0xMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQCgPhpPbLk9qw8C3tqCku6iaYiA7fK2mLzG7tNHgkrn039VaFxtnqq6WeNb
fzJPeURKTxPkLj8fmBCkctXw50SO1Ke5+1S+tvr33Jwpk9SfoVsYbmiTkRuMoE8C
Uumd6Jjz/WfaeEtP2C2Qg1wL5f5IJ+Tsu5kmBo40/pPk0vyXVwIDAQABo1AwTjAd
BgNVHQ4EFgQU0jBxVlCmvCHFoaGrEacIW+s6pCcwHwYDVR0jBBgwFoAUHS8JYOvk
6rULUqlcXgkr3TRwz7owDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCm
Fi/8E2dezg55y7CRUpuetZ/h+n149CqT85RiRReHuQpZuaOpdVHK8ARsAdE6qd1m
fSd7Hk9IOiXqpQEy/IdLCNr49WKI6LmUx8vuMwirL1L0ShRPrC2i+N7Jb5W3kSO5
7JWQ3oYh9W8bzxNHd3jdehbpi8zfPUWKdq8V0Zo3og==
-----END CERTIFICATE-----

Binary file not shown.

69
t/cert/ocsp/test-com.crt Normal file
View File

@ -0,0 +1,69 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4 (0x4)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=test.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:c7:bd:50:99:71:46:af:93:22:85:ab:74:8b:5b:
19:74:af:3e:ad:d2:e1:17:3e:cb:5b:36:9c:8a:38:
bd:1b:47:2d:8b:92:55:1d:fe:a6:72:92:78:00:de:
30:cb:a3:10:b5:92:aa:b8:e0:7b:44:9a:f5:99:89:
36:f4:84:20:81:e3:5c:76:00:9d:76:e7:b9:41:ab:
74:b6:14:9f:b2:94:b3:b6:48:a8:92:dc:09:e3:3d:
04:e3:5f:0f:5b:50:ad:0c:59:3a:88:06:39:2d:34:
a6:52:2f:58:6f:53:1b:df:9f:98:ea:82:8d:52:60:
b1:ef:6b:e9:f5:ad:29:87:45
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
67:DF:28:25:D1:F8:83:36:28:EE:DB:41:63:E4:E0:3A:32:0D:EA:30
X509v3 Authority Key Identifier:
keyid:B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
DirName:/C=US/ST=California/L=Default City/O=OpenResty/CN=signing-ca-1
serial:03
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888/ocsp?foo=1
Signature Algorithm: sha1WithRSAEncryption
37:29:3f:ed:d9:47:9a:51:36:a3:5b:00:85:66:de:51:4d:48:
2d:f8:bc:f1:5e:b4:fd:30:48:f0:25:ee:77:57:9c:f1:4b:0a:
4f:7e:96:1a:f8:48:76:23:46:8d:d6:f2:5e:1e:08:52:12:53:
08:07:9f:75:db:77:22:2e:7e:89:c2:2c:66:85:6b:df:e9:77:
ca:23:6d:9a:af:87:8a:8c:27:37:1e:9e:55:92:8e:8a:a9:93:
24:41:a8:96:01:c0:65:93:8e:3d:7a:6c:bf:ed:c8:2a:f8:26:
cc:00:17:b7:27:ca:85:6c:2e:d5:2a:0a:8d:f3:88:e8:26:48:
e3:e8
-----BEGIN CERTIFICATE-----
MIIDaTCCAtKgAwIBAgIBBDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMjAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MREwDwYDVQQDEwh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEAx71QmXFGr5Mihat0i1sZdK8+rdLhFz7LWzaciji9G0cti5JVHf6mcpJ4
AN4wy6MQtZKquOB7RJr1mYk29IQggeNcdgCddue5Qat0thSfspSztkioktwJ4z0E
418PW1CtDFk6iAY5LTSmUi9Yb1Mb35+Y6oKNUmCx72vp9a0ph0UCAwEAAaOCASsw
ggEnMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVk
IENlcnRpZmljYXRlMB0GA1UdDgQWBBRn3ygl0fiDNiju20Fj5OA6Mg3qMDCBjgYD
VR0jBIGGMIGDgBSzC/V9URZRfig3w6IPHS8QwFGjs6FopGYwZDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTES
MBAGA1UEChMJT3BlblJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTGCAQMwPAYI
KwYBBQUHAQEEMDAuMCwGCCsGAQUFBzABhiBodHRwOi8vMTI3LjAuMC4xOjg4ODgv
b2NzcD9mb289MTANBgkqhkiG9w0BAQUFAAOBgQA3KT/t2UeaUTajWwCFZt5RTUgt
+LzxXrT9MEjwJe53V5zxSwpPfpYa+Eh2I0aN1vJeHghSElMIB59123ciLn6Jwixm
hWvf6XfKI22ar4eKjCc3Hp5Vko6KqZMkQaiWAcBlk449emy/7cgq+CbMABe3J8qF
bC7VKgqN84joJkjj6A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,183 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4 (0x4)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=test.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:c7:bd:50:99:71:46:af:93:22:85:ab:74:8b:5b:
19:74:af:3e:ad:d2:e1:17:3e:cb:5b:36:9c:8a:38:
bd:1b:47:2d:8b:92:55:1d:fe:a6:72:92:78:00:de:
30:cb:a3:10:b5:92:aa:b8:e0:7b:44:9a:f5:99:89:
36:f4:84:20:81:e3:5c:76:00:9d:76:e7:b9:41:ab:
74:b6:14:9f:b2:94:b3:b6:48:a8:92:dc:09:e3:3d:
04:e3:5f:0f:5b:50:ad:0c:59:3a:88:06:39:2d:34:
a6:52:2f:58:6f:53:1b:df:9f:98:ea:82:8d:52:60:
b1:ef:6b:e9:f5:ad:29:87:45
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
67:DF:28:25:D1:F8:83:36:28:EE:DB:41:63:E4:E0:3A:32:0D:EA:30
X509v3 Authority Key Identifier:
keyid:B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
DirName:/C=US/ST=California/L=Default City/O=OpenResty/CN=signing-ca-1
serial:03
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888/ocsp?foo=1
Signature Algorithm: sha1WithRSAEncryption
37:29:3f:ed:d9:47:9a:51:36:a3:5b:00:85:66:de:51:4d:48:
2d:f8:bc:f1:5e:b4:fd:30:48:f0:25:ee:77:57:9c:f1:4b:0a:
4f:7e:96:1a:f8:48:76:23:46:8d:d6:f2:5e:1e:08:52:12:53:
08:07:9f:75:db:77:22:2e:7e:89:c2:2c:66:85:6b:df:e9:77:
ca:23:6d:9a:af:87:8a:8c:27:37:1e:9e:55:92:8e:8a:a9:93:
24:41:a8:96:01:c0:65:93:8e:3d:7a:6c:bf:ed:c8:2a:f8:26:
cc:00:17:b7:27:ca:85:6c:2e:d5:2a:0a:8d:f3:88:e8:26:48:
e3:e8
-----BEGIN CERTIFICATE-----
MIIDaTCCAtKgAwIBAgIBBDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMjAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MREwDwYDVQQDEwh0ZXN0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEAx71QmXFGr5Mihat0i1sZdK8+rdLhFz7LWzaciji9G0cti5JVHf6mcpJ4
AN4wy6MQtZKquOB7RJr1mYk29IQggeNcdgCddue5Qat0thSfspSztkioktwJ4z0E
418PW1CtDFk6iAY5LTSmUi9Yb1Mb35+Y6oKNUmCx72vp9a0ph0UCAwEAAaOCASsw
ggEnMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVk
IENlcnRpZmljYXRlMB0GA1UdDgQWBBRn3ygl0fiDNiju20Fj5OA6Mg3qMDCBjgYD
VR0jBIGGMIGDgBSzC/V9URZRfig3w6IPHS8QwFGjs6FopGYwZDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTES
MBAGA1UEChMJT3BlblJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTGCAQMwPAYI
KwYBBQUHAQEEMDAuMCwGCCsGAQUFBzABhiBodHRwOi8vMTI3LjAuMC4xOjg4ODgv
b2NzcD9mb289MTANBgkqhkiG9w0BAQUFAAOBgQA3KT/t2UeaUTajWwCFZt5RTUgt
+LzxXrT9MEjwJe53V5zxSwpPfpYa+Eh2I0aN1vJeHghSElMIB59123ciLn6Jwixm
hWvf6XfKI22ar4eKjCc3Hp5Vko6KqZMkQaiWAcBlk449emy/7cgq+CbMABe3J8qF
bC7VKgqN84joJkjj6A==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=root-ca
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:a0:3e:1a:4f:6c:b9:3d:ab:0f:02:de:da:82:92:
ee:a2:69:88:80:ed:f2:b6:98:bc:c6:ee:d3:47:82:
4a:e7:d3:7f:55:68:5c:6d:9e:aa:ba:59:e3:5b:7f:
32:4f:79:44:4a:4f:13:e4:2e:3f:1f:98:10:a4:72:
d5:f0:e7:44:8e:d4:a7:b9:fb:54:be:b6:fa:f7:dc:
9c:29:93:d4:9f:a1:5b:18:6e:68:93:91:1b:8c:a0:
4f:02:52:e9:9d:e8:98:f3:fd:67:da:78:4b:4f:d8:
2d:90:83:5c:0b:e5:fe:48:27:e4:ec:bb:99:26:06:
8e:34:fe:93:e4:d2:fc:97:57
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D2:30:71:56:50:A6:BC:21:C5:A1:A1:AB:11:A7:08:5B:EB:3A:A4:27
X509v3 Authority Key Identifier:
keyid:1D:2F:09:60:EB:E4:EA:B5:0B:52:A9:5C:5E:09:2B:DD:34:70:CF:BA
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
a6:16:2f:fc:13:67:5e:ce:0e:79:cb:b0:91:52:9b:9e:b5:9f:
e1:fa:7d:78:f4:2a:93:f3:94:62:45:17:87:b9:0a:59:b9:a3:
a9:75:51:ca:f0:04:6c:01:d1:3a:a9:dd:66:7d:27:7b:1e:4f:
48:3a:25:ea:a5:01:32:fc:87:4b:08:da:f8:f5:62:88:e8:b9:
94:c7:cb:ee:33:08:ab:2f:52:f4:4a:14:4f:ac:2d:a2:f8:de:
c9:6f:95:b7:91:23:b9:ec:95:90:de:86:21:f5:6f:1b:cf:13:
47:77:78:dd:7a:16:e9:8b:cc:df:3d:45:8a:76:af:15:d1:9a:
37:a2
-----BEGIN CERTIFICATE-----
MIICizCCAfSgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxEDAOBgNVBAMTB3Jvb3QtY2EwIBcNMTQxMDE2MDMyNzA5
WhgPMjExNDA5MjIwMzI3MDlaMGQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxp
Zm9ybmlhMRUwEwYDVQQHEwxEZWZhdWx0IENpdHkxEjAQBgNVBAoTCU9wZW5SZXN0
eTEVMBMGA1UEAxMMc2lnbmluZy1jYS0xMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQCgPhpPbLk9qw8C3tqCku6iaYiA7fK2mLzG7tNHgkrn039VaFxtnqq6WeNb
fzJPeURKTxPkLj8fmBCkctXw50SO1Ke5+1S+tvr33Jwpk9SfoVsYbmiTkRuMoE8C
Uumd6Jjz/WfaeEtP2C2Qg1wL5f5IJ+Tsu5kmBo40/pPk0vyXVwIDAQABo1AwTjAd
BgNVHQ4EFgQU0jBxVlCmvCHFoaGrEacIW+s6pCcwHwYDVR0jBBgwFoAUHS8JYOvk
6rULUqlcXgkr3TRwz7owDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCm
Fi/8E2dezg55y7CRUpuetZ/h+n149CqT85RiRReHuQpZuaOpdVHK8ARsAdE6qd1m
fSd7Hk9IOiXqpQEy/IdLCNr49WKI6LmUx8vuMwirL1L0ShRPrC2i+N7Jb5W3kSO5
7JWQ3oYh9W8bzxNHd3jdehbpi8zfPUWKdq8V0Zo3og==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-1
Validity
Not Before: Oct 16 03:27:09 2014 GMT
Not After : Sep 22 03:27:09 2114 GMT
Subject: C=US, ST=California, L=Default City, O=OpenResty, CN=signing-ca-2
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:d3:24:1c:92:a5:bb:00:d9:b1:fb:2b:1d:7a:32:
a1:6c:49:eb:3c:2d:29:80:d6:65:8b:17:3a:f0:4b:
dc:0c:57:fb:d5:31:68:a5:e4:54:86:55:f9:1b:a8:
d7:7d:32:01:3b:cf:5c:38:2b:f5:bc:d3:8b:c8:b6:
ab:76:65:32:e6:4b:d5:e4:fd:d1:92:c8:33:6a:74:
f3:c7:ec:97:c3:c7:9f:e4:d5:55:75:b8:bd:39:ec:
2d:1f:c6:54:c8:2b:2d:17:e0:05:77:28:44:f7:dd:
e1:6e:f0:59:05:51:f5:b9:b4:fe:be:ad:40:a6:d5:
9a:c1:64:e0:9b:dd:67:e5:f1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
B3:0B:F5:7D:51:16:51:7E:28:37:C3:A2:0F:1D:2F:10:C0:51:A3:B3
X509v3 Authority Key Identifier:
keyid:D2:30:71:56:50:A6:BC:21:C5:A1:A1:AB:11:A7:08:5B:EB:3A:A4:27
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
0c:61:c0:c7:11:c2:f0:39:f0:76:9d:4f:43:d4:90:54:1f:26:
3d:54:3d:77:5f:c0:b3:4a:c2:1b:b6:18:d2:12:8d:24:4d:76:
f5:07:0b:14:3e:17:2d:42:ee:85:30:db:e3:4d:81:67:59:97:
0a:b3:bb:c5:27:ea:69:c6:ee:99:5c:44:36:53:3e:c4:47:68:
f8:fe:c6:53:38:fb:e7:9a:0c:3c:6c:78:93:29:d2:49:7d:29:
d0:61:6e:81:9b:d6:ec:1a:e2:3e:62:62:41:bc:6d:4d:33:91:
76:20:5e:32:70:08:3e:24:72:fe:b1:8a:83:57:04:19:b5:cb:
99:b7
-----BEGIN CERTIFICATE-----
MIICkDCCAfmgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEVMBMGA1UEBxMMRGVmYXVsdCBDaXR5MRIwEAYD
VQQKEwlPcGVuUmVzdHkxFTATBgNVBAMTDHNpZ25pbmctY2EtMTAgFw0xNDEwMTYw
MzI3MDlaGA8yMTE0MDkyMjAzMjcwOVowZDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFTATBgNVBAcTDERlZmF1bHQgQ2l0eTESMBAGA1UEChMJT3Bl
blJlc3R5MRUwEwYDVQQDEwxzaWduaW5nLWNhLTIwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBANMkHJKluwDZsfsrHXoyoWxJ6zwtKYDWZYsXOvBL3AxX+9UxaKXk
VIZV+Ruo130yATvPXDgr9bzTi8i2q3ZlMuZL1eT90ZLIM2p088fsl8PHn+TVVXW4
vTnsLR/GVMgrLRfgBXcoRPfd4W7wWQVR9bm0/r6tQKbVmsFk4JvdZ+XxAgMBAAGj
UDBOMB0GA1UdDgQWBBSzC/V9URZRfig3w6IPHS8QwFGjszAfBgNVHSMEGDAWgBTS
MHFWUKa8IcWhoasRpwhb6zqkJzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
A4GBAAxhwMcRwvA58HadT0PUkFQfJj1UPXdfwLNKwhu2GNISjSRNdvUHCxQ+Fy1C
7oUw2+NNgWdZlwqzu8Un6mnG7plcRDZTPsRHaPj+xlM4++eaDDxseJMp0kl9KdBh
boGb1uwa4j5iYkG8bU0zkXYgXjJwCD4kcv6xioNXBBm1y5m3
-----END CERTIFICATE-----

35
t/cert/test.crt Normal file
View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGETCCA/mgAwIBAgIUE3pqyVuRQL+qGuSFAUCLq4g7pt4wDQYJKoZIhvcNAQEL
BQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
DA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9w
ZW5SZXN0eTERMA8GA1UEAwwIdGVzdC5jb20xIDAeBgkqhkiG9w0BCQEWEWFnZW50
emhAZ21haWwuY29tMB4XDTIyMDUyOTA2MTk1N1oXDTMyMDUyNjA2MTk1N1owgZcx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4g
RnJhbmNpc2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9wZW5SZXN0
eTERMA8GA1UEAwwIdGVzdC5jb20xIDAeBgkqhkiG9w0BCQEWEWFnZW50emhAZ21h
aWwuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyLzMbnMBcxYB
2W0uEqPKo2lOJdUQTnakipVLqRvZIJv7NkZgU76pxdFwoSxPpvJcpJ4rsosBZvhV
dkGoKmuVfIFU0lYcdaccq88aT7E9XfTXiiyB2tkT6jS6wr+QxDj7KW47zdUBUT9O
6ClNyY2o1gZldElTG0Bwk4j2sAkXuWGmyncTOJ4ge3mWVksAQYbL5pwfdfyqgDmK
B4nLJHBkorLbF7nm7pK2HzQCtaEUJpQKpJdCULcOHrydjVAwHUQsZAb9XXjQWPTb
A0BSplbgMSI6saT9uA2RjLBzpYKj8J1rWGadWteSyQAf6XooQrquTPuR+OWF6t/m
2vkTcJlh1ukPPAPZBvlAQX9VSLWk5fmAQZA5BxYXNVWcMGVNO7UtilRmjqK1nCmv
oyDXHzpE5RZPBZH4ecOqTscUgmS72ItPGWMtEtCQYbzWyMAa1cpCvK40YRa4814r
XgffWgWJQfqyVvRjzpWIUiqwjUX3/p3W3pxX/GNHOv2ZH/pebcODOl7EFxzv5eQc
z9vW5+RfiCSzs5bGG7qw58dMFROeAJbwR6t2o6GRd0HfgTTwSDcrrpcdLP/PaL+v
twnrNa9r7rIwXnDWxYo3KiGpqEfG2WwW+lJsUzZOi9eI9kYPyvFmNFLugZbHMi+h
ICCb8AQB2thON5X4N7FtP5GVfMR9vIkCAwEAAaNTMFEwHQYDVR0OBBYEFDEy866N
WPHPTJKeL/96VYoczkINMB8GA1UdIwQYMBaAFDEy866NWPHPTJKeL/96VYoczkIN
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAHJgyS6/OyRqzqwd
+6DnntGV+MTo9WBNvNs/fekJghnrr85oG330GasYENQLi0wF20to7FMan2U9kwgV
cbhDwe70JD0jg8htYof/uXOMBVWT4iZ+eXn60mP3iLsSutwt/drXBBzbxMYbUC12
CIiadkV8aMPIN6oGnF7TLF4AvBqYYp2qAVGXr/ZQm3L7NPB0jkSktSe6obnaq1tO
ug18ImhzAqkn1UGnLRiTADOba5HuKtovwWtLblNBODdnv1E7IK1A6jpqwiYjlbU5
4v9ZzFzEJw+GqYHkTRmJGCA2Uw7HNEUFeno1BTp19Ce9fvrkofTWYmLp/NAvEp6E
aFnBdCjY4tWzI2Iig7IjDIM7F6igGODybeN9ijD7oSyEDtNE7ECLSuXhgekiQJ8c
NYALgbbNPxWx8zJcNiYy1NDYdIjO5vYFOpbn+rQObKVCX/X+Al9fT126ciTT1xAF
fZtkGPpp3Wjgws9UDSzetvWHYt0Rs70m351LFPHyR4tQLHoDFqnA2buG/mSvKZ9U
to0JQ/8QPRIYv0FUJcF0+/xQRYrIqvmjiCpfL2hwJyRViq7f8z0/tmMoLxFlPo/k
GC+gvh3WwTB622h9JEqS48lllcYZIQWOX2mbFtUtNFdzUtSRmZMa8RmcYr5So2OH
9QLIIjC6ntPFjWq+uLJAJ8uazRt7
-----END CERTIFICATE-----

BIN
t/cert/test.crt.der Normal file

Binary file not shown.

52
t/cert/test.key Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDIvMxucwFzFgHZ
bS4So8qjaU4l1RBOdqSKlUupG9kgm/s2RmBTvqnF0XChLE+m8lykniuyiwFm+FV2
Qagqa5V8gVTSVhx1pxyrzxpPsT1d9NeKLIHa2RPqNLrCv5DEOPspbjvN1QFRP07o
KU3JjajWBmV0SVMbQHCTiPawCRe5YabKdxM4niB7eZZWSwBBhsvmnB91/KqAOYoH
icskcGSistsXuebukrYfNAK1oRQmlAqkl0JQtw4evJ2NUDAdRCxkBv1deNBY9NsD
QFKmVuAxIjqxpP24DZGMsHOlgqPwnWtYZp1a15LJAB/peihCuq5M+5H45YXq3+ba
+RNwmWHW6Q88A9kG+UBBf1VItaTl+YBBkDkHFhc1VZwwZU07tS2KVGaOorWcKa+j
INcfOkTlFk8Fkfh5w6pOxxSCZLvYi08ZYy0S0JBhvNbIwBrVykK8rjRhFrjzXite
B99aBYlB+rJW9GPOlYhSKrCNRff+ndbenFf8Y0c6/Zkf+l5tw4M6XsQXHO/l5BzP
29bn5F+IJLOzlsYburDnx0wVE54AlvBHq3ajoZF3Qd+BNPBINyuulx0s/89ov6+3
Ces1r2vusjBecNbFijcqIamoR8bZbBb6UmxTNk6L14j2Rg/K8WY0Uu6BlscyL6Eg
IJvwBAHa2E43lfg3sW0/kZV8xH28iQIDAQABAoICABwZgax8YNmRXRTomah2USlq
1kupdazmIsZbe8niYhSUgSfp1hYi/HT6in+lSkkeaCWLFqbZmoqlfKEfM8EsajKR
kCQZdcZqbDMIvLAnKWX7nihzboIKHSWN2A7m7gbpyw7TpX98r8CF0i/hiEgMknPT
VWRf10hbTub4J0AhJbcHmmeBH6mvSPC/5nGR8ik6C1TuyeCkS+HDLDU97rfdG9lC
nDTICzGeS+w2RaLTN5Tm6E599gSCe3GGCa/8Z5/RKT2fVNw+yzuImxfrayZpxtxZ
5El1xSZ8j8FX+fhTP0uxXZN0WdabkqqcX9s5BGXC6B9Sn+5tgr+MNC626ye58N36
vhASbLhnZxZ1MxbVNdvQjl9A5mj/Tv/TM/syjFkElFwXBFz5MXgYRTu5TnoJpDyp
wMqNodTSbe8UuULtBIHYyuLEsrPWjne/ape8CqSQKgfnI91tveAMlAQA+yGRufSw
fx1gDrBIk3EtDGuelAIiW1ZimZoTj51HW7Mpfq9PXbuVO7i+zzSDFP/zyto2hITq
UeiTwKYpj8AgF39HiudAyJVYKDjKgATeSN1ziTvXBvBxe4JJ+7wovjl+R5ClBUEk
cNrn3FNVrgPjmJ6X7+42U2upQ2WIo8dT6PuE408nh2FR/VuabEqqwmmDEUW19+U+
yZWfhDMv6Y/l6f75UBHhAoIBAQD++61+BBEwqTPfGH5EUxxeeaXXto8zW/N3aBql
f77iI3/XuOhve4WfL8v5xAXPfPIBGGBlG5RQ0Quxoo5fE6RPKt+5vaaOpMhLVf4L
sAQfLM9M9JK84BbWI+Z1o2s1knxlTlK0GZ/pbqg6J1YFfFAL/3PKi98EAGw9Dpj9
u3GnSjF/dbtwwUiTS0Rv/8FV4bFxsSoul+N3xeyi26UWG9WIvuAzuLJfvqV2fb/l
o3LKpeXyD+SFatRmPe0JWIi2o0ZQpF8hhJXg1UYo2RGWf/bbl3SnivIMb/hNs8LB
17DNKu1DvgZoJSyocFmKAShZvbbirQYRtHo7SI2hAaOt6T81AoIBAQDJib1HIp/V
N59IIFOru6+kc9Vkhm8FDf3iyQfIL9Y0uVI4WrdKUeIH+pEKU7CMCT0WSgCxENe2
TT+pOF3/T398eEmGAr79CtgtSzzuDvBlpD2aEfhjBPUE7g6tNuUFjgFJmzdXHT7u
99mWag4l30hfIAFXLu9ofm05nw0CRxlotYUiTCBU892ZxpO2/nDNJUjAU1MOvSa7
MM7VbETui9teHgjZa+AadHZs2OqnWVo+g3RcKl3PyAdcOWobVQknRrpN9SlQkf/V
GYRhY2tQCpstKrEDQPWsNGmwjmT3uPdK8t5SXZZBji282XtCa+C+tB34AZ8YuU/Z
BH+Akez5zw6FAoIBAQCLK17kGuAvCQsQx1OTgzFGt2q3NCMwyw01rRJuJi1PTETo
vznOL0MdQX85Ua5CM1X7Fwz14nmvKooRaEIAzr2toB8AR+zyiinwRH0mb+mwAksb
G5pDkKOmOW394zYOxWcz++3T8vB+/jC/nNysnc8q3UCb2n/ctUZehOsoAfjkb/BY
OzAVOMmd60TtRFCHyWmKPkJhr/EtXE/uC6gtSv/fZR8F29cvvuScqcHlWrK6vJWm
6tm1oDtRmpcXtMTZuoAUX8K0jqMnVgC3JtMcq7dW33GCSKoX870429Z+6nTLZpSd
lsf0a+XWAYw9cKhPYubBDeL0IudcGBuFN1nZACfJAoIBABpg/vdKnuUHjL+iC5GU
1V6PEsU/m1RsCmkeqvgW2tC32P0rUoZVxWIJ9+YEIj2SD/7U3NZQQAvKfKSnjhYW
z7b4/5ac0WbJfpYfHPCD4A9Nugpqg7piMbfdeOpPHxblCWIbANlUKKKaqk43v3ZR
jWV2CPbiW8+vjJhYKxm7OKYt7CkbEbhM2xp/lWIEV7tiP+18eoiZVXJ25vukWjlm
8OWWxM3Aguqzh7Sjh8MzvM4l4psVqIXDxsLZePvu223aohQGHMxA791yo5Mjsi4d
1UXKKrUkUYOisJq9aJXMDgIvW84oFbyq4W2wgaOl/xq29J07iRlxV/Qt1Ip9jyj7
YwkCggEBANFod5wwlOzTyAlRdZAPvyBWa0i+SLWecwr93rGQF3UuZwzaviY7U0tp
BoeFZAxiwns3SM6SJYIhce8Ku0PO1rp4FXvOopnIVO9BFXk1E5YUholQHHbERsse
pQahtlZ26ZAyRQjMQI9KKmU6pNFiF87MCnsz70BQajKo2+5NfZpnjaQzT6uyJeRQ
Iy38QlOk0221UsUU/bjahDlxmuHVdcnl6gi+4SYrI45wJSiBTiCJrVmxScg2D3IA
EZ3pVKOam0a3Mroqe0uMdY892cIYSAKOrZcxg5ZXWKDwlcsXgVuGDSvrU8LUPqn1
jxLWsmKbkVA5hFln8GPxs6EqfqOJhkM=
-----END PRIVATE KEY-----

BIN
t/cert/test.key.der Normal file

Binary file not shown.

34
t/cert/test2.crt Normal file
View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF7zCCA9egAwIBAgIUBwRM0hlOTRGtIMvLy56P3TZc6jIwDQYJKoZIhvcNAQEL
BQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
DA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAMMCXRl
c3QyLmNvbTEiMCAGCSqGSIb3DQEJARYTb3BlbnJlc3R5QGdtYWlsLmNvbTAeFw0y
MjA1MjkwNjEzMjVaFw0zMjA1MjYwNjEzMjVaMIGGMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAGA1UE
CgwJT3BlblJlc3R5MRIwEAYDVQQDDAl0ZXN0Mi5jb20xIjAgBgkqhkiG9w0BCQEW
E29wZW5yZXN0eUBnbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQDCOlzMo5qPtb5wjXHqy1mjm2rMAPeiz0wrDRXC0+9l/f/6KYBfJoaAuLau
P6kCojDNLOOHAjmbjeoG+bVGoLVNAGhFaThKQepmBPFhjtT0+7syGq6H12Wb9mmn
hMy65gS+7dsUU74HUjlHuYyOnXHmrJH/7sGzG6DXX6t2x0Ts49gVWzFr1TINMjP0
opupvpokbr+EEzIP4NE5isPQnMeIt2HhzTMN+wHBalnHIVq0v03M911l4XpzHtP+
MetDMraHJdv8Hlh5qeyrDkn7N5zE05cnSu6AK3vMaxsDbRJ92Akv6bLMqTqSnR3j
16Qwqs369cU+zl7wQJ7+J1Gh5DhOe9Vns+bLbBJC5XZz3qcFxUXXRResuBYPQNht
JwgT7wq+XNMkd7TzDQzfWx4G7zz+3ogiNK6k02e0Uafuizi4nyMpwuu9SwvfYan3
QBL7ddjuMGUHG9oOepLmiKt8ST/plduc+67oXu1oeGnaEthpQ9Aong0/JhDvvnsX
Pe4tZQwzFK1m/fxF034l3fy+gmGfnRC4k2thUotfp+BReDAZ5Wwg6FO7eaowuQbH
Qk8Pt36yd0fWwAgX3kmhxC91QOYt29Aya4gTuJT4ajueDVRzB3bCxX0t7Awz/Ohh
36XiLHZp7Zo1eJtG7oOXRJdiKCglxOk9WiXTIaDXunXDGRl+DQIDAQABo1MwUTAd
BgNVHQ4EFgQUwIug2OC092u/dZFClmac5Sbo4ukwHwYDVR0jBBgwFoAUwIug2OC0
92u/dZFClmac5Sbo4ukwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AgEAhnWpbQ31DbMzbSiY7iKM8OsUAqPzjvGObB6hzF+i8/q0GfQm11V+aKFTWu3p
5aJua0hCPxq3F6q3Wj+F42pcRkoOmcGMMkGVOFeT1TRaC1WwTvhYjwFke4jqOi+5
kcfLWO3bkADm73AuW/PJ6WgQzrfOvd+uvVX6B0NKumgNYrhyDfwb4tZ/fOLHSA9W
NNd/93yo2OhH0vOMbAcDGdOpBR7Sx7c+BeI57kORIlSwCQmaSeIIOLs69VsMH/Yo
DiNWfbj7pPRhdCSEmd59wyPKUcBJHfEE9z54NIb6ETTKrmtwYLPNKdgW6dqHe8UK
bsjXiiw0Y3nHhrv7Fh+aqy470mn890K0+CPTh9Gi+Qwkfyt7tJr4uCdDzLPoP6up
FlQaobMtOFuCMNZs+B1UZm2jcj5j/PjY19PZu8XAHOFJmY66oqK6FvZHrq/26K29
p6BXPMUVhxFoXUX2EmAtey919+GY9KncMuhobn+5If/6mug3QRNNt/csw30NvBOZ
6Lqc920e/lgoFPQ7RnxI5PLNdTFLTqM8c9lMt6rqcff7cK9aonfWPd3s1pyWTFhx
tSBH95qNcu+kgu+iVLeFZjqyTW5lLyqZLPjnx8cS4HZtAUeM73RjFcDVokqGxAgc
qzatPoPNGjLtG1JoMsXjaMuc0ed3BkjXpXH24z8o0g7ppgQ=
-----END CERTIFICATE-----

52
t/cert/test2.key Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDCOlzMo5qPtb5w
jXHqy1mjm2rMAPeiz0wrDRXC0+9l/f/6KYBfJoaAuLauP6kCojDNLOOHAjmbjeoG
+bVGoLVNAGhFaThKQepmBPFhjtT0+7syGq6H12Wb9mmnhMy65gS+7dsUU74HUjlH
uYyOnXHmrJH/7sGzG6DXX6t2x0Ts49gVWzFr1TINMjP0opupvpokbr+EEzIP4NE5
isPQnMeIt2HhzTMN+wHBalnHIVq0v03M911l4XpzHtP+MetDMraHJdv8Hlh5qeyr
Dkn7N5zE05cnSu6AK3vMaxsDbRJ92Akv6bLMqTqSnR3j16Qwqs369cU+zl7wQJ7+
J1Gh5DhOe9Vns+bLbBJC5XZz3qcFxUXXRResuBYPQNhtJwgT7wq+XNMkd7TzDQzf
Wx4G7zz+3ogiNK6k02e0Uafuizi4nyMpwuu9SwvfYan3QBL7ddjuMGUHG9oOepLm
iKt8ST/plduc+67oXu1oeGnaEthpQ9Aong0/JhDvvnsXPe4tZQwzFK1m/fxF034l
3fy+gmGfnRC4k2thUotfp+BReDAZ5Wwg6FO7eaowuQbHQk8Pt36yd0fWwAgX3kmh
xC91QOYt29Aya4gTuJT4ajueDVRzB3bCxX0t7Awz/Ohh36XiLHZp7Zo1eJtG7oOX
RJdiKCglxOk9WiXTIaDXunXDGRl+DQIDAQABAoICAF1s/V5iA+LEpUo2f2lVT1Tn
WUOv424pEJZUwPbqQuloeEy40crzGRepwhWnAYEGyzqmMPusElHEvaGFU7EZdJwJ
ah6ZMj5l3n4cOo+WyNDelXjQZMtqzLFsof22X1Q3eGjuuMbaUTnsTyk1E2s2SKeQ
stnM2tdwnrl6h1unFnxsTHBZYThhYKYAVAWEgiPwTuzfaLf7E8WXeoyXNwGNqF0W
+LpInhpHdty2b2Ddpmfy7VV+Vyq+fswJNCe9k1DLgAkaKOrpHd3H4tOVF/kDEyGK
NdbssDRutinqCyBmwtJNrH2jiCHx9P65HLuP6qaOQm5I8gv38KrYKqJN/3JD+ODQ
dn8yVXjcCRzq8vylFLJfVEY0hB3ZWJAgDxY8GFax7foyaJ6889qgMOU4xBdotF7c
hKNsH+ClHSuJ5h4SNAV7/mRcJrYDFJ9imuY4PGFMZMQYSZig5dLawTe52+wsSuZ4
kYxDXk76ZLr/MsbijrNOdze3Mny6EPxJGlv04XNNfIrsN9PXQGLpUwaJSJMGnFTI
TgAlcLATVMLusU2xf/Ra8eCk8Xydk0ExdB4TnMD0TTM/6bPJYIwwqrjee1ao4V0E
eISaoC8QHDi1XIFav2amzeyfHQI8uV4OQwV1rVLgeBY8TtQ0Mk6oU3AZ9z8Hvb1V
szdpOicXdI2lC9qpFiIBAoIBAQDxYMCvRDTkYcebG2sSi7ViUd3NWuBYFjlNfHED
/Rz6mUDud9r6TC3nqCPuugGt9P7fz3KktCoSh/x6iYsUP7iSK2V6Gg/kD1wtjIdY
9OW8u2cHyyGPshgZBgFJ4jE1wvzHQApQYrbdLdSmlwnvXT+tEG57vEa1ep2DSNaJ
hXlvpPMJAghNtc0wvcTZ8Aas8HfuEthKrb22O2zo0J2QObBZv2gcA0djW0ZbEEGg
BVNzmfaZFrLqSIP+7SD9KTgyRz1pAmeIgv//szYweHNVGeVbPgG2W4HiaV7c4+aF
3dmaikSZgz6Qily74HcVMrTzcgw2v2uM3IkTvWEX1PrixPdhAoIBAQDN/mqXtqtO
mbQBbn6+QgnVNRc/lnLv2QnzfuK93omZGFrl+COaCdM+T93PQkTiEydCjOYgwv3x
6KVMyrZLVKuYIHcdwP7z+Xi3wDQkCO0D8JmJGFBU+tAUJHEgF7DxEDa+PGxEPXWe
Ta4ZXpqQH96bssfzyFB2+PQcSIcRyWdnhJyVgXdRfMxMbYxH6g5X1bIGOAzCF1hp
b9QN3Rc/2UKCfw3IoFl1Yzo8yXi9hLhkIb2tjpBmt9V6itgVHL12HLLgvzs4egkI
aBHtMIxWcrzq+XtG3ARgugkRlhLhDR7nZOV2nFYXFNJ2tHfg2q4PBkrFm5EPLpXj
shzOZzlok0ItAoIBAGVo7ssz7rzcz828sTlzdNs/5d33BjY78As78wdn+hrW9E68
EEYEZ2ziWWZcw4PgYIyLTXEhOGPcuhiJXOM++j5++P1Zob+BLIr+dYbMLREj8t91
Wj5S5ojs2vG9lEswBp93ql2ne4hlTuhGoKfuF2iQdLqLmXF5eF/F5EyWxTRAqtLo
BbEQZorJz8B4dXWG+fwN8s09PW0oflM6AlirxAmQvx61yfJWULLFm6ytUChLUS10
OerwWHRsD+YMwU1uXMCXgAP8da1qD6RBlsL0REYXQQTqYSFMf8xx+1HWqSuiV7vf
RhPX7aoJXj6LtTZqffMqbPcbWIPwlrA6jMVlV0ECggEAHqfDAyrrGXpkQGZKNFQt
lG4fNJZWKn15LqRuZ+UrQv5N4LVpzu2xYy+Nid+J0r8Y8512Td/W3N5LYz8zm8hQ
9QW31FS5XGN+5JGU8NvnMdPndXCJ2+urdaPqteTwrx9DllH1pr4it9lFlH7wr4we
m7siaJQh7WKlKWRdvXbkjZI9nz4yHI1e9ezDmJwrYETsBmLm+ydwP9iljR6e7CCP
9k2kJnw+c+q9avhsoH+U1Un0KVTzBmZLPb5V4+ZwB5jDwhCTZpc6quaZ7FtyNxdC
KBHSl7v8ZsyottZHnvhN5g+s2lbvtOWjYIkA4hSJHLFKBVheYQ0Ev2rtQIY6E+b+
TQKCAQA+jl6f5wFSGBFjnrQu5V6E3tcI6fhkk2b4KGRBLyp27gIdqnw6FCZLsJ+g
qUmJd8lz3E4UR/VXy2lSWcKbmDmO1l+v2fZZu227hbZp8YTsPqt1JhnrroBP3Pdj
d+Zj+ZJpw5MbLb+8Wsx41fsAy8k1kZX82Vigbj2HEE/O7keflwBfa455ue9IGIFY
1SRNNJPB4RZ/9T/NM+MsTJZapsp6Si/nkncluSKeBnE1LfHoJi2eVW77qPIOGOPy
L3iaDoc8MmhMx2EFzSDlzwdB9Jg8cXxF4XxjadYc6JOtzUEkCqR0XS8KOgO2eMU0
uWdAG+HmOwioRij8CIrb/9veL2jl
-----END PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICozCCAgwCCQDEutRdSs3vZjANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMC
Q04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpoZW4xEjAQBgNV
BAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0
LmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5jb20wIBcNMTYw
NDI4MTQ0MzI4WhgPMjE1MTAzMjcxNDQzMjhaMIGUMQswCQYDVQQGEwJDTjESMBAG
A1UECAwJR3Vhbmdkb25nMREwDwYDVQQHDAhTaGVuWmhlbjESMBAGA1UECgwJT3Bl
blJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRlc3QuY29tMSMw
IQYJKoZIhvcNAQkBFhRndWFuZ2xpbmx2QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEA2KZ+HdH9R2tarxD8PKqu5EYq2BNGlFRg1xJmrw0XZBRM
UP/VPb+sIeioooz36uhiXfQjExlpBCA/0zNAN+HbFyqpPPTf1qLGrj/dqeE4MJaN
Bwzxiv3fZnENT65u2qbiFWIY+ATNHgA20d50nxNNjPTzLbkx/nYXL92r4kuAGk0C
AwEAATANBgkqhkiG9w0BAQUFAAOBgQCfMo0qbcs3kwl1tcNBO5hCcUUJRzyv041V
ff/nZ/JPIMo/LSZd12K82G/dLRN7uRT9nzqtm+JRkHALHWWWFKi6bdg1vcdOTWqC
08bCkJHQoXJQQLvvA6gNvnR+0b7L4CrCmrcyYgKDLXVGNP9Wv/PqSWWbxsmqngkA
Mvy6CVytFw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,679ACC8E69ACAA92
Ssrjp3VU4somCNPiXkWqcudDnvnwbyj/Q0pS07at3lXKbhQSgI1Tzhg9Pm3BXXj5
mkLdeGG5ocrj1Q9dhtmZgZeHHQIiynZBhjBu1Y+HPef8jXOWLrCOi8EKiWkJ2qG3
V1KFM/95CcDt0mRLykUXEL3IpUst05SFb9XwiLokB7ypeu3NhgNUHjL6G+ubB4ri
TOUjCW4pEoNHjdC22IiqSncwCVhluYSGhr6ktHKehZMhYIXmL1wmSLdhTlsPXCQl
xvYILQ2vJcKIR1BkeYYPD/OQC6zCZlXIErzfgeZiz2+NTudKYpb9VmsQKsO+R8L7
tZ/fNaR0vk8bbimMHgStAV4acVsC/7WxsqOjMJ8VTq1iqhYPl6N7kRdR3H3kSSOm
cN9T3SrOHDVaHbnWgToaOE4mKFjvFSLIOcWgus0iOHWXmY+SLG+Ndag3oVB6R9oB
cAHX19mq99+GhzA8IV4I0En2UCKQhnGPvkM+9mcCDxhRETlwncDjlMGOHpQ65J9r
eReVPIpnDkvHxPGTtsR3ZHTdWTZb+C0W2N3QIlJKrOzxFmfoj++yG3tMX42aDY0g
DVkrXgcKobiWN0AVrJNAwfG7uObKSCFYgz/0RRMCO4cjXRW99nxdjVDZhyc6R0Te
jzuF04okkOLNb25n2hP+yIULrn+6Nv/uHtFI0j0n3hOzcKh//dNbACSAKgkHni9g
JKDFJXgLJxf+Wc3So0DF9gYMKJJ+WbcdVT9gkC7RyQHlC90Pn7kNXzHr0ZawUsNI
ZxSkL4dMhYAfA4lUBJbOkwbSurv97LinOSRffpM0Nmf7VNw/Ue15eg==
-----END RSA PRIVATE KEY-----

45
t/count.t Normal file
View File

@ -0,0 +1,45 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 3);
#no_diff();
no_long_string();
run_tests();
__DATA__
=== TEST 1: module size
--- config
location = /re {
access_log off;
content_by_lua_block {
local base = require "resty.core.base"
local n = 0
for _, _ in pairs(base) do
n = n + 1
end
ngx.say("base size: ", n)
}
}
--- request
GET /re
--- stap2
global c
probe process("$LIBLUA_PATH").function("rehashtab") {
c++
printf("rehash: %d\n", c)
}
--- response_body
base size: 20
--- no_error_log
[error]

1044
t/ctx.t Normal file

File diff suppressed because it is too large Load Diff

225
t/decode-base64.t Normal file
View File

@ -0,0 +1,225 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 5 - 2);
#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();
__DATA__
=== TEST 1: string
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.decode_base64("aGVsbG8=")
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
hello
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 2: set base64 (nil)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.decode_base64("")
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body eval: "\n"
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 3: set base64 (number)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.decode_base64("My4xNA==")
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
3.14
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 4: set base64 (boolean)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.decode_base64("dHJ1ZQ==")
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
true
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 5: string (buf size just smaller than 4096)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.decode_base64(string.rep("a", 5460))
end
if not s then
ngx.say("bad base64 string")
else
ngx.say(string.len(s))
end
}
}
--- request
GET /base64
--- response_body
4095
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 6: string (buf size just a bit bigger than 4096)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.decode_base64(string.rep("a", 5462))
end
if not s then
ngx.say("bad base64 string")
else
ngx.say(string.len(s))
end
}
}
--- request
GET /base64
--- response_body
4096
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 7: decode_base64url
--- config
location = /t {
content_by_lua_block {
local enc = require("ngx.base64")
local function to_hex(str)
return (str:gsub('.', function(c)
return string.format('%02x', string.byte(c))
end))
end
-- RFC 4648 test vectors
ngx.say("decode_base64url(\"\") = \"", enc.decode_base64url(""), "\"")
ngx.say("decode_base64url(\"Zg\") = \"", enc.decode_base64url("Zg"), "\"")
ngx.say("decode_base64url(\"Zm8\") = \"", enc.decode_base64url("Zm8"), "\"")
ngx.say("decode_base64url(\"Zm9v\") = \"", enc.decode_base64url("Zm9v"), "\"")
ngx.say("decode_base64url(\"Zm9vYg\") = \"", enc.decode_base64url("Zm9vYg"), "\"")
ngx.say("decode_base64url(\"Zm9vYmE\") = \"", enc.decode_base64url("Zm9vYmE"), "\"")
ngx.say("decode_base64url(\"Zm9vYmFy\") = \"", enc.decode_base64url("Zm9vYmFy"), "\"")
ngx.say("decode_base64url(\"_w\") = \"\\x", to_hex(enc.decode_base64url("_w")), "\"")
ngx.say("decode_base64url(\"YQBi\") = \"\\x", to_hex(enc.decode_base64url("YQBi")), "\"")
}
}
--- request
GET /t
--- response_body
decode_base64url("") = ""
decode_base64url("Zg") = "f"
decode_base64url("Zm8") = "fo"
decode_base64url("Zm9v") = "foo"
decode_base64url("Zm9vYg") = "foob"
decode_base64url("Zm9vYmE") = "fooba"
decode_base64url("Zm9vYmFy") = "foobar"
decode_base64url("_w") = "\xff"
decode_base64url("YQBi") = "\x610062"
--- no_error_log
[error]
[crit]
=== TEST 8: decode_base64url with invalid input
--- config
location = /t {
content_by_lua_block {
local enc = require("ngx.base64")
local res, err = enc.decode_base64url(" ")
ngx.say("decode_base64url returned: ", res, ", ", err)
}
}
--- request
GET /t
--- response_body
decode_base64url returned: nil, invalid input
--- no_error_log
[error]
-- NYI:

236
t/encode-base64.t Normal file
View File

@ -0,0 +1,236 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 5 - 1);
#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();
__DATA__
=== TEST 1: set base64 (string)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.encode_base64("hello")
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
aGVsbG8=
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 2: set base64 (nil)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.encode_base64(nil)
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body eval: "\n"
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 3: set base64 (number)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.encode_base64(3.14)
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
My4xNA==
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 4: set base64 (boolean)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.encode_base64(true)
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
dHJ1ZQ==
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 5: set base64 (buf is a little larger than 4096)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.encode_base64(string.rep("a", 3073))
end
ngx.say(string.len(s))
}
}
--- request
GET /base64
--- response_body
4100
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 6: set base64 (buf is just 4096)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.encode_base64(string.rep("a", 3071))
end
ngx.say(string.len(s))
}
}
--- request
GET /base64
--- response_body
4096
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 7: set base64 (number) without padding (explicitly specified)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 200 do
s = ngx.encode_base64(3.14, true)
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
My4xNA
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 8: set base64 (number) with padding (explicitly specified)
--- config
location = /base64 {
content_by_lua_block {
local s
for i = 1, 200 do
s = ngx.encode_base64(3.14, false)
end
ngx.say(s)
}
}
--- request
GET /base64
--- response_body
My4xNA==
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
-- NYI:
=== TEST 9: encode_base64url
--- config
location = /t {
content_by_lua_block {
local enc = require("ngx.base64")
-- RFC 4648 test vectors
ngx.say("encode_base64url(\"\") = \"", enc.encode_base64url(""), "\"")
ngx.say("encode_base64url(\"f\") = \"", enc.encode_base64url("f"), "\"")
ngx.say("encode_base64url(\"fo\") = \"", enc.encode_base64url("fo"), "\"")
ngx.say("encode_base64url(\"foo\") = \"", enc.encode_base64url("foo"), "\"")
ngx.say("encode_base64url(\"foob\") = \"", enc.encode_base64url("foob"), "\"")
ngx.say("encode_base64url(\"fooba\") = \"", enc.encode_base64url("fooba"), "\"")
ngx.say("encode_base64url(\"foobar\") = \"", enc.encode_base64url("foobar"), "\"")
ngx.say("encode_base64url(\"\\xff\") = \"", enc.encode_base64url("\xff"), "\"")
ngx.say("encode_base64url(\"a\\0b\") = \"", enc.encode_base64url("a\0b"), "\"")
}
}
--- request
GET /t
--- response_body
encode_base64url("") = ""
encode_base64url("f") = "Zg"
encode_base64url("fo") = "Zm8"
encode_base64url("foo") = "Zm9v"
encode_base64url("foob") = "Zm9vYg"
encode_base64url("fooba") = "Zm9vYmE"
encode_base64url("foobar") = "Zm9vYmFy"
encode_base64url("\xff") = "_w"
encode_base64url("a\0b") = "YQBi"
--- no_error_log
[error]
-- NYI:

218
t/errlog-raw-log.t Normal file
View File

@ -0,0 +1,218 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
log_level('error');
repeat_each(1);
plan tests => repeat_each() * (blocks() * 2 + 5);
add_block_preprocessor(sub {
my $block = shift;
my $http_config = $block->http_config || '';
my $init_by_lua_block = $block->init_by_lua_block || '';
$http_config .= <<_EOC_;
lua_package_path '$t::TestCore::lua_package_path';
init_by_lua_block {
$t::TestCore::init_by_lua_block
$init_by_lua_block
}
_EOC_
$block->set_value("http_config", $http_config);
});
no_long_string();
run_tests();
__DATA__
=== TEST 1: errlog.raw_log with bad log level (ngx.ERROR, -1)
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERROR, "hello, log")
ngx.say("done")
}
}
--- request
GET /log
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log
bad log level
=== TEST 2: errlog.raw_log with bad levels (9)
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(9, "hello, log")
ngx.say("done")
}
}
--- request
GET /log
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log
bad log level
=== TEST 3: errlog.raw_log with bad log message
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERR, 123)
ngx.say("done")
}
}
--- request
GET /log
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log
bad argument #2 to 'raw_log' (must be a string)
=== TEST 4: errlog.raw_log test log-level ERR
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERR, "hello world")
}
}
--- request
GET /log
--- error_log eval
qr/\[error\] \S+: \S+ hello world/
=== TEST 5: errlog.raw_log JITs
--- init_by_lua_block
-- local verbose = true
local verbose = false
local outfile = errlog_file
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
for i = 1, 100 do
errlog.raw_log(ngx.ERR, "hello world")
end
}
}
--- request
GET /log
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx.conf:\d+\):4 loop\]/
=== TEST 6: errlog.raw_log in init_by_lua
--- init_by_lua_block
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERR, "hello world from init_by_lua")
--- config
location /t {
return 200;
}
--- request
GET /t
--- grep_error_log chop
hello world from init_by_lua
--- grep_error_log_out eval
["hello world from init_by_lua\n", ""]
=== TEST 7: errlog.raw_log in init_worker_by_lua
--- http_config
init_worker_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERR, "hello world from init_worker_by_lua")
}
--- config
location /t {
return 200;
}
--- request
GET /t
--- grep_error_log chop
hello world from init_worker_by_lua
--- grep_error_log_out eval
["hello world from init_worker_by_lua\n", ""]
=== TEST 8: errlog.raw_log with \0 in the log message
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERR, "hello\0world")
ngx.say("ok")
}
}
--- request
GET /log
--- response_body
ok
--- error_log eval
"hello\0world, client: "
=== TEST 9: errlog.raw_log is captured by errlog.get_logs()
--- http_config
lua_capture_error_log 4k;
--- config
location /log {
content_by_lua_block {
local errlog = require "ngx.errlog"
errlog.raw_log(ngx.ERR, "hello from raw_log()")
local res, err = errlog.get_logs()
if not res then
error("FAILED " .. err)
end
ngx.say("log lines: ", #res / 3)
}
}
--- request
GET /log
--- response_body
log lines: 1
--- error_log eval
qr/\[error\] .*? hello from raw_log\(\)/
--- skip_nginx: 3: <1.11.2

1253
t/errlog.t Normal file

File diff suppressed because it is too large Load Diff

171
t/exit.t Normal file
View File

@ -0,0 +1,171 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
log_level('warn');
repeat_each(120);
#repeat_each(2);
plan tests => repeat_each() * (blocks() * 5);
add_block_preprocessor(sub {
my $block = shift;
my $http_config = $block->http_config || '';
my $init_by_lua_block = $block->init_by_lua_block || '';
$http_config .= <<_EOC_;
lua_package_path '\$prefix/html/?.lua;$t::TestCore::lua_package_path';
init_by_lua_block {
$t::TestCore::init_by_lua_block
$init_by_lua_block
}
_EOC_
$block->set_value("http_config", $http_config);
});
#no_diff();
#no_long_string();
run_tests();
__DATA__
=== TEST 1: sanity
--- config
location = /t {
content_by_lua_block {
ngx.exit(403)
}
}
--- request
GET /t
--- response_body_like: 403 Forbidden
--- error_code: 403
--- no_error_log eval
["[error]",
qr/ -- NYI: (?!FastFunc coroutine.yield)/,
" bad argument"]
=== TEST 2: call ngx.exit() from a custom lua module
--- config
location = /t {
content_by_lua_block {
local foo = require "foo"
foo.go()
}
}
--- user_files
>>> foo.lua
local exit = ngx.exit
local function go()
exit(403)
return
end
return { go = go }
--- request
GET /t
--- response_body_like: 403 Forbidden
--- error_code: 403
--- no_error_log eval
["[error]",
qr/ -- NYI: (?!FastFunc coroutine.yield)/,
" bad argument"]
=== TEST 3: accepts NGX_OK
--- config
location = /t {
content_by_lua_block {
ngx.exit(ngx.OK)
}
}
--- request
GET /t
--- response_body
--- no_error_log eval
["[error]",
qr/ -- NYI: (?!FastFunc coroutine.yield)/,
" bad argument"]
=== TEST 4: accepts NGX_ERROR
--- config
location = /t {
content_by_lua_block {
ngx.exit(ngx.ERROR)
}
}
--- request
GET /t
--- error_code:
--- response_body
--- no_error_log eval
["[error]",
qr/ -- NYI: (?!FastFunc coroutine.yield)/,
" bad argument"]
=== TEST 5: accepts NGX_DECLINED
--- config
location = /t {
content_by_lua_block {
ngx.exit(ngx.DECLINED)
}
}
--- request
GET /t
--- error_code:
--- response_body
--- no_error_log eval
["[error]",
qr/ -- NYI: (?!FastFunc coroutine.yield)/,
" bad argument"]
=== TEST 6: refuses NGX_AGAIN
--- config
location = /t {
content_by_lua_block {
ngx.exit(ngx.AGAIN)
}
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? bad argument to 'ngx.exit': does not accept NGX_AGAIN or NGX_DONE/
--- no_error_log eval
qr/ -- NYI: (?!FastFunc coroutine.yield)/
[crit]
=== TEST 7: refuses NGX_DONE
--- config
location = /t {
content_by_lua_block {
ngx.exit(ngx.DONE)
}
}
--- request
GET /t
--- response_body_like: 500 Internal Server Error
--- error_code: 500
--- error_log eval
qr/\[error\] .*? bad argument to 'ngx.exit': does not accept NGX_AGAIN or NGX_DONE/
--- no_error_log eval
qr/ -- NYI: (?!FastFunc coroutine.yield)/
[crit]

57
t/lib/helper.lua Normal file
View File

@ -0,0 +1,57 @@
local _M = {}
local run_lua_with_graceful_shutdown
do
local function set_up_ngx_conf(dir, code)
local conf = [[
error_log stderr error;
master_process off;
daemon off;
events {
worker_connections 64;
}
http {
init_worker_by_lua_block {
ngx.timer.at(0, function ()
]] .. code .. [[
require("ngx.process").signal_graceful_exit()
end)
}
}
]]
assert(os.execute("mkdir -p " .. dir .. "/logs"))
local conf_file = dir .. "/nginx.conf"
local f, err = io.open(conf_file, "w")
if not f then
ngx.log(ngx.ERR, err)
return
end
assert(f:write(conf))
f:close()
return conf_file
end
local function get_ngx_bin_path()
local ffi = require "ffi"
ffi.cdef[[char **ngx_argv;]]
return ffi.string(ffi.C.ngx_argv[0])
end
function run_lua_with_graceful_shutdown(dir, code)
local ngx_pipe = require "ngx.pipe"
local conf_file = set_up_ngx_conf(dir, code)
local nginx = get_ngx_bin_path()
local cmd = nginx .. " -p " .. dir .. " -c " .. conf_file
return ngx_pipe.spawn(cmd)
end
end
_M.run_lua_with_graceful_shutdown = run_lua_with_graceful_shutdown
return _M

View File

@ -0,0 +1,57 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
#master_on();
repeat_each(2);
plan tests => repeat_each() * (blocks() * 6);
#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();
__DATA__
=== TEST 1: sanity
--- config
location = /t {
content_by_lua_block {
local process = require "ngx.process"
local v
local get_pid = process.get_master_pid
for i = 1, 400 do
v = get_pid()
end
local f = assert(io.open(ngx.config.prefix() .. "/logs/nginx.pid", "r"))
local str = assert(f:read("*l"))
local expected = str
if tostring(v) == expected then
ngx.say("ok")
else
ngx.say("expected: ", expected)
end
f:close()
ngx.say("got: ", v, " (", type(v), ")")
}
}
--- request
GET /t
--- response_body_like chop
\Aok
got: \d+ \(number\)
\z
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):\d loop\]/
--- no_error_log
[error]
-- NYI:
stitch
--- skip_nginx: 6: < 1.13.8

57
t/master-pid.t Normal file
View File

@ -0,0 +1,57 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
master_on();
repeat_each(2);
plan tests => repeat_each() * (blocks() * 6);
#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();
__DATA__
=== TEST 1: sanity
--- config
location = /t {
content_by_lua_block {
local process = require "ngx.process"
local v
local get_pid = process.get_master_pid
for i = 1, 400 do
v = get_pid()
end
local f = assert(io.open(ngx.config.prefix() .. "/logs/nginx.pid", "r"))
local str = assert(f:read("*l"))
local expected = str
if tostring(v) == expected then
ngx.say("ok")
else
ngx.say("expected: ", expected)
end
f:close()
ngx.say("got: ", v, " (", type(v), ")")
}
}
--- request
GET /t
--- response_body_like chop
\Aok
got: \d+ \(number\)
\z
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):\d loop\]/
--- no_error_log
[error]
-- NYI:
stitch
--- skip_nginx: 6: < 1.13.8

104
t/md5-bin.t Normal file
View File

@ -0,0 +1,104 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 4);
#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();
__DATA__
=== TEST 1: set md5_bin (string)
--- config
location = /md5_bin {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5_bin("hello")
end
ngx.say(string.len(s))
}
}
--- request
GET /md5_bin
--- response_body
16
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
=== TEST 2: set md5_bin (nil)
--- config
location = /md5_bin {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5_bin(nil)
end
ngx.say(string.len(s))
}
}
--- request
GET /md5_bin
--- response_body
16
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
=== TEST 3: set md5_bin (number)
--- config
location = /md5_bin {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5_bin(3.14)
end
ngx.say(string.len(s))
}
}
--- request
GET /md5_bin
--- response_body
16
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
=== TEST 4: set md5_bin (boolean)
--- config
location = /md5_bin {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5_bin(true)
end
ngx.say(string.len(s))
}
}
--- request
GET /md5_bin
--- response_body
16
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]

104
t/md5.t Normal file
View File

@ -0,0 +1,104 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;
#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 4);
#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();
__DATA__
=== TEST 1: set md5 hello
--- config
location = /md5 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5("hello")
end
ngx.say(s)
}
}
--- request
GET /md5
--- response_body
5d41402abc4b2a76b9719d911017c592
--- error_log eval
qr/\[TRACE\s+1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
=== TEST 2: nil string to ngx.md5
--- config
location = /md5 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5(nil)
end
ngx.say(s)
}
}
--- request
GET /md5
--- response_body
d41d8cd98f00b204e9800998ecf8427e
--- error_log eval
qr/\[TRACE\s+1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
=== TEST 3: empty string to ngx.md5
--- config
location /md5 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5("")
end
ngx.say(s)
}
}
--- request
GET /md5
--- response_body
d41d8cd98f00b204e9800998ecf8427e
--- error_log eval
qr/\[TRACE\s+1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
=== TEST 4: number to ngx.md5
--- config
location /md5 {
content_by_lua_block {
local s
for i = 1, 100 do
s = ngx.md5(3.14)
end
ngx.say(s)
}
}
--- request
GET /md5
--- response_body
4beed3b9c4a886067de0e3a094246f78
--- error_log eval
qr/\[TRACE\s+1 content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]

Some files were not shown because too many files have changed in this diff Show More