diff --git a/src/bw/lua/utils.lua b/src/bw/lua/utils.lua index 0a56c26f9..2204b6f3f 100644 --- a/src/bw/lua/utils.lua +++ b/src/bw/lua/utils.lua @@ -353,7 +353,10 @@ utils.rand = function(nb) return result end -utils.get_deny_status = function() +utils.get_deny_status = function(stream) + if stream then + return 403 + end local status, err = datastore:get("variable_DENY_HTTP_STATUS") if not status then logger.log(ngx.ERR, "UTILS", "Can't get DENY_HTTP_STATUS variable " .. err) diff --git a/src/common/confs/http.conf b/src/common/confs/http.conf index 2c090c9a5..55399ec56 100644 --- a/src/common/confs/http.conf +++ b/src/common/confs/http.conf @@ -1,4 +1,4 @@ -# /etc/nginx/base_http.conf +# /etc/nginx/http.conf # zero copy within the kernel sendfile on; @@ -67,7 +67,7 @@ server_tokens off; {% if MULTISITE == "yes" and SERVER_NAME != "" %} {% set map_servers = {} %} {% for server_name in SERVER_NAME.split(" ") %} - {% if server_name + "_SERVER_NAME" in all %} + {% if server_name + "_SERVER_NAME" in all and all[server_name + "_SERVER_TYPE"] == "http" %} {% set x = map_servers.update({server_name : all[server_name + "_SERVER_NAME"].split(" ")}) %} {% endif %} {% endfor %} @@ -79,7 +79,7 @@ server_tokens off; {% set x = found.update({"res" : true}) %} {% endif %} {% endfor %} - {% if not found["res"] %} + {% if not found["res"] and all[server_name + "_SERVER_TYPE"] == "stream" %} {% set x = map_servers.update({server_name : [server_name]}) %} {% endif %} {% endif %} @@ -87,6 +87,6 @@ server_tokens off; {% for first_server in map_servers +%} include /etc/nginx/{{ first_server }}/server.conf; {% endfor %} -{% elif MULTISITE == "no" and SERVER_NAME != "" +%} +{% elif MULTISITE == "no" and SERVER_NAME != "" and SERVER_TYPE == "http" +%} include /etc/nginx/server.conf; {% endif %} diff --git a/src/common/confs/nginx.conf b/src/common/confs/nginx.conf index 7ef34e6cc..38420cafe 100644 --- a/src/common/confs/nginx.conf +++ b/src/common/confs/nginx.conf @@ -46,13 +46,13 @@ http { include /etc/bunkerweb/configs/http/*.conf; } -#stream { +stream { # include base stream configuration -# include /etc/nginx/stream.conf; + include /etc/nginx/stream.conf; # include core and plugins stream configurations -# include /etc/nginx/stream/*.conf; + include /etc/nginx/stream/*.conf; # include custom stream configurations -# include /etc/bunkerweb/configs/stream/*.conf; -#} + include /etc/bunkerweb/configs/stream/*.conf; +} \ No newline at end of file diff --git a/src/common/confs/server-stream/log-stream-lua.conf b/src/common/confs/server-stream/log-stream-lua.conf new file mode 100644 index 000000000..b3448d129 --- /dev/null +++ b/src/common/confs/server-stream/log-stream-lua.conf @@ -0,0 +1,44 @@ +log_by_lua_block { + +local utils = require "utils" +local logger = require "logger" +local datastore = require "datastore" +local plugins = require "plugins" + +logger.log(ngx.INFO, "LOG", "Log phase started") + +-- List all plugins +local list, err = plugins:list() +if not list then + logger.log(ngx.ERR, "LOG", "Can't list loaded plugins : " .. err) + list = {} +end + +-- Call log method of plugins +for i, plugin in ipairs(list) do + local ret, plugin_lua = pcall(require, plugin.id .. "/" .. plugin.id) + if ret then + local plugin_obj = plugin_lua.new() + if plugin_obj.log ~= nil then + logger.log(ngx.INFO, "LOG", "Executing log() of " .. plugin.id) + local ok, err = plugin_obj:log() + if not ok then + logger.log(ngx.ERR, "LOG", "Error while calling log() on plugin " .. plugin.id .. " : " .. err) + else + logger.log(ngx.INFO, "LOG", "Return value from " .. plugin.id .. ".log() is : " .. err) + end + else + logger.log(ngx.INFO, "LOG", "log() method not found in " .. plugin.id .. ", skipped execution") + end + end +end + +-- Display reason at info level +local reason = utils.get_reason() +if reason then + logger.log(ngx.INFO, "LOG", "Client was denied with reason : " .. reason) +end + +logger.log(ngx.INFO, "LOG", "Log phase ended") + +} \ No newline at end of file diff --git a/src/common/confs/server-stream/preread-stream-lua.conf b/src/common/confs/server-stream/preread-stream-lua.conf new file mode 100644 index 000000000..834038aa8 --- /dev/null +++ b/src/common/confs/server-stream/preread-stream-lua.conf @@ -0,0 +1,86 @@ +preread_by_lua_block { + +local logger = require "logger" +local datastore = require "datastore" +local plugins = require "plugins" +local utils = require "utils" + +logger.log(ngx.INFO, "PREREAD", "Preread phase started") + +-- Process bans as soon as possible +local banned, err = datastore:get("bans_ip_" .. ngx.var.remote_addr) +if banned then + logger.log(ngx.WARN, "PREREAD", "IP " .. ngx.var.remote_addr .. " is banned with reason : " .. banned) + ngx.exit(utils.get_deny_status(true)) +end +-- Redis case +local use_redis = utils.get_variable("USE_REDIS") +if use_redis == "yes" then + -- Connect + local redis_client, err = clusterstore:connect() + if not redis_client then + logger.log(ngx.ERR, "PREREAD", "can't connect to redis server : " .. err) + else + -- Get ban + local ban, err = redis_client:get("ban_" .. ngx.var.remote_addr) + if err then + logger.log(ngx.ERR, "PREREAD", "GET failed : " .. err) + elseif ban then + -- Get TTL + local ttl, err = redis_client:ttl("ban_" .. ngx.var.remote_addr) + if not ttl then + logger.log(ngx.ERR, "PREREAD", "TTL failed : " .. err) + else + local ok, err = datastore:set("bans_ip_" .. ip, ban, ttl) + if not ok then + logger.log(ngx.ERR, "PREREAD", "can't save ban to the datastore : " .. err) + return + end + end + end + redis_client:close() + end +end + +-- List all plugins +local list, err = plugins:list() +if not list then + logger.log(ngx.ERR, "PREREAD", "Can't list loaded plugins : " .. err) + list = {} +end + +-- Call preread method of plugins +for i, plugin in ipairs(list) do + local ret, plugin_lua = pcall(require, plugin.id .. "/" .. plugin.id) + if ret then + local plugin_obj = plugin_lua.new() + if plugin_obj.preread ~= nil then + logger.log(ngx.INFO, "PREREAD", "Executing preread() of " .. plugin.id) + local ok, err, ret, value = plugin_obj:preread() + if not ok then + logger.log(ngx.ERR, "PREREAD", "Error while calling preread() on plugin " .. plugin.id .. " : " .. err) + else + logger.log(ngx.INFO, "PREREAD", "Return value from " .. plugin.id .. ".preread() is : " .. err) + end + if ret then + if type(value) == "number" then + if value == utils.get_deny_status(true) then + logger.log(ngx.WARN, "PREREAD", "Denied access from " .. plugin.id .. " : " .. err) + ngx.var.reason = plugin.id + else + logger.log(ngx.NOTICE, "PREREAD", plugin.id .. " returned status " .. tostring(value) .. " : " .. err) + end + return ngx.exit(value) + else + return value + end + end + else + logger.log(ngx.INFO, "PREREAD", "preread() method not found in " .. plugin.id .. ", skipped execution") + end + end +end + +logger.log(ngx.INFO, "PREREAD", "Preread phase ended") + +} \ No newline at end of file diff --git a/src/common/confs/server-stream/server-stream.conf b/src/common/confs/server-stream/server-stream.conf new file mode 100644 index 000000000..ca55e6a8a --- /dev/null +++ b/src/common/confs/server-stream/server-stream.conf @@ -0,0 +1,23 @@ +server { + + # listen +{% if LISTEN_STREAM == "yes" +%} + listen 0.0.0.0:{{ LISTEN_STREAM_PORT }}{% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %}; +{% endif %} + + # custom config + include /etc/bunkerweb/configs/server-stream/*.conf; +{% if MULTISITE == "yes" +%} + include /etc/bunkerweb/configs/server-stream/{{ SERVER_NAME.split(" ")[0] }}/*.conf; +{% endif %} + + # reason variable + set $reason ''; + + # include LUA files + include {{ NGINX_PREFIX }}preread-stream-lua.conf; + include {{ NGINX_PREFIX }}log-stream-lua.conf; + + # include config files + include {{ NGINX_PREFIX }}server-stream/*.conf; +} \ No newline at end of file diff --git a/src/common/confs/stream.conf b/src/common/confs/stream.conf index b88622196..cfa07abba 100644 --- a/src/common/confs/stream.conf +++ b/src/common/confs/stream.conf @@ -1,52 +1,44 @@ # /etc/nginx/stream.conf -# size of the preread buffer +# preread buffer size +# TODO : global setting STREAM_PREREAD_BUFFER_SIZE preread_buffer_size 16k; -# timeout of the preread phase +# preread timeout +# TODO : global setting STREAM_PREREAD_TIMEOUT preread_timeout 30s; -# proxy protocol timeout +# PROXY protocol timeout +# TODO : global setting STREAM_PROXY_PROTOCOL_TIMEOUT proxy_protocol_timeout 30s; # resolvers to use resolver {{ DNS_RESOLVERS }} ipv6=off; # resolver timeout +# TODO : global setting STREAM_RESOLVER_TIMEOUT resolver_timeout 30s; # remove 200ms delay tcp_nodelay on; -# bucket hash size -variables_hash_bucket_size 64; -variables_hash_max_size 1024; - -# log format and level -log_format proxy '$remote_addr [$time_local] ' - '$protocol $status $bytes_sent $bytes_received ' - '$session_time "$upstream_addr" ' - '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"'; -access_log /var/log/nginx/access.log proxy; - # lua path and dicts lua_package_path "/usr/share/bunkerweb/lua/?.lua;/usr/share/bunkerweb/core/?.lua;/etc/bunkerweb/plugins/?.lua;/usr/share/bunkerweb/deps/lib/lua/?.lua;;"; lua_package_cpath "/usr/share/bunkerweb/deps/lib/?.so;/usr/share/bunkerweb/deps/lib/lua/?.so;;"; lua_ssl_trusted_certificate "/usr/share/bunkerweb/misc/root-ca.pem"; lua_ssl_verify_depth 2; -lua_shared_dict datastore 256m; +lua_shared_dict datastore {{ DATASTORE_MEMORY_SIZE }}; # LUA init block -include /etc/nginx/init-lua.conf; +include /etc/nginx/init-stream-lua.conf; -# default server when MULTISITE=yes -{% if MULTISITE == "yes" %}include /etc/nginx/multisite-default-server.conf;{% endif +%} +# TODO add default stream server if that makes any sense ? # server config(s) {% if MULTISITE == "yes" and SERVER_NAME != "" %} {% set map_servers = {} %} {% for server_name in SERVER_NAME.split(" ") %} - {% if server_name + "_SERVER_NAME" in all %} + {% if server_name + "_SERVER_NAME" in all and all[server_name + "_SERVER_TYPE"] == "stream" %} {% set x = map_servers.update({server_name : all[server_name + "_SERVER_NAME"].split(" ")}) %} {% endif %} {% endfor %} @@ -58,14 +50,14 @@ include /etc/nginx/init-lua.conf; {% set x = found.update({"res" : true}) %} {% endif %} {% endfor %} - {% if not found["res"] %} + {% if not found["res"] and all[server_name + "_SERVER_TYPE"] == "stream" %} {% set x = map_servers.update({server_name : [server_name]}) %} - {% endif %} + {% endif %} {% endif %} {% endfor %} {% for first_server in map_servers +%} -include /etc/nginx/{{ first_server }}/server.conf; +include /etc/nginx/{{ first_server }}/server-stream.conf; {% endfor %} -{% elif MULTISITE == "no" +%} -include /etc/nginx/server.conf; +{% elif MULTISITE == "no" and SERVER_NAME != "" and SERVER_TYPE == "stream" +%} +include /etc/nginx/server-stream.conf; {% endif %} diff --git a/src/common/core/country/country.lua b/src/common/core/country/country.lua index 6d31fbfc3..dcf31306c 100644 --- a/src/common/core/country/country.lua +++ b/src/common/core/country/country.lua @@ -81,6 +81,10 @@ function _M:access() return true, "client IP " .. ngx.var.remote_addr .. " is not blacklisted (country = " .. country .. ")", nil, nil end +function _M:preread() + return self:access() +end + function _M:is_in_cache(ip) local data, err = datastore:get("plugin_country_cache_" .. ip) if not data then diff --git a/src/common/core/limit/confs/server-stream/limitconn.conf b/src/common/core/limit/confs/server-stream/limitconn.conf new file mode 100644 index 000000000..4e0f0efd8 --- /dev/null +++ b/src/common/core/limit/confs/server-stream/limitconn.conf @@ -0,0 +1,5 @@ +{% if USE_LIMIT_CONN == "yes" +%} + +limit_conn sips {{ LIMIT_CONN_MAX_STREAM }}; + +{% endif %} \ No newline at end of file diff --git a/src/common/core/limit/confs/stream/limitconn.conf b/src/common/core/limit/confs/stream/limitconn.conf new file mode 100644 index 000000000..db1073dfb --- /dev/null +++ b/src/common/core/limit/confs/stream/limitconn.conf @@ -0,0 +1,6 @@ +{% if has_variable(all, "USE_LIMIT_CONN", "yes") +%} + +limit_conn_zone $binary_remote_addr zone=sips:10m; +limit_conn_log_level warn; + +{% endif %} \ No newline at end of file diff --git a/src/common/core/realip/confs/server-stream/real-ip.conf b/src/common/core/realip/confs/server-stream/real-ip.conf new file mode 100644 index 000000000..684968c30 --- /dev/null +++ b/src/common/core/realip/confs/server-stream/real-ip.conf @@ -0,0 +1,10 @@ +{% if USE_REAL_IP == "yes" +%} + {% for element in read_lines("/var/cache/bunkerweb/realip/combined.list") +%} +set_real_ip_from {{ element }}; + {% endfor +%} + {% if REAL_IP_FROM != "" %} + {% for element in REAL_IP_FROM.split(" ") +%} +set_real_ip_from {{ element }}; + {% endfor %} + {% endif %} +{% endif %} \ No newline at end of file diff --git a/src/common/core/reverseproxy/confs/server-stream/reverse-proxy.conf b/src/common/core/reverseproxy/confs/server-stream/reverse-proxy.conf new file mode 100644 index 000000000..393d57794 --- /dev/null +++ b/src/common/core/reverseproxy/confs/server-stream/reverse-proxy.conf @@ -0,0 +1,11 @@ +{% if USE_REVERSE_PROXY == "yes" +%} + +# TODO : more settings specific to stream +{% if REVERSE_PROXY_STREAM_PROXY_PROTOCOL == "yes" +%} +proxy_protocol on; +{% endif +%} + +set $backend{{ counter.value }} "{{ host }}"; +proxy_pass $backend{{ counter.value }}; + +{% endif %} \ No newline at end of file diff --git a/src/deps/clone.sh b/src/deps/clone.sh index 64dc8b325..ce71e3c15 100755 --- a/src/deps/clone.sh +++ b/src/deps/clone.sh @@ -288,3 +288,7 @@ git_secure_clone "https://github.com/google/ngx_brotli.git" "6e975bcb015f62e1f30 # ngx_devel_kit echo "ℹ️ Downloading ngx_devel_kit" git_secure_clone "https://github.com/vision5/ngx_devel_kit.git" "b4642d6ca01011bd8cd30b253f5c3872b384fd21" + +# stream-lua-nginx-module +echo "ℹ️ Downloading stream-lua-nginx-module" +git_secure_clone "https://github.com/openresty/stream-lua-nginx-module.git" "2ef14f373b991b911c4eb5d09aa333352be9a756" \ No newline at end of file diff --git a/src/deps/install.sh b/src/deps/install.sh index 1f75f1c25..394129fb2 100755 --- a/src/deps/install.sh +++ b/src/deps/install.sh @@ -132,7 +132,7 @@ if [ "$OS" = "fedora" ] ; then CONFARGS="$(echo -n "$CONFARGS" | sed "s/--with-ld-opt='.*'/--with-ld-opt=-lpcre/" | sed "s/--with-cc-opt='.*'//")" fi echo '#!/bin/bash' > "/tmp/bunkerweb/deps/src/nginx-${NGINX_VERSION}/configure-fix.sh" -echo "./configure $CONFARGS --add-dynamic-module=/tmp/bunkerweb/deps/src/ModSecurity-nginx --add-dynamic-module=/tmp/bunkerweb/deps/src/headers-more-nginx-module --add-dynamic-module=/tmp/bunkerweb/deps/src/nginx_cookie_flag_module --add-dynamic-module=/tmp/bunkerweb/deps/src/lua-nginx-module --add-dynamic-module=/tmp/bunkerweb/deps/src/ngx_brotli --add-dynamic-module=/tmp/bunkerweb/deps/src/ngx_devel_kit" >> "/tmp/bunkerweb/deps/src/nginx-${NGINX_VERSION}/configure-fix.sh" +echo "./configure $CONFARGS --add-dynamic-module=/tmp/bunkerweb/deps/src/ModSecurity-nginx --add-dynamic-module=/tmp/bunkerweb/deps/src/headers-more-nginx-module --add-dynamic-module=/tmp/bunkerweb/deps/src/nginx_cookie_flag_module --add-dynamic-module=/tmp/bunkerweb/deps/src/lua-nginx-module --add-dynamic-module=/tmp/bunkerweb/deps/src/ngx_brotli --add-dynamic-module=/tmp/bunkerweb/deps/src/ngx_devel_kit --add-dynamic-module=/tmp/bunkerweb/deps/src/stream-lua-nginx-module" >> "/tmp/bunkerweb/deps/src/nginx-${NGINX_VERSION}/configure-fix.sh" do_and_check_cmd chmod +x "/tmp/bunkerweb/deps/src/nginx-${NGINX_VERSION}/configure-fix.sh" CHANGE_DIR="/tmp/bunkerweb/deps/src/nginx-${NGINX_VERSION}" LUAJIT_LIB="/usr/share/bunkerweb/deps/lib -Wl,-rpath,/usr/share/bunkerweb/deps/lib" LUAJIT_INC="/usr/share/bunkerweb/deps/include/luajit-2.1" MODSECURITY_LIB="/usr/share/bunkerweb/deps/lib" MODSECURITY_INC="/usr/share/bunkerweb/deps/include" do_and_check_cmd ./configure-fix.sh CHANGE_DIR="/tmp/bunkerweb/deps/src/nginx-${NGINX_VERSION}" do_and_check_cmd make -j $NTASK modules diff --git a/src/deps/src/stream-lua-nginx-module/.gitattributes b/src/deps/src/stream-lua-nginx-module/.gitattributes new file mode 100644 index 000000000..6fe6f35ce --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/.gitattributes @@ -0,0 +1 @@ +*.t linguist-language=Text diff --git a/src/deps/src/stream-lua-nginx-module/.github/workflows/semantic-pull-request.yml b/src/deps/src/stream-lua-nginx-module/.github/workflows/semantic-pull-request.yml new file mode 100644 index 000000000..12b87cb3c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/.github/workflows/semantic-pull-request.yml @@ -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 diff --git a/src/deps/src/stream-lua-nginx-module/.gitignore b/src/deps/src/stream-lua-nginx-module/.gitignore new file mode 100644 index 000000000..6d82f25b5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/.gitignore @@ -0,0 +1,78 @@ +*~ +*.swp +*.swo +*.bak +nginx +buildroot/ +lua +tags +build19 +ctags +*.tags +src/contentby.[ch] +src/common.h +src/util.[ch] +src/directive.[ch] +src/lex.[ch] +src/module.c +src/cache.[ch] +src/clfactory.[ch] +src/tcp.[ch] +src/exception.[ch] +src/pcrefix.[ch] +src/uthread.[ch] +src/coroutine.[ch] +src/semaphore.[ch] +src/output.[ch] +src/consts.[ch] +src/string.[ch] +src/log.[ch] +src/time.[ch] +src/control.[ch] +src/sleep.[ch] +src/phase.[ch] +src/ctx.[ch] +src/regex.[ch] +src/script.[ch] +src/shdict.[ch] +src/udp.[ch] +src/timer.[ch] +src/config.[ch] +src/worker.[ch] +src/misc.[ch] +src/initby.[ch] +src/shdict.[ch] +src/initworkerby.[ch] +src/variable.[ch] +src/args.[ch] +echo +reindex +go +t/servroot* +*.plist +Makefile +t/*.t_ +html_out/ +ebooks.sh +*.pdf +*.patch +src/ngx_stream_lua_autoconf.h +src/api.c +src/autoconf.h +src/balancer.c +src/balancer.h +src/certby.c +src/certby.h +src/filters.c +src/filters.h +src/logby.c +src/logby.h +src/prereadby.c +src/prereadby.h +src/probe.h +src/request.c +src/request.h +src/ringbuf.c +src/ringbuf.h +src/ssl.c +src/ssl.h diff --git a/src/deps/src/stream-lua-nginx-module/.travis.yml b/src/deps/src/stream-lua-nginx-module/.travis.yml new file mode 100644 index 000000000..e13c46eed --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/.travis.yml @@ -0,0 +1,104 @@ +sudo: required +dist: bionic + +branches: + only: + - "master" + +os: linux + +language: c + +compiler: + - gcc + +addons: + apt: + packages: [ axel, cpanminus, libgd-dev, libtest-base-perl, libtext-diff-perl, liburi-perl, libwww-perl, libtest-longstring-perl, liblist-moreutils-perl, dnsutils ] + +cache: + apt: true + directories: + - download-cache + +env: + global: + - LUAJIT_PREFIX=/opt/luajit21 + - LUAJIT_LIB=$LUAJIT_PREFIX/lib + - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH + - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 + - LUA_INCLUDE_DIR=$LUAJIT_INC + - 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 + - JOBS=3 + - NGX_BUILD_JOBS=$JOBS + - TEST_NGINX_SLEEP=0.006 + matrix: + - NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.0l + - NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1s + +services: + - memcache + - redis-server + +install: + - sudo apt update + - sudo apt install --only-upgrade ca-certificates + - if [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache/ https://downloads.sourceforge.net/project/pcre/pcre/${PCRE_VER}/pcre-${PCRE_VER}.tar.gz; 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 + - git clone https://github.com/openresty/openresty-devel-utils.git + - git clone https://github.com/openresty/lua-cjson.git + - git clone https://github.com/openresty/openresty.git ../openresty + - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx + - git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module + - git clone https://github.com/openresty/mockeagain.git + - git clone https://github.com/openresty/test-nginx.git + - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git + - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module + - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module + - git clone https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module + - git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module + - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache + - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core + +script: + - sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT + - sudo iptables -A OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP + - sudo iptables -A OUTPUT -p udp --dst 127.0.0.2 --dport 12345 -j DROP + - sudo ip addr add 10.254.254.1/24 dev lo + - sudo ip addr add 10.254.254.2/24 dev lo + - sudo ip route add prohibit 0.0.0.1/32 + - 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 .. + - cd luajit2 + - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > 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 ../test-nginx && sudo cpanm . && cd .. + - cd lua-cjson/ && make -j$JOBS && sudo make install && cd .. + - cd mockeagain/ && make CC=$CC -j$JOBS && cd .. + - tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz + - cd openssl-$OPENSSL_VER/ + - ./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 .. + - export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH + - export NGX_BUILD_CC=$CC + - sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1) + - nginx -V + - ldd `which nginx`|grep -E 'luajit|ssl|pcre' + - export LD_PRELOAD=$PWD/mockeagain/mockeagain.so + - export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH + - export TEST_NGINX_RESOLVER=8.8.4.4 + - dig +short @$TEST_NGINX_RESOLVER openresty.org || exit 0 + - dig +short @$TEST_NGINX_RESOLVER agentzh.org || exit 0 + - prove -I. -r t diff --git a/src/deps/src/stream-lua-nginx-module/DEV_NOTES.md b/src/deps/src/stream-lua-nginx-module/DEV_NOTES.md new file mode 100644 index 000000000..2a6c85ee9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/DEV_NOTES.md @@ -0,0 +1,32 @@ +**DO NOT EDIT THIS REPO DIRECTLY, CODE ARE AUTOMATICALLY GENERATED FROM TEMPLATES** + +# Install + +There are some quirks when compiling the refactored stream module. + +You will need NGINX 1.13.x for it to work as we targeted the refactor toward +that version only. The [1.13.x branch](https://github.com/openresty/openresty/tree/1.13.x) +of OpenResty should serve this well. + +You need to turn off FFI when compiling (for the time being) as not all FFI functions got +their respective guard. + +Here is what I use for compiling: + +```shell +./configure --prefix=/home/datong/openresty-stream-build \ + --with-stream --with-stream_ssl_module + --add-module=/home/datong/orinc/stream-lua-nginx-module + -j4 --with-cc-opt='-DNGX_LUA_NO_FFI_API' --with-debug +``` + +# Status +The project can run simple "Hello, World" without any problem or Valgrind warnings. + +I have also tested timer and variable support and they appears to work as well. + +The only feature that has not been ported is the cosocket API due to it's +complexity. This does makes the module somewhat useless as input from user can not be read by +Lua programs right now. I will spend some time to sort this out shortly. + +I will spend some time debugging and get the test suite running next. diff --git a/src/deps/src/stream-lua-nginx-module/README.md b/src/deps/src/stream-lua-nginx-module/README.md new file mode 100644 index 000000000..d3f9bf0e4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/README.md @@ -0,0 +1,683 @@ + +Name +==== + +ngx_stream_lua_module - Embed the power of Lua into Nginx stream/TCP Servers. + +This module is a core component of OpenResty. If you are using this module, +then you are essentially using OpenResty. + +*This module is not distributed with the Nginx source.* See [the installation +instructions](#installation). + +Table of Contents +================= + +* [Name](#name) +* [Status](#status) +* [Version](#version) +* [Synopsis](#synopsis) +* [Description](#description) + * [Directives](#directives) + * [Nginx API for Lua](#nginx-api-for-lua) +* [TODO](#todo) +* [Nginx Compatibility](#nginx-compatibility) +* [Installation](#installation) +* [Community](#community) + * [English Mailing List](#english-mailing-list) + * [Chinese Mailing List](#chinese-mailing-list) +* [Code Repository](#code-repository) +* [Bugs and Patches](#bugs-and-patches) +* [Acknowledgments](#acknowledgments) +* [Copyright and License](#copyright-and-license) +* [See Also](#see-also) + +Status +====== + +Production ready. + +Version +======= + +This document describes ngx_stream_lua +[v0.0.8](https://github.com/openresty/stream-lua-nginx-module/tags), which was released +on 2 July, 2020. + +Synopsis +======== + +```nginx +events { + worker_connections 1024; +} + +stream { + # define a TCP server listening on the port 1234: + server { + listen 1234; + + content_by_lua_block { + ngx.say("Hello, Lua!") + } + } +} +``` + +Set up as an SSL TCP server: + +```nginx +stream { + server { + listen 4343 ssl; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; + ssl_certificate /path/to/cert.pem; + ssl_certificate_key /path/to/cert.key; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() -- read a line from downstream + if data == "thunder!" then + ngx.say("flash!") -- output data + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +} +``` + +Listening on a UNIX domain socket is also supported: + +```nginx +stream { + server { + listen unix:/tmp/nginx.sock; + + content_by_lua_block { + ngx.say("What's up?") + ngx.flush(true) -- flush any pending output and wait + ngx.sleep(3) -- sleeping for 3 sec + ngx.say("Bye bye...") + } + } +} +``` + +[Back to TOC](#table-of-contents) + +Description +=========== + +This is a port of the +[ngx_http_lua_module](https://github.com/openresty/lua-nginx-module#readme) to +the Nginx "stream" subsystem so as to support generic stream/TCP clients. + +The available Lua APIs and Nginx directives remain the same as those of the +ngx_http_lua module. + +[Back to TOC](#table-of-contents) + +Directives +---------- + +The following directives are ported directly from ngx_http_lua. Please check +the documentation of ngx_http_lua for more details about their usage and +behavior. + +* [lua_load_resty_core](https://github.com/openresty/lua-nginx-module#lua_load_resty_core) +* [lua_code_cache](https://github.com/openresty/lua-nginx-module#lua_code_cache) +* [lua_regex_cache_max_entries](https://github.com/openresty/lua-nginx-module#lua_regex_cache_max_entries) +* [lua_package_path](https://github.com/openresty/lua-nginx-module#lua_package_path) +* [lua_package_cpath](https://github.com/openresty/lua-nginx-module#lua_package_cpath) +* [init_by_lua_block](https://github.com/openresty/lua-nginx-module#init_by_lua_block) +* [init_by_lua_file](https://github.com/openresty/lua-nginx-module#init_by_lua_file) +* [init_worker_by_lua_block](https://github.com/openresty/lua-nginx-module#init_worker_by_lua_block) +* [init_worker_by_lua_file](https://github.com/openresty/lua-nginx-module#init_worker_by_lua_file) +* [preread_by_lua_block](#preread_by_lua_block) +* [preread_by_lua_file](#preread_by_lua_file) +* [content_by_lua_block](https://github.com/openresty/lua-nginx-module#content_by_lua_block) +* [content_by_lua_file](https://github.com/openresty/lua-nginx-module#content_by_lua_file) +* [balancer_by_lua_block](https://github.com/openresty/lua-nginx-module#balancer_by_lua_block) +* [balancer_by_lua_file](https://github.com/openresty/lua-nginx-module#balancer_by_lua_file) +* [log_by_lua_block](#log_by_lua_block) +* [log_by_lua_file](#log_by_lua_file) +* [ssl_client_hello_by_lua_block](https://github.com/openresty/lua-nginx-module#ssl_client_hello_by_lua_block) +* [ssl_client_hello_by_lua_file](https://github.com/openresty/lua-nginx-module#ssl_client_hello_by_lua_file) +* [ssl_certificate_by_lua_block](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_block) +* [ssl_certificate_by_lua_file](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_file) +* [lua_shared_dict](https://github.com/openresty/lua-nginx-module#lua_shared_dict) +* [lua_socket_connect_timeout](https://github.com/openresty/lua-nginx-module#lua_socket_connect_timeout) +* [lua_socket_buffer_size](https://github.com/openresty/lua-nginx-module#lua_socket_buffer_size) +* [lua_socket_pool_size](https://github.com/openresty/lua-nginx-module#lua_socket_pool_size) +* [lua_socket_keepalive_timeout](https://github.com/openresty/lua-nginx-module#lua_socket_keepalive_timeout) +* [lua_socket_log_errors](https://github.com/openresty/lua-nginx-module#lua_socket_log_errors) +* [lua_ssl_ciphers](https://github.com/openresty/lua-nginx-module#lua_ssl_ciphers) +* [lua_ssl_crl](https://github.com/openresty/lua-nginx-module#lua_ssl_crl) +* [lua_ssl_protocols](https://github.com/openresty/lua-nginx-module#lua_ssl_protocols) +* [lua_ssl_trusted_certificate](https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate) +* [lua_ssl_verify_depth](https://github.com/openresty/lua-nginx-module#lua_ssl_verify_depth) +* [lua_ssl_conf_command](https://github.com/openresty/lua-nginx-module#lua_ssl_conf_command) +* [lua_check_client_abort](https://github.com/openresty/lua-nginx-module#lua_check_client_abort) +* [lua_max_pending_timers](https://github.com/openresty/lua-nginx-module#lua_max_pending_timers) +* [lua_max_running_timers](https://github.com/openresty/lua-nginx-module#lua_max_running_timers) +* [lua_sa_restart](https://github.com/openresty/lua-nginx-module#lua_sa_restart) +* [lua_add_variable](#lua_add_variable) +* [lua_capture_error_log](https://github.com/openresty/lua-nginx-module#lua_capture_error_log) +* [preread_by_lua_no_postpone](#preread_by_lua_no_postpone) + +The [send_timeout](https://nginx.org/r/send_timeout) directive in the Nginx +"http" subsystem is missing in the "stream" subsystem. As such, +ngx_stream_lua_module uses the `lua_socket_send_timeout` directive for this +purpose instead. + +**Note:** the lingering close directive that used to exist in older version of +`stream_lua_nginx_module` has been removed and can now be simulated with the +newly added [tcpsock:shutdown](#tcpsockshutdown) API if necessary. + +[Back to TOC](#table-of-contents) + +preread_by_lua_block +-------------------- + +**syntax:** *preread_by_lua_block { lua-script }* + +**context:** *stream, server* + +**phase:** *preread* + +Acts as a `preread` phase handler and executes Lua code string specified in `lua-script` for every connection +(or packet in datagram mode). +The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). + +It is possible to acquire the raw request socket using [ngx.req.socket](https://github.com/openresty/lua-nginx-module#ngxreqsocket) +and receive data from or send data to the client. However, keep in mind that calling the `receive()` method +of the request socket will consume the data from the buffer and such consumed data will not be seen by handlers +further down the chain. + +The `preread_by_lua_block` code will always run at the end of the `preread` processing phase unless +[preread\_by\_lua\_no\_postpone](#preread_by_lua_no_postpone) is turned on. + +This directive was first introduced in the `v0.0.3` release. + +[Back to TOC](#directives) + +preread_by_lua_file +------------------- + +**syntax:** *preread_by_lua_file <path-to-lua-script-file>* + +**context:** *stream, server* + +**phase:** *preread* + +Equivalent to [preread_by_lua_block](#preread_by_lua_block), except that the file specified by `` contains the Lua code +or LuaJIT bytecode to be executed. + +Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. + +When a relative path like `foo/bar.lua` is given, it will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option given when starting the Nginx server. + +When the Lua code cache is turned on (by default), the user code is loaded once at the first connection and cached. The Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid having to reload Nginx. + +This directive was first introduced in the `v0.0.3` release. + +[Back to TOC](#directives) + +log_by_lua_block +---------------- + +**syntax:** *log_by_lua_block { lua-script }* + +**context:** *stream, server* + +**phase:** *log* + +Runs the Lua source code specified as `` during the `log` request processing phase. This does not replace the current access logs, but runs before. + +Yielding APIs such as `ngx.req.socket`, `ngx.socket.*`, `ngx.sleep`, or `ngx.say` are **not** available in this phase. + +This directive was first introduced in the `v0.0.3` release. + +[Back to TOC](#directives) + +log_by_lua_file +--------------- + +**syntax:** *log_by_lua_file <path-to-lua-script-file>* + +**context:** *stream, server* + +**phase:** *log* + +Equivalent to [log_by_lua_block](#log_by_lua_block), except that the file specified by `` contains the Lua code +or LuaJIT bytecode to be executed. + +Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. + +When a relative path like `foo/bar.lua` is given, it will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option given when starting the Nginx server. + +When the Lua code cache is turned on (by default), the user code is loaded once at the first connection and cached. The Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid having to reload Nginx. + +This directive was first introduced in the `v0.0.3` release. + +[Back to TOC](#directives) + +lua_add_variable +---------------- + +**syntax:** *lua_add_variable $var* + +**context:** *stream* + +Add the variable `$var` to the "stream" subsystem and makes it changeable. If `$var` already exists, +this directive will do nothing. + +By default, variables added using this directive are considered "not found" and reading them +using `ngx.var` will return `nil`. However, they could be re-assigned via the `ngx.var.VARIABLE` API at any time. + +This directive was first introduced in the `v0.0.4` release. + +[Back to TOC](#directives) + +preread_by_lua_no_postpone +-------------------------- + +**syntax:** *preread_by_lua_no_postpone on|off* + +**context:** *stream* + +Controls whether or not to disable postponing [preread\_by\_lua*](#preread_by_lua_block) directives +to run at the end of the `preread` processing phase. By default, this directive is turned off +and the Lua code is postponed to run at the end of the `preread` phase. + +This directive was first introduced in the `v0.0.4` release. + +[Back to TOC](#directives) + +Nginx API for Lua +----------------- + +Many Lua API functions are ported from ngx_http_lua. Check out the official +manual of ngx_http_lua for more details on these Lua API functions. + +* [ngx.var.VARIABLE](https://github.com/openresty/lua-nginx-module#ngxvarvariable) + +This module fully supports the new variable subsystem inside the Nginx stream core. You may access any +[built-in variables](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#variables) provided by the stream core or +other stream modules. +* [Core constants](https://github.com/openresty/lua-nginx-module#core-constants) + + `ngx.OK`, `ngx.ERROR`, and etc. +* [Nginx log level constants](https://github.com/openresty/lua-nginx-module#nginx-log-level-constants) + + `ngx.ERR`, `ngx.WARN`, and etc. +* [print](https://github.com/openresty/lua-nginx-module#print) +* [ngx.ctx](https://github.com/openresty/lua-nginx-module#ngxctx) +* [ngx.balancer](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md) + +* [ngx.req.socket](https://github.com/openresty/lua-nginx-module#ngxreqsocket) + +Only raw request sockets are supported, for obvious reasons. The `raw` argument value +is ignored and the raw request socket is always returned. Unlike ngx_http_lua, +you can still call output API functions like `ngx.say`, `ngx.print`, and `ngx.flush` +after acquiring the raw request socket via this function. + +When the stream server is in UDP mode, reading from the downstream socket returned by the +`ngx.req.socket` call will only return the content of a single packet. Therefore +the reading call will never block and will return `nil, "no more data"` when all the +data from the datagram has been consumed. However, you may choose to send multiple UDP +packets back to the client using the downstream socket. + +The raw TCP sockets returned by this module will contain the following extra method: + +[Back to TOC](#directives) + +reqsock:receiveany +------------------ + +**syntax:** *data, err = reqsock:receiveany(max)* + +**context:** *content_by_lua*, ngx.timer.*, ssl_certificate_by_lua** + +This method is similar to [tcpsock:receiveany](https://github.com/openresty/lua-nginx-module#tcpsockreceiveany) method + +This method was introduced into `stream-lua-nginx-module` since `v0.0.8`. + +[Back to TOC](#directives) + +tcpsock:shutdown +---------------- + +**syntax:** *ok, err = tcpsock:shutdown("send")* + +**context:** *content_by_lua** + +Shuts down the write part of the request socket, prevents all further writing to the client +and sends TCP FIN, while keeping the reading half open. + +Currently only the `"send"` direction is supported. Using any parameters other than "send" will return +an error. + +If you called any output functions (like [ngx.say](https://github.com/openresty/lua-nginx-module#ngxsay)) +before calling this method, consider use `ngx.flush(true)` to make sure all busy buffers are complely +flushed before shutting down the socket. If any busy buffers were detected, this method will return `nil` +will error message `"socket busy writing"`. + +This feature is particularly useful for protocols that generate a response before actually +finishing consuming all incoming data. Normally, the kernel will send RST to the client when +[tcpsock:close](https://github.com/openresty/lua-nginx-module#tcpsockclose) is called without +emptying the receiving buffer first. Calling this method will allow you to keep reading from +the receiving buffer and prevents RST from being sent. + +You can also use this method to simulate lingering close similar to that +[provided by the ngx_http_core_module](https://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close) +for protocols in need of such behavior. Here is an example: + +```lua +local LINGERING_TIME = 30 -- 30 seconds +local LINGERING_TIMEOUT = 5000 -- 5 seconds + +local ok, err = sock:shutdown("send") +if not ok then + ngx.log(ngx.ERR, "failed to shutdown: ", err) + return +end + +local deadline = ngx.time() + LINGERING_TIME + +sock:settimeouts(nil, nil, LINGERING_TIMEOUT) + +repeat + local data, _, partial = sock:receive(1024) +until (not data and not partial) or ngx.time() >= deadline +``` + +[Back to TOC](#directives) + +reqsock:peek +------------ + +**syntax:** *ok, err = reqsock:peek(size)* + +**context:** *preread_by_lua** + +Peeks into the [preread](https://nginx.org/en/docs/stream/stream_processing.html#preread_phase) +buffer that contains downstream data sent by the client without consuming them. +That is, data returned by this API will still be forwarded upstream in later phases. + +This function takes a single required argument, `size`, which is the number of bytes to be peeked. +Repeated calls to this function always returns data from the beginning of the preread buffer. + +Note that preread phase happens after the TLS handshake. If the stream server was configured with +TLS enabled, the returned data will be in clear text. + +If preread buffer does not have the requested amount of data, then the current Lua thread will +be yielded until more data is available, [`preread_buffer_size`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#preread_buffer_size) +has been exceeded, or [`preread_timeout`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#preread_timeout) +has elapsed. Successful calls always return the requested amounts of data, that is, no partial +data will be returned. + +When [`preread_buffer_size`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#preread_buffer_size) +has been exceeded, the current stream session will be terminated with the +[session status code](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#var_status) `400` +immediately by the stream core module, with error message `"preread buffer full"` that will be printed to the error log. + +When [`preread_timeout`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#preread_timeout) has been exceeded, +the current stream session will be terminated with the +[session status code](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#var_status) `200` immediately by the stream core module. + +In both cases, no further processing on the session is possible (except `log_by_lua*`). The connection will be closed by the +stream core module automatically. + +Note that this API cannot be used if consumption of client data has occurred. For example, after calling +`reqsock:receive`. If such an attempt was made, the Lua error `"attempt to peek on a consumed socket"` will +be thrown. Consuming client data after calling this API is allowed and safe. + +Here is an example of using this API: + +```lua +local sock = assert(ngx.req.socket()) + +local data = assert(sock:peek(1)) -- peek the first 1 byte that contains the length +data = string.byte(data) + +data = assert(sock:peek(data + 1)) -- peek the length + the size byte + +local payload = data:sub(2) -- trim the length byte to get actual payload + +ngx.log(ngx.INFO, "payload is: ", payload) +``` + +This API was first introduced in the `v0.0.6` release. + +[Back to TOC](#directives) + +* [ngx.print](https://github.com/openresty/lua-nginx-module#ngxprint) +* [ngx.say](https://github.com/openresty/lua-nginx-module#ngxsay) +* [ngx.log](https://github.com/openresty/lua-nginx-module#ngxlog) +* [ngx.flush](https://github.com/openresty/lua-nginx-module#ngxflush) + + This call currently ignores the `wait` argument and always wait for all the pending +output to be completely flushed out (to the system socket send buffers). +* [ngx.exit](https://github.com/openresty/lua-nginx-module#ngxexit) +* [ngx.eof](https://github.com/openresty/lua-nginx-module#ngxeof) +* [ngx.sleep](https://github.com/openresty/lua-nginx-module#ngxsleep) +* [ngx.escape_uri](https://github.com/openresty/lua-nginx-module#ngxescape_uri) +* [ngx.unescape_uri](https://github.com/openresty/lua-nginx-module#ngxunescape_uri) +* [ngx.encode_args](https://github.com/openresty/lua-nginx-module#ngxencode_args) +* [ngx.decode_args](https://github.com/openresty/lua-nginx-module#ngxdecode_args) +* [ngx.encode_base64](https://github.com/openresty/lua-nginx-module#ngxencode_base64) +* [ngx.decode_base64](https://github.com/openresty/lua-nginx-module#ngxdecode_base64) +* [ngx.crc32_short](https://github.com/openresty/lua-nginx-module#ngxcrc32_short) +* [ngx.crc32_long](https://github.com/openresty/lua-nginx-module#ngxcrc32_long) +* [ngx.hmac_sha1](https://github.com/openresty/lua-nginx-module#ngxhmac_sha1) +* [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) +* [ngx.quote_sql_str](https://github.com/openresty/lua-nginx-module#ngxquote_sql_str) +* [ngx.today](https://github.com/openresty/lua-nginx-module#ngxtoday) +* [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.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch) +* [ngx.re.find](https://github.com/openresty/lua-nginx-module#ngxrefind) +* [ngx.re.gmatch](https://github.com/openresty/lua-nginx-module#ngxregmatch) +* [ngx.re.sub](https://github.com/openresty/lua-nginx-module#ngxresub) +* [ngx.re.gsub](https://github.com/openresty/lua-nginx-module#ngxregsub) +* [ngx.shared.DICT](https://github.com/openresty/lua-nginx-module#ngxshareddict) +* [ngx.socket.tcp](https://github.com/openresty/lua-nginx-module#ngxsockettcp) +* [ngx.socket.udp](https://github.com/openresty/lua-nginx-module#ngxsocketudp) +* [ngx.socket.connect](https://github.com/openresty/lua-nginx-module#ngxsocketconnect) +* [ngx.get_phase](https://github.com/openresty/lua-nginx-module#ngxget_phase) +* [ngx.thread.spawn](https://github.com/openresty/lua-nginx-module#ngxthreadspawn) +* [ngx.thread.wait](https://github.com/openresty/lua-nginx-module#ngxthreadwait) +* [ngx.thread.kill](https://github.com/openresty/lua-nginx-module#ngxthreadkill) +* [ngx.on_abort](https://github.com/openresty/lua-nginx-module#ngxon_abort) +* [ngx.timer.at](https://github.com/openresty/lua-nginx-module#ngxtimerat) +* [ngx.timer.running_count](https://github.com/openresty/lua-nginx-module#ngxtimerrunning_count) +* [ngx.timer.pending_count](https://github.com/openresty/lua-nginx-module#ngxtimerpending_count) +* [ngx.config.debug](https://github.com/openresty/lua-nginx-module#ngxconfigdebug) +* [ngx.config.subsystem](https://github.com/openresty/lua-nginx-module#ngxconfigsubsystem) + + Always takes the Lua string value `"stream"` in this module. +* [ngx.config.prefix](https://github.com/openresty/lua-nginx-module#ngxconfigprefix) +* [ngx.config.nginx_version](https://github.com/openresty/lua-nginx-module#ngxconfignginx_version) +* [ngx.config.nginx_configure](https://github.com/openresty/lua-nginx-module#ngxconfignginx_configure) +* [ngx.config.ngx_lua_version](https://github.com/openresty/lua-nginx-module#ngxconfigngx_lua_version) +* [ngx.worker.exiting](https://github.com/openresty/lua-nginx-module#ngxworkerexiting) +* [ngx.worker.pid](https://github.com/openresty/lua-nginx-module#ngxworkerpid) +* [ngx.worker.pids](https://github.com/openresty/lua-nginx-module#ngxworkerpids) +* [ngx.worker.count](https://github.com/openresty/lua-nginx-module#ngxworkercount) +* [ngx.worker.id](https://github.com/openresty/lua-nginx-module#ngxworkerid) +* [coroutine.create](https://github.com/openresty/lua-nginx-module#coroutinecreate) +* [coroutine.resume](https://github.com/openresty/lua-nginx-module#coroutineresume) +* [coroutine.yield](https://github.com/openresty/lua-nginx-module#coroutineyield) +* [coroutine.wrap](https://github.com/openresty/lua-nginx-module#coroutinewrap) +* [coroutine.running](https://github.com/openresty/lua-nginx-module#coroutinerunning) +* [coroutine.status](https://github.com/openresty/lua-nginx-module#coroutinestatus) + +[Back to TOC](#table-of-contents) + +TODO +==== + +* Add new directives `access_by_lua_block` and `access_by_lua_file`. +* Add `lua_postpone_output` to emulate the [postpone_output](https://nginx.org/r/postpone_output) directive. + +[Back to TOC](#table-of-contents) + +Nginx Compatibility +=================== + +The latest version of this module is compatible with the following versions of Nginx: + +* 1.19.x (last tested: 1.19.3) +* 1.17.x (last tested: 1.17.8) +* 1.15.x (last tested: 1.15.8) +* 1.13.x (last tested: 1.13.6) + +Nginx cores older than 1.13.6 (exclusive) are *not* tested and may or may not +work. Use at your own risk! + +[Back to TOC](#table-of-contents) + +Installation +============ + +It is *highly* recommended to use [OpenResty releases](https://openresty.org) +which bundle Nginx, ngx_http_lua, ngx_stream_lua, (this module), LuaJIT, as +well as other powerful companion Nginx modules and Lua libraries. + +It is discouraged to build this module with Nginx yourself since it is tricky +to set up exactly right. + +Note that Nginx, LuaJIT, and OpenSSL official releases have various limitations +and long standing bugs that can cause some of this module's features to be +disabled, not work properly, or run slower. Official OpenResty releases are +recommended because they bundle [OpenResty's optimized LuaJIT 2.1 fork](https://github.com/openresty/luajit2) and +[Nginx/OpenSSL +patches](https://github.com/openresty/openresty/tree/master/patches). + +Alternatively, ngx_stream_lua can be manually compiled into Nginx: + +1. LuaJIT can be downloaded from the [latest release of OpenResty's LuaJIT fork](https://github.com/openresty/luajit2/releases). The official LuaJIT 2.x releases are also supported, although performance will be significantly lower for reasons elaborated above +1. Download the latest version of ngx_stream_lua [HERE](https://github.com/openresty/stream-lua-nginx-module/tags) +1. Download the latest supported version of Nginx [HERE](https://nginx.org/) (See [Nginx Compatibility](#nginx-compatibility)) + +Build the source with this module: + +```bash +wget 'https://nginx.org/download/nginx-1.13.6.tar.gz' +tar -xzvf nginx-1.13.6.tar.gz +cd nginx-1.13.6/ + +# tell nginx's build system where to find LuaJIT 2.1: +export LUAJIT_LIB=/path/to/luajit/lib +export LUAJIT_INC=/path/to/luajit/include/luajit-2.1 + +# Here we assume Nginx is to be installed under /opt/nginx/. +./configure --prefix=/opt/nginx \ + --with-ld-opt="-Wl,-rpath,/path/to/luajit-or-lua/lib" \ + --with-stream \ + --with-stream_ssl_module \ + --add-module=/path/to/stream-lua-nginx-module + +# Build and install +make -j4 +make install +``` + +You may use `--without-http` if you do not wish to use this module with the +"http" subsystem. ngx_stream_lua will work perfectly fine without the "http" +subsystem. + +[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) + +Code Repository +=============== + +The code repository of this project is hosted on GitHub at +[openresty/stream-lua-nginx-module](https://github.com/openresty/stream-lua-nginx-module). + +[Back to TOC](#table-of-contents) + +Bugs and Patches +================ + +Please submit bug reports, wishlists, or patches by + +1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/stream-lua-nginx-module/issues), +1. or posting to the [OpenResty community](#community). + +[Back to TOC](#table-of-contents) + +Acknowledgments +=============== + +We appreciate [Kong Inc.](https://konghq.com/) for kindly sponsoring [OpenResty Inc.](https://openresty.com/) on the following +work: +* Compatibility with Nginx core 1.13.3. +* Development of [meta-lua-nginx-module](https://github.com/openresty/meta-lua-nginx-module) +to make code sharing between this module and [lua-nginx-module](https://github.com/openresty/lua-nginx-module) possible. +* `balancer_by_lua_*`, `preread_by_lua_*`, `log_by_lua_*` and `ssl_certby_lua*` phases support. +* [`reqsock:peek`](#reqsockpeek) API support. + +[Back to TOC](#table-of-contents) + +Copyright and License +===================== + +This module is licensed under the BSD license. + +Copyright (C) 2009-2019, by Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. + +Copyright (C) 2009-2016, by Xiaozhe Wang (chaoslawful) . + +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 +======== + +* [ngx_http_lua_module](https://github.com/openresty/lua-nginx-module) +* [ngx_stream_echo_module](https://github.com/openresty/stream-echo-nginx-module) +* [OpenResty](https://openresty.org/) + +[Back to TOC](#table-of-contents) + diff --git a/src/deps/src/stream-lua-nginx-module/config b/src/deps/src/stream-lua-nginx-module/config new file mode 100644 index 000000000..e984f965d --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/config @@ -0,0 +1,455 @@ +ngx_lua_opt_I= +ngx_lua_opt_L= +luajit_ld_opt= + +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs= +ngx_feature_test= + +if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then + # explicitly set LuaJIT paths + + if [ "$NGX_PLATFORM" = win32 ]; then + ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (win32)" + ngx_feature_path="$LUAJIT_INC" + ngx_lua_opt_I="-I$LUAJIT_INC" + ngx_lua_opt_L="-L$LUAJIT_LIB" + + # ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first. + SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" + CC_TEST_FLAGS="$ngx_lua_opt_I $CC_TEST_FLAGS" + SAVED_NGX_TEST_LD_OPT="$NGX_TEST_LD_OPT" + NGX_TEST_LD_OPT="$ngx_lua_opt_L $NGX_TEST_LD_OPT" + + # LuaJIT's win32 build uses the library file name lua51.dll + ngx_feature_libs="$ngx_lua_opt_L -llua51" + + . auto/feature + + # clean up + CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" + NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT" + else + # attempt to link with -ldl, static linking on Linux requires it. + ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env, with -ldl)" + ngx_feature_path="$LUAJIT_INC" + ngx_lua_opt_I="-I$LUAJIT_INC" + ngx_lua_opt_L="-L$LUAJIT_LIB" + luajit_ld_opt="-lm -ldl" + + # ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first + SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" + CC_TEST_FLAGS="$ngx_lua_opt_I $CC_TEST_FLAGS" + SAVED_NGX_TEST_LD_OPT="$NGX_TEST_LD_OPT" + NGX_TEST_LD_OPT="$ngx_lua_opt_L $NGX_TEST_LD_OPT" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R$LUAJIT_LIB $ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt" + else + ngx_feature_libs="$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt" + fi + + . auto/feature + + # clean up + CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" + NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT" + + if [ $ngx_found = no ]; then + # retry without -ldl + ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env)" + ngx_feature_path="$LUAJIT_INC" + ngx_lua_opt_I="-I$LUAJIT_INC" + ngx_lua_opt_L="-L$LUAJIT_LIB" + luajit_ld_opt="-lm" + + # ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first + SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" + CC_TEST_FLAGS="$ngx_lua_opt_I $CC_TEST_FLAGS" + SAVED_NGX_TEST_LD_OPT="$NGX_TEST_LD_OPT" + NGX_TEST_LD_OPT="$ngx_lua_opt_L $NGX_TEST_LD_OPT" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R$LUAJIT_LIB $ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt" + else + ngx_feature_libs="$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt" + fi + + . auto/feature + + # clean up + CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" + NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT" + fi + fi + + if [ $ngx_found = no ]; then + cat << END + $0: error: ngx_stream_lua_module requires the LuaJIT library, but it could not be found where specified (LUAJIT_LIB=$LUAJIT_LIB, LUAJIT_INC=$LUAJIT_INC). +END + exit 1 + fi + + case "$NGX_PLATFORM" in + Darwin:*) + case "$NGX_MACHINE" in + amd64 | x86_64 | i386) + echo "adding extra linking options needed by LuaJIT on $NGX_MACHINE" + luajit_ld_opt="$luajit_ld_opt -pagezero_size 10000 -image_base 100000000" + ngx_feature_libs="$ngx_feature_libs -pagezero_size 10000 -image_base 100000000" + ;; + + *) + ;; + esac + ;; + + *) + ;; + esac +else + # auto-discovery + if [ $ngx_found = no ]; then + # FreeBSD with luajit-2.0 from ports collection + ngx_feature="LuaJIT library in /usr/local/" + ngx_feature_path="/usr/local/include/luajit-2.0" + luajit_ld_opt="-lm" + LUAJIT_LIB="/usr/local/lib" + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lluajit-5.1 -lm" + else + ngx_feature_libs="-L/usr/local/lib -lluajit-5.1 -lm" + fi + . auto/feature + fi + + if [ $ngx_found = no ]; then + # Gentoo with LuaJIT-2.0, try with -ldl + ngx_feature="LuaJIT library in /usr/" + ngx_feature_path="/usr/include/luajit-2.0" + luajit_ld_opt="-lm -ldl" + LUAJIT_LIB="/usr/lib" + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -lluajit-5.1 -ldl" + else + ngx_feature_libs="-L/usr/lib -lm -lluajit-5.1 -ldl" + fi + . auto/feature + fi + + if [ $ngx_found = no ]; then + # Gentoo with LuaJIT 2.0 + ngx_feature="LuaJIT library in /usr/" + ngx_feature_path="/usr/include/luajit-2.0" + luajit_ld_opt="-lm" + LUAJIT_LIB="/usr/lib" + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -lluajit-5.1" + else + ngx_feature_libs="-L/usr/lib -lm -lluajit-5.1" + fi + . auto/feature + fi +fi + +ngx_module_incs= +ngx_module_libs= + +if [ $ngx_found = yes ]; then + # this is a hack to persuade nginx's build system to favor + # the paths set by our user environment + CFLAGS="$ngx_lua_opt_I $CFLAGS" + NGX_LD_OPT="$ngx_lua_opt_L $NGX_LD_OPT" + + ngx_module_incs="$ngx_module_incs $ngx_feature_path" + ngx_module_libs="$ngx_module_libs $ngx_feature_libs" +else + cat << END + $0: error: ngx_stream_lua_module requires the LuaJIT library. +END + exit 1 +fi + +# ---------------------------------------- + +ngx_feature="LuaJIT 2.x" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_test="#if !defined(LUAJIT_VERSION_NUM) || LUAJIT_VERSION_NUM < 20000 + # error unsupported LuaJIT version + #endif + " + +. auto/feature + +if [ $ngx_found = no ]; then + cat << END + $0: error: unsupported LuaJIT version; ngx_stream_lua_module requires LuaJIT 2.x. +END + exit 1 +fi + +# ---------------------------------------- + +ngx_feature="Lua language 5.1" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_test="#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM != 501 + # error unsupported Lua language version + #endif + " + +. auto/feature + +if [ $ngx_found = no ]; then + cat << END + $0: error: unsupported Lua language version; ngx_stream_lua_module requires Lua 5.1. +END + exit 1 +fi + +# ---------------------------------------- + +ngx_feature="LuaJIT has FFI" +ngx_feature_libs="$ngx_module_libs" +ngx_feature_run=no +ngx_feature_incs="#include + #include + #include + " +ngx_feature_test="lua_State *L = luaL_newstate(); + assert(L != NULL); + luaopen_ffi(L); + " + +. auto/feature + +if [ $ngx_found = no ]; then + cat << END + $0: error: unsupported LuaJIT build; ngx_stream_lua_module requires LuaJIT with FFI enabled. +END + exit 1 +fi + +# ---------------------------------------- + +ngx_addon_name=ngx_stream_lua_module +STREAM_LUA_SRCS=" \ + $ngx_addon_dir/src/ngx_stream_lua_api.c \ + $ngx_addon_dir/src/ngx_stream_lua_request.c \ + $ngx_addon_dir/src/ngx_stream_lua_module.c \ + $ngx_addon_dir/src/ngx_stream_lua_directive.c \ + $ngx_addon_dir/src/ngx_stream_lua_lex.c \ + $ngx_addon_dir/src/ngx_stream_lua_contentby.c \ + $ngx_addon_dir/src/ngx_stream_lua_util.c \ + $ngx_addon_dir/src/ngx_stream_lua_cache.c \ + $ngx_addon_dir/src/ngx_stream_lua_clfactory.c \ + $ngx_addon_dir/src/ngx_stream_lua_exception.c \ + $ngx_addon_dir/src/ngx_stream_lua_pcrefix.c \ + $ngx_addon_dir/src/ngx_stream_lua_uthread.c \ + $ngx_addon_dir/src/ngx_stream_lua_coroutine.c \ + $ngx_addon_dir/src/ngx_stream_lua_output.c \ + $ngx_addon_dir/src/ngx_stream_lua_consts.c \ + $ngx_addon_dir/src/ngx_stream_lua_log.c \ + $ngx_addon_dir/src/ngx_stream_lua_time.c \ + $ngx_addon_dir/src/ngx_stream_lua_string.c \ + $ngx_addon_dir/src/ngx_stream_lua_control.c \ + $ngx_addon_dir/src/ngx_stream_lua_sleep.c \ + $ngx_addon_dir/src/ngx_stream_lua_phase.c \ + $ngx_addon_dir/src/ngx_stream_lua_ctx.c \ + $ngx_addon_dir/src/ngx_stream_lua_regex.c \ + $ngx_addon_dir/src/ngx_stream_lua_script.c \ + $ngx_addon_dir/src/ngx_stream_lua_shdict.c \ + $ngx_addon_dir/src/ngx_stream_lua_variable.c \ + $ngx_addon_dir/src/ngx_stream_lua_timer.c \ + $ngx_addon_dir/src/ngx_stream_lua_config.c \ + $ngx_addon_dir/src/ngx_stream_lua_worker.c \ + $ngx_addon_dir/src/ngx_stream_lua_misc.c \ + $ngx_addon_dir/src/ngx_stream_lua_initby.c \ + $ngx_addon_dir/src/ngx_stream_lua_initworkerby.c \ + $ngx_addon_dir/src/ngx_stream_lua_socket_tcp.c \ + $ngx_addon_dir/src/ngx_stream_lua_socket_udp.c \ + $ngx_addon_dir/src/ngx_stream_lua_args.c \ + $ngx_addon_dir/src/ngx_stream_lua_ssl.c \ + $ngx_addon_dir/src/ngx_stream_lua_balancer.c \ + $ngx_addon_dir/src/ngx_stream_lua_logby.c \ + $ngx_addon_dir/src/ngx_stream_lua_prereadby.c \ + $ngx_addon_dir/src/ngx_stream_lua_semaphore.c \ + $ngx_addon_dir/src/ngx_stream_lua_ssl_client_helloby.c \ + $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.c \ + $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.c \ + $ngx_addon_dir/src/ngx_stream_lua_input_filters.c \ + " + +STREAM_LUA_DEPS=" \ + $ngx_addon_dir/src/ddebug.h \ + $ngx_addon_dir/src/ngx_stream_lua_autoconf.h \ + $ngx_addon_dir/src/api/ngx_stream_lua_api.h \ + $ngx_addon_dir/src/ngx_stream_lua_request.h \ + $ngx_addon_dir/src/ngx_stream_lua_common.h \ + $ngx_addon_dir/src/ngx_stream_lua_lex.h \ + $ngx_addon_dir/src/ngx_stream_lua_contentby.h \ + $ngx_addon_dir/src/ngx_stream_lua_util.h \ + $ngx_addon_dir/src/ngx_stream_lua_cache.h \ + $ngx_addon_dir/src/ngx_stream_lua_clfactory.h \ + $ngx_addon_dir/src/ngx_stream_lua_pcrefix.h \ + $ngx_addon_dir/src/ngx_stream_lua_uthread.h \ + $ngx_addon_dir/src/ngx_stream_lua_coroutine.h \ + $ngx_addon_dir/src/ngx_stream_lua_output.h \ + $ngx_addon_dir/src/ngx_stream_lua_consts.h \ + $ngx_addon_dir/src/ngx_stream_lua_log.h \ + $ngx_addon_dir/src/ngx_stream_lua_string.h \ + $ngx_addon_dir/src/ngx_stream_lua_control.h \ + $ngx_addon_dir/src/ngx_stream_lua_sleep.h \ + $ngx_addon_dir/src/ngx_stream_lua_phase.h \ + $ngx_addon_dir/src/ngx_stream_lua_ctx.h \ + $ngx_addon_dir/src/ngx_stream_lua_script.h \ + $ngx_addon_dir/src/ngx_stream_lua_shdict.h \ + $ngx_addon_dir/src/ngx_stream_lua_timer.h \ + $ngx_addon_dir/src/ngx_stream_lua_config.h \ + $ngx_addon_dir/src/api/ngx_stream_lua_api.h \ + $ngx_addon_dir/src/ngx_stream_lua_misc.h \ + $ngx_addon_dir/src/ngx_stream_lua_initby.h \ + $ngx_addon_dir/src/ngx_stream_lua_initworkerby.h \ + $ngx_addon_dir/src/ngx_stream_lua_socket_tcp.h \ + $ngx_addon_dir/src/ngx_stream_lua_socket_udp.h \ + $ngx_addon_dir/src/ngx_stream_lua_args.h \ + $ngx_addon_dir/src/ngx_stream_lua_ssl.h \ + $ngx_addon_dir/src/ngx_stream_lua_balancer.h \ + $ngx_addon_dir/src/ngx_stream_lua_logby.h \ + $ngx_addon_dir/src/ngx_stream_lua_prereadby.h \ + $ngx_addon_dir/src/ngx_stream_lua_semaphore.h \ + $ngx_addon_dir/src/ngx_stream_lua_ssl_client_helloby.h \ + $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.h \ + $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.h \ + $ngx_addon_dir/src/ngx_stream_lua_input_filters.h \ + " + +# ---------------------------------------- + +ngx_feature="export symbols by default (-E)" +ngx_feature_libs="-Wl,-E" +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_test='printf("hello");' + +. auto/feature + +if [ $ngx_found = yes ]; then + CORE_LIBS="-Wl,-E $CORE_LIBS" +fi + +# ---------------------------------------- + +# for Cygwin +ngx_feature="export symbols by default (--export-all-symbols)" +ngx_feature_libs="-Wl,--export-all-symbols" +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_test='printf("hello");' + +. auto/feature + +if [ $ngx_found = yes ]; then + CORE_LIBS="-Wl,--export-all-symbols $CORE_LIBS" +fi + +# ---------------------------------------- + +ngx_feature="SO_PASSCRED" +ngx_feature_libs= +ngx_feature_name="NGX_STREAM_LUA_HAVE_SO_PASSCRED" +ngx_feature_run=no +ngx_feature_incs="#include +#include " +ngx_feature_path= +ngx_feature_test='setsockopt(1, SOL_SOCKET, SO_PASSCRED, NULL, 0);' + +. auto/feature + +# ---------------------------------------- + +ngx_feature="SA_RESTART" +ngx_feature_libs= +ngx_feature_name="NGX_STREAM_LUA_HAVE_SA_RESTART" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_test='struct sigaction act; + act.sa_flags |= SA_RESTART;' + +. auto/feature + +# ---------------------------------------- + +if [ -n "$ngx_module_link" ]; then + ngx_module_type=STREAM + ngx_module_name=$ngx_addon_name + ngx_module_deps="$STREAM_LUA_DEPS" + ngx_module_srcs="$STREAM_LUA_SRCS" + + . auto/module +else + STREAM_MODULES="$STREAM_MODULES $ngx_addon_name" + NGX_ADDON_SRCS="$NGX_ADDON_SRCS $STREAM_LUA_SRCS" + NGX_ADDON_DEPS="$NGX_ADDON_DEPS $STREAM_LUA_DEPS" + + CORE_INCS="$CORE_INCS $ngx_module_incs" + CORE_LIBS="$CORE_LIBS $ngx_module_libs" +fi + +# ---------------------------------------- + +if [ $USE_PCRE = YES -o $PCRE != NONE ] && [ $PCRE != NO -a $PCRE != YES ]; then + # force pcre_version symbol to be required when PCRE is statically linked + case "$NGX_PLATFORM" in + Darwin:*) + ngx_feature="require defined symbols (-u)" + ngx_feature_name= + ngx_feature_path= + ngx_feature_libs="-Wl,-u,_strerror" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_test='printf("hello");' + + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="-Wl,-u,_pcre_version $CORE_LIBS" + fi + ;; + + *) + ngx_feature="require defined symbols (--require-defined)" + ngx_feature_name= + ngx_feature_path= + ngx_feature_libs="-Wl,--require-defined=strerror" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_test='printf("hello");' + + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="-Wl,--require-defined=pcre_version $CORE_LIBS" + fi + ;; + esac +fi + +# ---------------------------------------- + +USE_MD5=YES +USE_SHA1=YES + +#NGX_DTRACE_PROVIDERS="$NGX_DTRACE_PROVIDERS $ngx_addon_dir/dtrace/ngx_lua_provider.d" +#NGX_TAPSET_SRCS="$NGX_TAPSET_SRCS $ngx_addon_dir/tapset/ngx_lua.stp" + +CORE_INCS="$CORE_INCS $ngx_addon_dir/src/api" + +echo "/* DO NOT EDIT! This file was automatically generated by config */" > "$ngx_addon_dir/src/ngx_stream_lua_autoconf.h" diff --git a/src/deps/src/stream-lua-nginx-module/src/api/ngx_stream_lua_api.h b/src/deps/src/stream-lua-nginx-module/src/api/ngx_stream_lua_api.h new file mode 100644 index 000000000..23826d27f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/api/ngx_stream_lua_api.h @@ -0,0 +1,72 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/api/ngx_subsys_lua_api.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_API_H_INCLUDED_ +#define _NGX_STREAM_LUA_API_H_INCLUDED_ + + +#include +#include + + + + +#include +#include + + +/* Public API for other Nginx modules */ + + +#define ngx_stream_lua_version 12 + + +typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + lua_Number n; /* number */ + ngx_str_t s; /* string */ + } value; + +} ngx_stream_lua_value_t; + + +typedef struct { + int len; + /* this padding hole on 64-bit systems is expected */ + u_char *data; +} ngx_stream_lua_ffi_str_t; + + +lua_State *ngx_stream_lua_get_global_state(ngx_conf_t *cf); + + +ngx_int_t ngx_stream_lua_add_package_preload(ngx_conf_t *cf, + const char *package, lua_CFunction func); + +ngx_int_t ngx_stream_lua_shared_dict_get(ngx_shm_zone_t *shm_zone, + u_char *key_data, size_t key_len, ngx_stream_lua_value_t *value); + +ngx_shm_zone_t *ngx_stream_lua_find_zone(u_char *name_data, + size_t name_len); + +ngx_shm_zone_t *ngx_stream_lua_shared_memory_add(ngx_conf_t *cf, + ngx_str_t *name, size_t size, void *tag); + + +#endif /* _NGX_STREAM_LUA_API_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ddebug.h b/src/deps/src/stream-lua-nginx-module/src/ddebug.h new file mode 100644 index 000000000..6ee0cb5a7 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ddebug.h @@ -0,0 +1,93 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ddebug.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _DDEBUG_H_INCLUDED_ +#define _DDEBUG_H_INCLUDED_ + + +#include +#include +#include + + +#if defined(DDEBUG) && (DDEBUG) + +# if (NGX_HAVE_VARIADIC_MACROS) + +# define dd(...) fprintf(stderr, "lua *** %s: ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) + +# else + +#include +#include + +#include + +static ngx_inline void +dd(const char *fmt, ...) { +} + +# endif + +#else + +# if (NGX_HAVE_VARIADIC_MACROS) + +# define dd(...) + +# else + +#include + +static ngx_inline void +dd(const char *fmt, ...) { +} + +# endif + +#endif + +#if defined(DDEBUG) && (DDEBUG) + +#define dd_check_read_event_handler(r) \ + dd("r->read_event_handler = %s", \ + r->read_event_handler == ngx_http_block_reading ? \ + "ngx_http_block_reading" : \ + r->read_event_handler == ngx_http_test_reading ? \ + "ngx_http_test_reading" : \ + r->read_event_handler == ngx_http_request_empty_handler ? \ + "ngx_http_request_empty_handler" : "UNKNOWN") + +#define dd_check_write_event_handler(r) \ + dd("r->write_event_handler = %s", \ + r->write_event_handler == ngx_http_handler ? \ + "ngx_http_handler" : \ + r->write_event_handler == ngx_http_core_run_phases ? \ + "ngx_http_core_run_phases" : \ + r->write_event_handler == ngx_http_request_empty_handler ? \ + "ngx_http_request_empty_handler" : "UNKNOWN") + +#else + +#define dd_check_read_event_handler(r) +#define dd_check_write_event_handler(r) + +#endif + + +#endif /* _DDEBUG_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_api.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_api.c new file mode 100644 index 000000000..cf01b365a --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_api.c @@ -0,0 +1,221 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_api.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_common.h" +#include "api/ngx_stream_lua_api.h" +#include "ngx_stream_lua_shdict.h" +#include "ngx_stream_lua_util.h" + + +lua_State * +ngx_stream_lua_get_global_state(ngx_conf_t *cf) +{ + ngx_stream_lua_main_conf_t *lmcf; + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module); + + return lmcf->lua; +} + + + + +static ngx_int_t ngx_stream_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, + void *data); + + +ngx_int_t +ngx_stream_lua_add_package_preload(ngx_conf_t *cf, const char *package, + lua_CFunction func) +{ + lua_State *L; + + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_preload_hook_t *hook; + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module); + + L = lmcf->lua; + + if (L) { + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + lua_pushcfunction(L, func); + lua_setfield(L, -2, package); + lua_pop(L, 2); + } + + /* we always register preload_hooks since we always create new Lua VMs + * when lua code cache is off. */ + + if (lmcf->preload_hooks == NULL) { + lmcf->preload_hooks = + ngx_array_create(cf->pool, 4, + sizeof(ngx_stream_lua_preload_hook_t)); + + if (lmcf->preload_hooks == NULL) { + return NGX_ERROR; + } + } + + hook = ngx_array_push(lmcf->preload_hooks); + if (hook == NULL) { + return NGX_ERROR; + } + + hook->package = (u_char *) package; + hook->loader = func; + + return NGX_OK; +} + + +ngx_shm_zone_t * +ngx_stream_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, + size_t size, void *tag) +{ + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_shm_zone_ctx_t *ctx; + + ngx_shm_zone_t **zp; + ngx_shm_zone_t *zone; + ngx_int_t n; + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module); + if (lmcf == NULL) { + return NULL; + } + + if (lmcf->shm_zones == NULL) { + lmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t)); + if (lmcf->shm_zones == NULL) { + return NULL; + } + + if (ngx_array_init(lmcf->shm_zones, cf->pool, 2, + sizeof(ngx_shm_zone_t *)) + != NGX_OK) + { + return NULL; + } + } + + zone = ngx_shared_memory_add(cf, name, (size_t) size, tag); + if (zone == NULL) { + return NULL; + } + + if (zone->data) { + ctx = (ngx_stream_lua_shm_zone_ctx_t *) zone->data; + return &ctx->zone; + } + + n = sizeof(ngx_stream_lua_shm_zone_ctx_t); + + ctx = ngx_pcalloc(cf->pool, n); + if (ctx == NULL) { + return NULL; + } + + ctx->lmcf = lmcf; + ctx->log = &cf->cycle->new_log; + ctx->cycle = cf->cycle; + + ngx_memcpy(&ctx->zone, zone, sizeof(ngx_shm_zone_t)); + + zp = ngx_array_push(lmcf->shm_zones); + if (zp == NULL) { + return NULL; + } + + *zp = zone; + + /* set zone init */ + zone->init = ngx_stream_lua_shared_memory_init; + zone->data = ctx; + + lmcf->requires_shm = 1; + + return &ctx->zone; +} + + +static ngx_int_t +ngx_stream_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_stream_lua_shm_zone_ctx_t *octx = data; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_shm_zone_ctx_t *ctx; + + ngx_shm_zone_t *ozone; + void *odata; + ngx_int_t rc; + volatile ngx_cycle_t *saved_cycle; + ngx_shm_zone_t *zone; + + ctx = (ngx_stream_lua_shm_zone_ctx_t *) shm_zone->data; + zone = &ctx->zone; + + odata = NULL; + if (octx) { + ozone = &octx->zone; + odata = ozone->data; + } + + zone->shm = shm_zone->shm; +#if defined(nginx_version) && nginx_version >= 1009000 + zone->noreuse = shm_zone->noreuse; +#endif + + if (zone->init(zone, odata) != NGX_OK) { + return NGX_ERROR; + } + + dd("get lmcf"); + + lmcf = ctx->lmcf; + if (lmcf == NULL) { + return NGX_ERROR; + } + + dd("lmcf->lua: %p", lmcf->lua); + + lmcf->shm_zones_inited++; + + if (lmcf->shm_zones_inited == lmcf->shm_zones->nelts + && lmcf->init_handler && !ngx_test_config) + { + saved_cycle = ngx_cycle; + ngx_cycle = ctx->cycle; + + rc = lmcf->init_handler(ctx->log, lmcf, lmcf->lua); + + ngx_cycle = saved_cycle; + + if (rc != NGX_OK) { + /* an error happened */ + return NGX_ERROR; + } + } + + return NGX_OK; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_args.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_args.c new file mode 100644 index 000000000..0cdaef1db --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_args.c @@ -0,0 +1,161 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_args.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_args.h" +#include "ngx_stream_lua_util.h" + + + + +int +ngx_stream_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max) +{ + u_char *p, *q; + u_char *src, *dst; + unsigned parsing_value; + size_t len; + int count = 0; + int top; + + top = lua_gettop(L); + + p = buf; + + parsing_value = 0; + q = p; + + while (p != last) { + if (*p == '=' && ! parsing_value) { + /* key data is between p and q */ + + src = q; dst = q; + + ngx_stream_lua_unescape_uri(&dst, &src, p - q, + NGX_UNESCAPE_URI_COMPONENT); + + dd("pushing key %.*s", (int) (dst - q), q); + + /* push the key */ + lua_pushlstring(L, (char *) q, dst - q); + + /* skip the current '=' char */ + p++; + + q = p; + parsing_value = 1; + + } else if (*p == '&') { + /* reached the end of a key or a value, just save it */ + src = q; dst = q; + + ngx_stream_lua_unescape_uri(&dst, &src, p - q, + NGX_UNESCAPE_URI_COMPONENT); + + dd("pushing key or value %.*s", (int) (dst - q), q); + + /* push the value or key */ + lua_pushlstring(L, (char *) q, dst - q); + + /* skip the current '&' char */ + p++; + + q = p; + + if (parsing_value) { + /* end of the current pair's value */ + parsing_value = 0; + + } else { + /* the current parsing pair takes no value, + * just push the value "true" */ + dd("pushing boolean true"); + + lua_pushboolean(L, 1); + } + + (void) lua_tolstring(L, -2, &len); + + if (len == 0) { + /* ignore empty string key pairs */ + dd("popping key and value..."); + lua_pop(L, 2); + + } else { + dd("setting table..."); + ngx_stream_lua_set_multi_value_table(L, top); + } + + if (max > 0 && ++count == max) { + lua_pushliteral(L, "truncated"); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua hit query args limit %d", + max); + return 2; + } + + } else { + p++; + } + } + + if (p != q || parsing_value) { + src = q; dst = q; + + ngx_stream_lua_unescape_uri(&dst, &src, p - q, + NGX_UNESCAPE_URI_COMPONENT); + + dd("pushing key or value %.*s", (int) (dst - q), q); + + lua_pushlstring(L, (char *) q, dst - q); + + if (!parsing_value) { + dd("pushing boolean true..."); + lua_pushboolean(L, 1); + } + + (void) lua_tolstring(L, -2, &len); + + if (len == 0) { + /* ignore empty string key pairs */ + dd("popping key and value..."); + lua_pop(L, 2); + + } else { + dd("setting table..."); + ngx_stream_lua_set_multi_value_table(L, top); + } + } + + dd("gettop: %d", lua_gettop(L)); + dd("type: %s", lua_typename(L, lua_type(L, 1))); + + if (lua_gettop(L) != top) { + return luaL_error(L, "internal error: stack in bad state"); + } + + return 1; +} + + + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_args.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_args.h new file mode 100644 index 000000000..862083672 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_args.h @@ -0,0 +1,28 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_args.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_ARGS_H_INCLUDED_ +#define _NGX_STREAM_LUA_ARGS_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + + +int ngx_stream_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max); + + +#endif /* _NGX_STREAM_LUA_ARGS_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_balancer.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_balancer.c new file mode 100644 index 000000000..aafe0eea5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_balancer.c @@ -0,0 +1,755 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_balancer.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_balancer.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_directive.h" + + +struct ngx_stream_lua_balancer_peer_data_s { + /* the round robin data must be first */ + ngx_stream_upstream_rr_peer_data_t rrp; + + ngx_stream_lua_srv_conf_t *conf; + ngx_stream_lua_request_t *request; + + ngx_uint_t more_tries; + ngx_uint_t total_tries; + + struct sockaddr *sockaddr; + socklen_t socklen; + + ngx_str_t *host; + in_port_t port; + + int last_peer_state; + +}; + + +#if (NGX_STREAM_SSL && HAVE_NGX_STREAM_BALANCER_EXPORT_PATCH) +static ngx_int_t ngx_stream_lua_balancer_set_session(ngx_peer_connection_t *pc, + void *data); +static void ngx_stream_lua_balancer_save_session(ngx_peer_connection_t *pc, + void *data); +#endif +static ngx_int_t ngx_stream_lua_balancer_init(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us); + + +static ngx_int_t ngx_stream_lua_balancer_init_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us); + + +#if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH) +ngx_uint_t +ngx_stream_proxy_get_next_upstream_tries(ngx_stream_session_t *s); +#endif + + +static ngx_int_t ngx_stream_lua_balancer_get_peer(ngx_peer_connection_t *pc, + void *data); +static ngx_int_t ngx_stream_lua_balancer_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); +void ngx_stream_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state); + + +ngx_int_t +ngx_stream_lua_balancer_handler_file(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, + lscf->balancer.src.data, + lscf->balancer.src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_balancer_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_balancer_handler_inline(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + lscf->balancer.src.data, + lscf->balancer.src.len, + lscf->balancer.src_key, + "=balancer_by_lua"); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_balancer_by_chunk(L, r); +} + + +char * +ngx_stream_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_balancer_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + u_char *p; + u_char *name; + ngx_str_t *value; + + ngx_stream_lua_srv_conf_t *lscf = conf; + ngx_stream_upstream_srv_conf_t *uscf; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->balancer.handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + lscf->balancer.handler = (ngx_stream_lua_srv_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_stream_lua_balancer_handler_file) { + /* Lua code in an external file */ + + name = ngx_stream_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lscf->balancer.src.data = name; + lscf->balancer.src.len = ngx_strlen(name); + + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->balancer.src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, + NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + /* inlined Lua code */ + + lscf->balancer.src = value[1]; + + p = ngx_palloc(cf->pool, + sizeof("balancer_by_lua") + NGX_STREAM_LUA_INLINE_KEY_LEN); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->balancer.src_key = p; + + p = ngx_copy(p, "balancer_by_lua", sizeof("balancer_by_lua") - 1); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + + uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); + + if (uscf->peer.init_upstream) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "load balancing method redefined"); + } + + uscf->peer.init_upstream = ngx_stream_lua_balancer_init; + + uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_FAILS + |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT + |NGX_STREAM_UPSTREAM_DOWN; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_lua_balancer_init(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us) +{ + if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + /* this callback is called upon individual requests */ + us->peer.init = ngx_stream_lua_balancer_init_peer; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_balancer_init_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_stream_lua_srv_conf_t *bcf; + ngx_stream_lua_balancer_peer_data_t *bp; + ngx_stream_upstream_t *upstream; + + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(s); + + if (ctx == NULL) { + return NGX_ERROR; + } + } + + r = ctx->request; + + upstream = s->upstream; + + + bp = ngx_pcalloc(r->pool, sizeof(ngx_stream_lua_balancer_peer_data_t)); + if (bp == NULL) { + return NGX_ERROR; + } + + upstream->peer.data = &bp->rrp; + + if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) { + return NGX_ERROR; + } + + upstream->peer.get = ngx_stream_lua_balancer_get_peer; + upstream->peer.free = ngx_stream_lua_balancer_free_peer; + + upstream->peer.notify = NULL; + +#if (NGX_STREAM_SSL && HAVE_NGX_STREAM_BALANCER_EXPORT_PATCH) + upstream->peer.set_session = ngx_stream_lua_balancer_set_session; + upstream->peer.save_session = ngx_stream_lua_balancer_save_session; +#endif + + bcf = ngx_stream_conf_upstream_srv_conf(us, ngx_stream_lua_module); + + bp->conf = bcf; + bp->request = r; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data) +{ + lua_State *L; + ngx_int_t rc; + ngx_stream_lua_request_t *r; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_balancer_peer_data_t *bp = data; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "lua balancer peer, tries: %ui", pc->tries); + + lscf = bp->conf; + + r = bp->request; + + ngx_stream_lua_assert(lscf->balancer.handler && r); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx == NULL) { + + ctx = ngx_stream_lua_create_ctx(r->session); + + if (ctx == NULL) { + return NGX_ERROR; + } + + L = ngx_stream_lua_get_lua_vm(r, ctx); + + } else { + L = ngx_stream_lua_get_lua_vm(r, ctx); + + dd("reset ctx"); + ngx_stream_lua_reset_ctx(r, L, ctx); + } + + ctx->context = NGX_STREAM_LUA_CONTEXT_BALANCER; + + bp->sockaddr = NULL; + bp->socklen = 0; + bp->more_tries = 0; + bp->total_tries++; + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + /* balancer_by_lua does not support yielding and + * there cannot be any conflicts among concurrent requests, + * thus it is safe to store the peer data in the main conf. + */ + lmcf->balancer_peer_data = bp; + + rc = lscf->balancer.handler(r, lscf, L); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (ctx->exited && ctx->exit_code != NGX_OK) { + rc = ctx->exit_code; + if (rc == NGX_ERROR + || rc == NGX_BUSY + || rc == NGX_DECLINED +#ifdef HAVE_BALANCER_STATUS_CODE_PATCH + || rc >= NGX_STREAM_SPECIAL_RESPONSE +#endif + ) { + return rc; + } + + if (rc > NGX_OK) { + return NGX_ERROR; + } + } + + if (bp->sockaddr && bp->socklen) { + pc->sockaddr = bp->sockaddr; + pc->socklen = bp->socklen; + pc->cached = 0; + pc->connection = NULL; + pc->name = bp->host; + + bp->rrp.peers->single = 0; + + if (bp->more_tries) { + r->session->upstream->peer.tries += bp->more_tries; + } + + dd("tries: %d", (int) r->session->upstream->peer.tries); + + return NGX_OK; + } + + return ngx_stream_upstream_get_round_robin_peer(pc, &bp->rrp); +} + + +static ngx_int_t +ngx_stream_lua_balancer_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) +{ + u_char *err_msg; + size_t len; + ngx_int_t rc; + + /* init nginx context in Lua VM */ + ngx_stream_lua_set_req(L, r); + +#ifndef OPENRESTY_LUAJIT + ngx_stream_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */); + + /* {{{ make new env inheriting main thread's globals table */ + lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */ + ngx_stream_lua_get_globals_table(L); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */ + /* }}} */ + + lua_setfenv(L, -2); /* set new running env for the code closure */ +#endif /* OPENRESTY_LUAJIT */ + + lua_pushcfunction(L, ngx_stream_lua_traceback); + lua_insert(L, 1); /* put it under chunk and args */ + + /* protected call user code */ + rc = lua_pcall(L, 0, 1, 1); + + lua_remove(L, 1); /* remove traceback function */ + + dd("rc == %d", (int) rc); + + if (rc != 0) { + /* error occurred when running loaded code */ + err_msg = (u_char *) lua_tolstring(L, -1, &len); + + if (err_msg == NULL) { + err_msg = (u_char *) "unknown reason"; + len = sizeof("unknown reason") - 1; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to run balancer_by_lua*: %*s", len, err_msg); + + lua_settop(L, 0); /* clear remaining elems on stack */ + + return NGX_ERROR; + } + + lua_settop(L, 0); /* clear remaining elems on stack */ + return rc; +} + + +void +ngx_stream_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state) +{ + ngx_stream_lua_balancer_peer_data_t *bp = data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua balancer free peer, tries: %ui", pc->tries); + + if (bp->sockaddr && bp->socklen) { + bp->last_peer_state = (int) state; + + if (pc->tries) { + pc->tries--; + } + + return; + } + + /* fallback */ + + ngx_stream_upstream_free_round_robin_peer(pc, data, state); +} + + +#if (NGX_STREAM_SSL && HAVE_NGX_STREAM_BALANCER_EXPORT_PATCH) +static ngx_int_t +ngx_stream_lua_balancer_set_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_lua_balancer_peer_data_t *bp = data; + + if (bp->sockaddr && bp->socklen) { + /* TODO */ + return NGX_OK; + } + + return ngx_stream_upstream_set_round_robin_peer_session(pc, &bp->rrp); +} + + +static void +ngx_stream_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_lua_balancer_peer_data_t *bp = data; + + if (bp->sockaddr && bp->socklen) { + /* TODO */ + return; + } + + ngx_stream_upstream_save_round_robin_peer_session(pc, &bp->rrp); + return; +} + +#endif + + +int +ngx_stream_lua_ffi_balancer_set_current_peer(ngx_stream_lua_request_t *r, + const u_char *addr, size_t addr_len, int port, char **err) +{ + ngx_url_t url; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_upstream_t *u; + + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_balancer_peer_data_t *bp; + + if (r == NULL) { + *err = "no request found"; + return NGX_ERROR; + } + + u = r->session->upstream; + + if (u == NULL) { + *err = "no upstream found"; + return NGX_ERROR; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + *err = "no ctx found"; + return NGX_ERROR; + } + + if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + /* we cannot read r->upstream->peer.data here directly because + * it could be overridden by other modules like + * ngx_stream_upstream_keepalive_module. + */ + bp = lmcf->balancer_peer_data; + if (bp == NULL) { + *err = "no upstream peer data found"; + return NGX_ERROR; + } + + ngx_memzero(&url, sizeof(ngx_url_t)); + + url.url.data = ngx_palloc(r->pool, addr_len); + if (url.url.data == NULL) { + *err = "no memory"; + return NGX_ERROR; + } + + ngx_memcpy(url.url.data, addr, addr_len); + + url.url.len = addr_len; + url.default_port = (in_port_t) port; + url.uri_part = 0; + url.no_resolve = 1; + + if (ngx_parse_url(r->pool, &url) != NGX_OK) { + if (url.err) { + *err = url.err; + } + + return NGX_ERROR; + } + + if (url.addrs && url.addrs[0].sockaddr) { + bp->sockaddr = url.addrs[0].sockaddr; + bp->socklen = url.addrs[0].socklen; + bp->host = &url.addrs[0].name; + + } else { + *err = "no host allowed"; + return NGX_ERROR; + } + + return NGX_OK; +} + + +#if (NGX_STREAM_HAVE_PROXY_TIMEOUT_FIELDS_PATCH) +int +ngx_stream_lua_ffi_balancer_set_timeouts(ngx_stream_lua_request_t *r, + long connect_timeout, long timeout, + char **err) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_proxy_ctx_t *pctx; + + if (r == NULL) { + *err = "no request found"; + return NGX_ERROR; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + *err = "no ctx found"; + return NGX_ERROR; + } + + if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + pctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_proxy_module); + ngx_stream_lua_assert(pctx != NULL); + if (pctx == NULL) { + *err = "no proxy ctx found"; + return NGX_ERROR; + } + + if (connect_timeout > 0) { + pctx->connect_timeout = connect_timeout; + } + + if (timeout > 0) { + pctx->timeout = timeout; + } + + return NGX_OK; +} +#else +int +ngx_stream_lua_ffi_balancer_set_timeouts(ngx_stream_lua_request_t *r, + long connect_timeout, long timeout, + char **err) +{ + *err = "required Nginx patch not present, API disabled"; + return NGX_ERROR; +} +#endif + + +int +ngx_stream_lua_ffi_balancer_set_more_tries(ngx_stream_lua_request_t *r, + int count, char **err) +{ +#if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH) + ngx_uint_t max_tries, total; +#endif + ngx_stream_lua_ctx_t *ctx; + ngx_stream_upstream_t *u; + + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_balancer_peer_data_t *bp; + + if (r == NULL) { + *err = "no request found"; + return NGX_ERROR; + } + + u = r->session->upstream; + + if (u == NULL) { + *err = "no upstream found"; + return NGX_ERROR; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + *err = "no ctx found"; + return NGX_ERROR; + } + + if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + bp = lmcf->balancer_peer_data; + if (bp == NULL) { + *err = "no upstream peer data found"; + return NGX_ERROR; + } + +#if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH) + max_tries = ngx_stream_proxy_get_next_upstream_tries(r->session); + total = bp->total_tries + u->peer.tries - 1; + + if (max_tries && total + count > max_tries) { + count = max_tries - total; + *err = "reduced tries due to limit"; + + } else { + *err = NULL; + } +#else + *err = NULL; +#endif + + bp->more_tries = count; + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_balancer_get_last_failure(ngx_stream_lua_request_t *r, + int *status, char **err) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_upstream_t *u; + ngx_stream_lua_balancer_peer_data_t *bp; + ngx_stream_lua_main_conf_t *lmcf; + + if (r == NULL) { + *err = "no request found"; + return NGX_ERROR; + } + + u = r->session->upstream; + + if (u == NULL) { + *err = "no upstream found"; + return NGX_ERROR; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + *err = "no ctx found"; + return NGX_ERROR; + } + + if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + bp = lmcf->balancer_peer_data; + if (bp == NULL) { + *err = "no upstream peer data found"; + return NGX_ERROR; + } + + *status = 0; + + return bp->last_peer_state; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_balancer.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_balancer.h new file mode 100644 index 000000000..fde876629 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_balancer.h @@ -0,0 +1,35 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_balancer.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_BALANCER_H_INCLUDED_ +#define _NGX_STREAM_LUA_BALANCER_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_balancer_handler_inline(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +ngx_int_t ngx_stream_lua_balancer_handler_file(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +char *ngx_stream_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +char *ngx_stream_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +#endif /* _NGX_STREAM_LUA_BALANCER_H_INCLUDED_ */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_cache.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_cache.c new file mode 100644 index 000000000..4c5713734 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_cache.c @@ -0,0 +1,319 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_cache.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include +#include +#include "ngx_stream_lua_common.h" +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_clfactory.h" +#include "ngx_stream_lua_util.h" + + +/** + * Find code chunk associated with the given key in code cache, + * and push it to the top of Lua stack if found. + * + * Stack layout before call: + * | ... | <- top + * + * Stack layout after call: + * | code chunk | <- top + * | ... | + * + * */ +static ngx_int_t +ngx_stream_lua_cache_load_code(ngx_log_t *log, lua_State *L, + const char *key) +{ +#ifndef OPENRESTY_LUAJIT + int rc; + u_char *err; +#endif + + /* get code cache table */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + code_cache_key)); + lua_rawget(L, LUA_REGISTRYINDEX); /* sp++ */ + + dd("Code cache table to load: %p", lua_topointer(L, -1)); + + if (!lua_istable(L, -1)) { + dd("Error: code cache table to load did not exist!!"); + return NGX_ERROR; + } + + lua_getfield(L, -1, key); /* sp++ */ + + if (lua_isfunction(L, -1)) { +#ifdef OPENRESTY_LUAJIT + lua_remove(L, -2); /* sp-- */ + return NGX_OK; +#else + /* call closure factory to gen new closure */ + rc = lua_pcall(L, 0, 1, 0); + if (rc == 0) { + /* remove cache table from stack, leave code chunk at + * top of stack */ + lua_remove(L, -2); /* sp-- */ + return NGX_OK; + } + + if (lua_isstring(L, -1)) { + err = (u_char *) lua_tostring(L, -1); + + } else { + err = (u_char *) "unknown error"; + } + + ngx_log_error(NGX_LOG_ERR, log, 0, + "lua: failed to run factory at key \"%s\": %s", + key, err); + lua_pop(L, 2); + return NGX_ERROR; +#endif /* OPENRESTY_LUAJIT */ + } + + dd("Value associated with given key in code cache table is not code " + "chunk: stack top=%d, top value type=%s\n", + lua_gettop(L), luaL_typename(L, -1)); + + /* remove cache table and value from stack */ + lua_pop(L, 2); /* sp-=2 */ + + return NGX_DECLINED; +} + + +/** + * Store the closure factory at the top of Lua stack to code cache, and + * associate it with the given key. Then generate new closure. + * + * Stack layout before call: + * | code factory | <- top + * | ... | + * + * Stack layout after call: + * | code chunk | <- top + * | ... | + * + * */ +static ngx_int_t +ngx_stream_lua_cache_store_code(lua_State *L, const char *key) +{ +#ifndef OPENRESTY_LUAJIT + int rc; +#endif + + /* get code cache table */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + code_cache_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + dd("Code cache table to store: %p", lua_topointer(L, -1)); + + if (!lua_istable(L, -1)) { + dd("Error: code cache table to load did not exist!!"); + return NGX_ERROR; + } + + lua_pushvalue(L, -2); /* closure cache closure */ + lua_setfield(L, -2, key); /* closure cache */ + + /* remove cache table, leave closure factory at top of stack */ + lua_pop(L, 1); /* closure */ + +#ifndef OPENRESTY_LUAJIT + /* call closure factory to generate new closure */ + rc = lua_pcall(L, 0, 1, 0); + if (rc != 0) { + dd("Error: failed to call closure factory!!"); + return NGX_ERROR; + } +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L, + const u_char *src, size_t src_len, const u_char *cache_key, + const char *name) +{ + int n; + ngx_int_t rc; + const char *err = NULL; + + n = lua_gettop(L); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "looking up Lua code cache with key '%s'", cache_key); + + rc = ngx_stream_lua_cache_load_code(log, L, (char *) cache_key); + if (rc == NGX_OK) { + /* code chunk loaded from cache, sp++ */ + dd("Code cache hit! cache key='%s', stack top=%d, script='%.*s'", + cache_key, lua_gettop(L), (int) src_len, src); + return NGX_OK; + } + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + /* rc == NGX_DECLINED */ + + dd("Code cache missed! cache key='%s', stack top=%d, script='%.*s'", + cache_key, lua_gettop(L), (int) src_len, src); + + /* load closure factory of inline script to the top of lua stack, sp++ */ + rc = ngx_stream_lua_clfactory_loadbuffer(L, (char *) src, src_len, name); + + if (rc != 0) { + /* Oops! error occurred when loading Lua script */ + if (rc == LUA_ERRMEM) { + err = "memory allocation error"; + + } else { + if (lua_isstring(L, -1)) { + err = lua_tostring(L, -1); + + } else { + err = "unknown error"; + } + } + + goto error; + } + + /* store closure factory and gen new closure at the top of lua stack to + * code cache */ + rc = ngx_stream_lua_cache_store_code(L, (char *) cache_key); + if (rc != NGX_OK) { + err = "fail to generate new closure from the closure factory"; + goto error; + } + + return NGX_OK; + +error: + + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to load inlined Lua code: %s", err); + lua_settop(L, n); + return NGX_ERROR; +} + + +ngx_int_t +ngx_stream_lua_cache_loadfile(ngx_log_t *log, lua_State *L, + const u_char *script, const u_char *cache_key) +{ + int n; + ngx_int_t rc, errcode = NGX_ERROR; + u_char *p; + u_char buf[NGX_STREAM_LUA_FILE_KEY_LEN + 1]; + const char *err = NULL; + + n = lua_gettop(L); + + /* calculate digest of script file path */ + if (cache_key == NULL) { + dd("CACHE file key not pre-calculated...calculating"); + p = ngx_copy(buf, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN); + + p = ngx_stream_lua_digest_hex(p, script, ngx_strlen(script)); + + *p = '\0'; + cache_key = buf; + + } else { + dd("CACHE file key already pre-calculated"); + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "looking up Lua code cache with key '%s'", cache_key); + + rc = ngx_stream_lua_cache_load_code(log, L, (char *) cache_key); + if (rc == NGX_OK) { + /* code chunk loaded from cache, sp++ */ + dd("Code cache hit! cache key='%s', stack top=%d, file path='%s'", + cache_key, lua_gettop(L), script); + return NGX_OK; + } + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + /* rc == NGX_DECLINED */ + + dd("Code cache missed! cache key='%s', stack top=%d, file path='%s'", + cache_key, lua_gettop(L), script); + + /* load closure factory of script file to the top of lua stack, sp++ */ + rc = ngx_stream_lua_clfactory_loadfile(L, (char *) script); + + dd("loadfile returns %d (%d)", (int) rc, LUA_ERRFILE); + + if (rc != 0) { + /* Oops! error occurred when loading Lua script */ + switch (rc) { + case LUA_ERRMEM: + err = "memory allocation error"; + break; + + case LUA_ERRFILE: + errcode = NGX_STREAM_INTERNAL_SERVER_ERROR; + /* fall through */ + + default: + if (lua_isstring(L, -1)) { + err = lua_tostring(L, -1); + + } else { + err = "unknown error"; + } + } + + goto error; + } + + /* store closure factory and gen new closure at the top of lua stack + * to code cache */ + rc = ngx_stream_lua_cache_store_code(L, (char *) cache_key); + if (rc != NGX_OK) { + err = "fail to generate new closure from the closure factory"; + goto error; + } + + return NGX_OK; + +error: + + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to load external Lua file \"%s\": %s", script, err); + + lua_settop(L, n); + return errcode; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_cache.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_cache.h new file mode 100644 index 000000000..5c3c73b3b --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_cache.h @@ -0,0 +1,32 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_cache.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CACHE_H_INCLUDED_ +#define _NGX_STREAM_LUA_CACHE_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L, + const u_char *src, size_t src_len, const u_char *cache_key, + const char *name); +ngx_int_t ngx_stream_lua_cache_loadfile(ngx_log_t *log, lua_State *L, + const u_char *script, const u_char *cache_key); + + +#endif /* _NGX_STREAM_LUA_CACHE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_clfactory.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_clfactory.c new file mode 100644 index 000000000..acecc6f4b --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_clfactory.c @@ -0,0 +1,937 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_clfactory.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include +#include "ngx_stream_lua_clfactory.h" + + +#ifndef OPENRESTY_LUAJIT +#define CLFACTORY_BEGIN_CODE "return function() " +#define CLFACTORY_BEGIN_SIZE (sizeof(CLFACTORY_BEGIN_CODE) - 1) + +#define CLFACTORY_END_CODE "\nend" +#define CLFACTORY_END_SIZE (sizeof(CLFACTORY_END_CODE) - 1) +#endif + + +/* + * taken from chaoslawful: + * Lua bytecode header Luajit bytecode header + * -------------- -------------- + * | \033Lua | 0-3 | \033LJ | 0-2 + * -------------- -------------- + * | LuaC | 4 | bytecode | 3 + * | Version | | version | + * -------------- -------------- + * | LuaC | 5 | misc flag | 4 [F|S|B] + * | Format | -------------- + * -------------- | chunkname | ULEB128 var-len + * | Endian | 6 | len | encoded uint32 + * -------------- -------------- + * | size of | 7 | chunkname | + * | int | | str no \0 | + * -------------- -------------- + * | size of | 8 + * | size_t | + * -------------- + * | size of | 9 + * | instruction| + * -------------- + * | size of | 10 + * | number | + * -------------- + * | number | 11 + * | is int? | + * -------------- +*/ + + +/* + * CLOSURE 0 0 RETURN 0 2 RETURN 0 1 + * length(Instruction) = 4 or 8 + * little endian or big endian +*/ +#ifndef OPENRESTY_LUAJIT +#define LUA_LITTLE_ENDIAN_4BYTES_CODE \ + "\x24\x00\x00\x00\x1e\x00\x00\x01\x1e\x00\x80\x00" +#define LUA_LITTLE_ENDIAN_8BYTES_CODE \ + "\x24\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x01" \ + "\x00\x00\x00\x00\x1e\x00\x80\x00\x00\x00\x00\x00" +#define LUA_BIG_ENDIAN_4BYTES_CODE \ + "\x00\x00\x00\x24\x01\x00\x00\x1e\x00\x08\x00\x1e" +#define LUA_BIG_ENDIAN_8BYTES_CODE \ + "\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x00" \ + "\x01\x00\x00\x1e\x00\x00\x00\x00\x00\x08\x00\x1e" +#define LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4) +#define LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8) +#define LUA_BIG_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4) +#define LUA_BIG_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8) +#define LUAC_HEADERSIZE 12 +#define LUAC_VERSION 0x51 +#endif /* OPENRESTY_LUAJIT */ + + +/* + * taken from chaoslawful: + * Lua Proto + * --------------------- + * | String | Can be empty string + * | [source] | (stripped or internal function) + * --------------------- + * | Int | At which line this function is defined + * | [linedefined] | + * --------------------- + * | Int | At while line this function definition ended + * | [lastlinedefined] | + * --------------------- + * | Char | Number of upvalues referenced by this function + * | [nups] | + * --------------------- + * | Char | Number of parameters of this function + * | [numparams] | + * --------------------- + * | Char | Does this function has variable number of arguments? + * | [is_var_arg] | main function always set to VARARG_ISVARARG (2) + * --------------------- + * | Char | Maximum stack size this function used + * | [maxstacksize] | Initially set to 2 + * --------------------- + * | Vector(instr) | Code instructions of this function + * | [code] | + * --------------------- + * | Int | Number of constants referenced by this function + * | [sizek] | + * --------------------- + * | Char | ------------------------------------ + * | type of [k[i]] | The type and content of constants | + * --------------------- |-> repeat for i in + * | Char if boolean | No content part if type is NIL | [1..sizek] + * | Number if number | ------------------------------------ + * | String if string | + * --------------------- + * | Int | Number of internal functions + * | [sizep] | + * --------------------- + * | Function | -> repeat for i in [1..sizep] + * | at [p[i]] | + * --------------------- + * | Vector | Debug lineinfo vector + * | [lineinfo] | Empty vector here if dubug info is stripped + * --------------------- + * | Int | Number of local variable in this function + * | [sizelocvars] | 0 if debug info is stripped + * --------------------- + * | String | ------------------------------------ + * | [locvars[i]] | Name of local var i | + * | .varname] | | + * --------------------- | + * | Int | instruction counter | + * | [locvars[i]] | where lcoal var i start to be |-> repeat for i in + * | .startpc] | referenced | [0..sizelocvars] + * --------------------- | + * | Int | instruction counter, where local | + * | [locvars[i]] | var i ceased to be referenced | + * | .endpc] | ------------------------------------ + * --------------------- + * | Int | Number of upvalues referenced by this function, + * | [sizeupvalues] | 0 if stripped + * --------------------- + * | String | -> repeat for i in[0..sizeupvalues] + * | [upvalues[i]] | + * --------------------- +*/ + +#ifndef OPENRESTY_LUAJIT +#define POS_SOURCE_STR_LEN LUAC_HEADERSIZE +#define POS_START_LINE (POS_SOURCE_STR_LEN + sizeof(size_t)) +#define POS_LAST_LINE (POS_START_LINE + sizeof(int)) +#define POS_NUM_OF_UPVS (POS_LAST_LINE + sizeof(int)) +#define POS_NUM_OF_PARA (POS_NUM_OF_UPVS + sizeof(char)) +#define POS_IS_VAR_ARG (POS_NUM_OF_PARA + sizeof(char)) +#define POS_MAX_STACK_SIZE (POS_IS_VAR_ARG + sizeof(char)) +#define POS_NUM_OF_INST (POS_MAX_STACK_SIZE +sizeof(char)) +#define POS_BYTECODE (POS_NUM_OF_INST + sizeof(int)) +#define MAX_BEGIN_CODE_SIZE \ + (POS_BYTECODE + LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN \ + + sizeof(int) + sizeof(int)) +#define MAX_END_CODE_SIZE (sizeof(int) + sizeof(int) + sizeof(int)) +#endif /* OPENRESTY_LUAJIT */ + +/* + * taken from chaoslawful: + * Luajit bytecode format + * --------------------- + * | HEAD | Luajit bytecode head + * --------------------- + * | Internal | All internal functions + * | functions | + * --------------------- + * | ULEB128 | Rest data total length of this function + * | [Date len of | (not include itself) + * | this function] | + * --------------------- + * | Char | F(ffi) | V(vararg)| C(has internal funcs) + * | [func flag] | + * --------------------- + * | Char | Number of parameters of this function + * | [numparams] | + * --------------------- + * | Char | + * | [framesize] | + * --------------------- + * | Char | Number of upvalues referenced by this function + * | [sizeupvalues] | + * --------------------- + * | ULEB128 | Number of collectable constants referenced + * | [sizekgc] | by this function + * --------------------- + * | ULEB128 | Number of lua number constants referenced + * | [sizekn] | by this function + * --------------------- + * | ULEB128 | Number of bytecode instructions of this function + * | [sizebc]m1 | minus 1 to omit the BC_FUNCV/BC_FUNCF header bytecode + * --------------------- + * | ULEB128 | + * | [size of dbg | Size of debug lineinfo map, available when not stripped + * | lineinfo] | + * --------------------- + * | ULEB128 | Available when not stripped + * | [firstline] | The first line of this function's definition + * --------------------- + * | ULEB128 | Available when not stripped + * | [numline] | The number of lines of this function's definition + * --------------------- + * | [bytecode] | Bytecode instructions of this function + * --------------------- + * |[upvalue ref slots]| [sizeupvalues] * 2 + * --------------------- + * | [collectable | [sizekgc] elems, variable length + * | constants] | + * --------------------- + * | [lua number | [sizekn] elems, variable length + * | constants] | + * --------------------- + * | [debug lineinfo | Length is the calculated size of debug lineinfo above + * | | Only available if not stripped + * --------------------- + * | Char | + * | [\x00] | Footer + * --------------------- +*/ + +/* bytecode for luajit 2.0 */ + +#ifndef OPENRESTY_LUAJIT +#define LJ20_LITTLE_ENDIAN_CODE_STRIPPED \ + "\x14\x03\x00\x01\x00\x01\x00\x03" \ + "\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \ + "\x00\x00" + +#define LJ20_BIG_ENDIAN_CODE_STRIPPED \ + "\x14\x03\x00\x01\x00\x01\x00\x03" \ + "\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \ + "\x00\x00" + +#define LJ20_LITTLE_ENDIAN_CODE \ + "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ + "\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \ + "\x00\x00" + +#define LJ20_BIG_ENDIAN_CODE \ + "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ + "\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \ + "\x00\x00" + +/* bytecode for luajit 2.1 */ + +#define LJ21_LITTLE_ENDIAN_CODE_STRIPPED \ + "\x14\x03\x00\x01\x00\x01\x00\x03" \ + "\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \ + "\x00\x00" + +#define LJ21_BIG_ENDIAN_CODE_STRIPPED \ + "\x14\x03\x00\x01\x00\x01\x00\x03" \ + "\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \ + "\x00\x00" + +#define LJ21_LITTLE_ENDIAN_CODE \ + "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ + "\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \ + "\x00\x00" + +#define LJ21_BIG_ENDIAN_CODE \ + "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ + "\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \ + "\x00\x00" + +#define LJ_CODE_LEN 23 +#define LJ_CODE_LEN_STRIPPED 22 +#define LJ_HEADERSIZE 5 +#define LJ_BCDUMP_F_BE 0x01 +#define LJ_BCDUMP_F_STRIP 0x02 +#define LJ21_BCDUMP_VERSION 2 +#define LJ20_BCDUMP_VERSION 1 +#define LJ_SIGNATURE "\x1b\x4c\x4a" +#endif /* OPENRESTY_LUAJIT */ + + +typedef enum { + NGX_LUA_TEXT_FILE, + NGX_LUA_BT_LUA, + NGX_LUA_BT_LJ +} ngx_stream_lua_clfactory_file_type_e; + + +enum { + NGX_LUA_READER_BUFSIZE = 4096 +}; + + +typedef struct { + ngx_stream_lua_clfactory_file_type_e file_type; + + int extraline; + FILE *f; +#ifndef OPENRESTY_LUAJIT + int sent_begin; + int sent_end; + size_t begin_code_len; + size_t end_code_len; + size_t rest_len; + union { + char *ptr; + char str[MAX_BEGIN_CODE_SIZE]; + } begin_code; + union { + char *ptr; + char str[MAX_END_CODE_SIZE]; + } end_code; +#endif /* OPENRESTY_LUAJIT */ + char buff[NGX_LUA_READER_BUFSIZE]; +} ngx_stream_lua_clfactory_file_ctx_t; + + +typedef struct { +#ifndef OPENRESTY_LUAJIT + int sent_begin; + int sent_end; +#endif + const char *s; + size_t size; +} ngx_stream_lua_clfactory_buffer_ctx_t; + + +static const char *ngx_stream_lua_clfactory_getF(lua_State *L, void *ud, + size_t *size); +static int ngx_stream_lua_clfactory_errfile(lua_State *L, const char *what, + int fname_index); +static const char *ngx_stream_lua_clfactory_getS(lua_State *L, void *ud, + size_t *size); +#ifndef OPENRESTY_LUAJIT +static long ngx_stream_lua_clfactory_file_size(FILE *f); +#endif + + +#ifndef OPENRESTY_LUAJIT +int +ngx_stream_lua_clfactory_bytecode_prepare(lua_State *L, + ngx_stream_lua_clfactory_file_ctx_t *lf, int fname_index) +{ + int x = 1, size_of_int, size_of_size_t, little_endian, + size_of_inst, version, stripped; + static int num_of_inst = 3, num_of_inter_func = 1; + const char *emsg, *serr, *bytecode; + size_t size, bytecode_len; + long fsize; + + serr = NULL; + + *lf->begin_code.str = LUA_SIGNATURE[0]; + + if (lf->file_type == NGX_LUA_BT_LJ) { + size = fread(lf->begin_code.str + 1, 1, LJ_HEADERSIZE - 1, lf->f); + + if (size != LJ_HEADERSIZE - 1) { + serr = strerror(errno); + emsg = "cannot read header"; + goto error; + } + + version = *(lf->begin_code.str + 3); + + dd("version: %d", (int) version); + + if (ngx_memcmp(lf->begin_code.str, LJ_SIGNATURE, + sizeof(LJ_SIGNATURE) - 1)) + { + emsg = "bad byte-code header"; + goto error; + } + +#if defined(DDEBUG) && (DDEBUG) + { + dd("==LJ_BT_HEADER=="); + size_t i; + for (i = 0; i < LJ_HEADERSIZE; i++) { + dd("%ld: 0x%02X", i, (unsigned)(u_char) lf->begin_code.str[i]); + } + dd("==LJ_BT_HEADER_END=="); + } +#endif + + lf->begin_code_len = LJ_HEADERSIZE; + little_endian = !((*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_BE); + stripped = (*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_STRIP; + + dd("stripped: %d", (int) stripped); + + if (version == LJ21_BCDUMP_VERSION) { + if (stripped) { + if (little_endian) { + lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE_STRIPPED; + + } else { + lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE_STRIPPED; + } + + lf->end_code_len = LJ_CODE_LEN_STRIPPED; + + } else { + if (little_endian) { + lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE; + + } else { + lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE; + } + + lf->end_code_len = LJ_CODE_LEN; + } + + } else if (version == LJ20_BCDUMP_VERSION) { + if (stripped) { + if (little_endian) { + lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE_STRIPPED; + + } else { + lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE_STRIPPED; + } + + lf->end_code_len = LJ_CODE_LEN_STRIPPED; + + } else { + if (little_endian) { + lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE; + + } else { + lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE; + } + + lf->end_code_len = LJ_CODE_LEN; + } + + } else { + emsg = "bytecode format version unsupported"; + goto error; + } + + fsize = ngx_stream_lua_clfactory_file_size(lf->f); + if (fsize < 0) { + serr = strerror(errno); + emsg = "cannot fseek/ftell"; + goto error; + } + + lf->rest_len = fsize - LJ_HEADERSIZE; + +#if defined(DDEBUG) && (DDEBUG) + { + size_t i = 0; + dd("==LJ_END_CODE: %ld rest_len: %ld==", lf->end_code_len, + lf->rest_len); + + for (i = 0; i < lf->end_code_len; i++) { + dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.ptr[i])); + } + dd("==LJ_END_CODE_END=="); + } +#endif + + } else { + size = fread(lf->begin_code.str + 1, 1, LUAC_HEADERSIZE - 1, lf->f); + + if (size != LUAC_HEADERSIZE - 1) { + serr = strerror(errno); + emsg = "cannot read header"; + goto error; + } + + version = *(lf->begin_code.str + 4); + little_endian = *(lf->begin_code.str + 6); + size_of_int = *(lf->begin_code.str + 7); + size_of_size_t = *(lf->begin_code.str + 8); + size_of_inst = *(lf->begin_code.str + 9); + +#if defined(DDEBUG) && (DDEBUG) + { + dd("==LUA_BT_HEADER=="); + size_t i; + for (i = 0; i < LUAC_HEADERSIZE; i++) { + dd("%ld, 0x%02X", i, (unsigned)(u_char) lf->begin_code.str[i]); + } + dd("==LUA_BT_HEADER_END=="); + } +#endif + + if (ngx_memcmp(lf->begin_code.str, LUA_SIGNATURE, + sizeof(LUA_SIGNATURE) -1) + || version != LUAC_VERSION + || little_endian != (int) (*(char *) &x) + || size_of_int != sizeof(int) + || size_of_size_t != sizeof(size_t) + || (size_of_inst != 4 && size_of_inst != 8)) + { + emsg = "bad byte-code header"; + goto error; + } + + /* clear the following fields to zero: + * - source string length + * - start line + * - last line + */ + ngx_memzero(lf->begin_code.str + POS_SOURCE_STR_LEN, + sizeof(size_t) + sizeof(int) * 2); + /* number of upvalues */ + *(lf->begin_code.str + POS_NUM_OF_UPVS) = 0; + /* number of parameters */ + *(lf->begin_code.str + POS_NUM_OF_PARA) = 0; + /* is var-argument function? */ + *(lf->begin_code.str + POS_IS_VAR_ARG) = 2; + /* max stack size */ + *(lf->begin_code.str + POS_MAX_STACK_SIZE) = 2; + /* number of bytecode instructions */ + ngx_memcpy(lf->begin_code.str + POS_NUM_OF_INST, &num_of_inst, + sizeof(int)); + + lf->begin_code_len = POS_BYTECODE; + + if (little_endian) { + if (size_of_inst == 4) { + bytecode = LUA_LITTLE_ENDIAN_4BYTES_CODE; + bytecode_len = LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN; + + } else { + bytecode = LUA_LITTLE_ENDIAN_8BYTES_CODE; + bytecode_len = LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN; + } + + } else { + if (size_of_inst == 4) { + bytecode = LUA_BIG_ENDIAN_4BYTES_CODE; + bytecode_len = LUA_BIG_ENDIAN_4BYTES_CODE_LEN; + + } else { + bytecode = LUA_BIG_ENDIAN_8BYTES_CODE; + bytecode_len = LUA_BIG_ENDIAN_8BYTES_CODE_LEN; + } + } + + /* bytecode */ + ngx_memcpy(lf->begin_code.str + POS_BYTECODE, bytecode, bytecode_len); + + /* number of consts */ + ngx_memzero(lf->begin_code.str + POS_BYTECODE + bytecode_len, + sizeof(int)); + /* number of internal functions */ + ngx_memcpy(lf->begin_code.str + POS_BYTECODE + bytecode_len + + sizeof(int), &num_of_inter_func, sizeof(int)); + + lf->begin_code_len += bytecode_len + sizeof(int) + sizeof(int); + +#if defined(DDEBUG) && (DDEBUG) + { + size_t i = 0; + dd("==LUA_BEGIN_CODE: %ld==", lf->begin_code_len); + for (i = 0; i < lf->begin_code_len; i++) { + dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->begin_code.str[i])); + } + dd("==LUA_BEGIN_CODE_END=="); + } +#endif + + /* clear the following fields to zero: + * - lineinfo vector size + * - number of local vars + * - number of upvalues + */ + ngx_memzero(lf->end_code.str, sizeof(int) * 3); + + lf->end_code_len = sizeof(int) + sizeof(int) + sizeof(int); + +#if defined(DDEBUG) && (DDEBUG) + { + size_t i = 0; + dd("==LUA_END_CODE: %ld==", lf->end_code_len); + for (i = 0; i < lf->end_code_len; i++) { + dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.str[i])); + } + dd("==LUA_END_CODE_END=="); + } +#endif + + } + + return 0; + +error: + + fclose(lf->f); /* close file (even in case of errors) */ + + if (serr) { + lua_pushfstring(L, "%s: %s", emsg, serr); + + } else { + lua_pushstring(L, emsg); + } + + lua_remove(L, fname_index); + + return LUA_ERRFILE; +} +#endif /* OPENRESTY_LUAJIT */ + + +ngx_int_t +ngx_stream_lua_clfactory_loadfile(lua_State *L, const char *filename) +{ + int c, status, readstatus; + ngx_flag_t sharp; + + ngx_stream_lua_clfactory_file_ctx_t lf; + + /* index of filename on the stack */ + int fname_index; + + sharp = 0; + fname_index = lua_gettop(L) + 1; + + lf.extraline = 0; + lf.file_type = NGX_LUA_TEXT_FILE; + +#ifndef OPENRESTY_LUAJIT + lf.begin_code.ptr = CLFACTORY_BEGIN_CODE; + lf.begin_code_len = CLFACTORY_BEGIN_SIZE; + lf.end_code.ptr = CLFACTORY_END_CODE; + lf.end_code_len = CLFACTORY_END_SIZE; +#endif + + lua_pushfstring(L, "@%s", filename); + + lf.f = fopen(filename, "r"); + if (lf.f == NULL) { + return ngx_stream_lua_clfactory_errfile(L, "open", fname_index); + } + + c = getc(lf.f); + + if (c == '#') { /* Unix exec. file? */ + lf.extraline = 1; + + while ((c = getc(lf.f)) != EOF && c != '\n') { + /* skip first line */ + } + + if (c == '\n') { + c = getc(lf.f); + } + + sharp = 1; + } + + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + + if (lf.f == NULL) { + return ngx_stream_lua_clfactory_errfile(L, "reopen", fname_index); + } + + /* check whether lib jit exists */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, "jit"); /* get _LOADED["jit"] */ + + if (lua_istable(L, -1)) { + lf.file_type = NGX_LUA_BT_LJ; + + } else { + lf.file_type = NGX_LUA_BT_LUA; + } + + lua_pop(L, 2); + + /* + * Loading bytecode with an extra header is disabled for security + * reasons. This may circumvent the usual check for bytecode vs. + * Lua code by looking at the first char. Since this is a potential + * security violation no attempt is made to echo the chunkname either. + */ + if (lf.file_type == NGX_LUA_BT_LJ && sharp) { + + if (filename) { + fclose(lf.f); /* close file (even in case of errors) */ + } + + filename = lua_tostring(L, fname_index) + 1; + lua_pushfstring(L, "bad byte-code header in %s", filename); + lua_remove(L, fname_index); + + return LUA_ERRFILE; + } + + while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) { + /* skip eventual `#!...' */ + } + +#ifndef OPENRESTY_LUAJIT + status = ngx_stream_lua_clfactory_bytecode_prepare(L, &lf, fname_index); + + if (status != 0) { + return status; + } +#endif + + lf.extraline = 0; + } + +#ifndef OPENRESTY_LUAJIT + if (lf.file_type == NGX_LUA_TEXT_FILE) { + ungetc(c, lf.f); + } + + lf.sent_begin = lf.sent_end = 0; + +#else + ungetc(c, lf.f); +#endif + status = lua_load(L, ngx_stream_lua_clfactory_getF, &lf, + lua_tostring(L, -1)); + + readstatus = ferror(lf.f); + + if (filename) { + fclose(lf.f); /* close file (even in case of errors) */ + } + + if (readstatus) { + lua_settop(L, fname_index); /* ignore results from `lua_load' */ + return ngx_stream_lua_clfactory_errfile(L, "read", fname_index); + } + + lua_remove(L, fname_index); + + return status; +} + + +ngx_int_t +ngx_stream_lua_clfactory_loadbuffer(lua_State *L, const char *buff, + size_t size, const char *name) +{ + ngx_stream_lua_clfactory_buffer_ctx_t ls; + + ls.s = buff; + ls.size = size; +#ifndef OPENRESTY_LUAJIT + ls.sent_begin = 0; + ls.sent_end = 0; +#endif + + return lua_load(L, ngx_stream_lua_clfactory_getS, &ls, name); +} + + +static const char * +ngx_stream_lua_clfactory_getF(lua_State *L, void *ud, size_t *size) +{ +#ifndef OPENRESTY_LUAJIT + char *buf; +#endif + size_t num; + + ngx_stream_lua_clfactory_file_ctx_t *lf; + + lf = (ngx_stream_lua_clfactory_file_ctx_t *) ud; + + if (lf->extraline) { + lf->extraline = 0; + *size = 1; + return "\n"; + } + +#ifndef OPENRESTY_LUAJIT + if (lf->sent_begin == 0) { + lf->sent_begin = 1; + *size = lf->begin_code_len; + + if (lf->file_type == NGX_LUA_TEXT_FILE) { + buf = lf->begin_code.ptr; + + } else { + buf = lf->begin_code.str; + } + + return buf; + } +#endif /* OPENRESTY_LUAJIT */ + + num = fread(lf->buff, 1, sizeof(lf->buff), lf->f); + + dd("fread returned %d", (int) num); + + if (num == 0) { +#ifndef OPENRESTY_LUAJIT + if (lf->sent_end == 0) { + lf->sent_end = 1; + *size = lf->end_code_len; + + if (lf->file_type == NGX_LUA_BT_LUA) { + buf = lf->end_code.str; + + } else { + buf = lf->end_code.ptr; + } + + return buf; + } +#endif /* OPENRESTY_LUAJIT */ + + *size = 0; + return NULL; + } + +#ifndef OPENRESTY_LUAJIT + if (lf->file_type == NGX_LUA_BT_LJ) { + /* skip the footer(\x00) in luajit */ + + lf->rest_len -= num; + + if (lf->rest_len == 0) { + if (--num == 0 && lf->sent_end == 0) { + lf->sent_end = 1; + buf = lf->end_code.ptr; + *size = lf->end_code_len; + + return buf; + } + } + } +#endif /* OPENRESTY_LUAJIT */ + + *size = num; + return lf->buff; +} + + +static int +ngx_stream_lua_clfactory_errfile(lua_State *L, const char *what, + int fname_index) +{ + const char *serr; + const char *filename; + + filename = lua_tostring(L, fname_index) + 1; + + if (errno) { + serr = strerror(errno); + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + + } else { + lua_pushfstring(L, "cannot %s %s", what, filename); + } + + lua_remove(L, fname_index); + + return LUA_ERRFILE; +} + + +static const char * +ngx_stream_lua_clfactory_getS(lua_State *L, void *ud, size_t *size) +{ + ngx_stream_lua_clfactory_buffer_ctx_t *ls = ud; + +#ifndef OPENRESTY_LUAJIT + if (ls->sent_begin == 0) { + ls->sent_begin = 1; + *size = CLFACTORY_BEGIN_SIZE; + + return CLFACTORY_BEGIN_CODE; + } +#endif + + if (ls->size == 0) { +#ifndef OPENRESTY_LUAJIT + if (ls->sent_end == 0) { + ls->sent_end = 1; + *size = CLFACTORY_END_SIZE; + return CLFACTORY_END_CODE; + } +#endif + + return NULL; + } + + *size = ls->size; + ls->size = 0; + + return ls->s; +} + + +#ifndef OPENRESTY_LUAJIT +static long +ngx_stream_lua_clfactory_file_size(FILE *f) +{ + long cur_pos, len; + + cur_pos = ftell(f); + if (cur_pos == -1) { + return -1; + } + + if (fseek(f, 0, SEEK_END) != 0) { + return -1; + } + + len = ftell(f); + if (len == -1) { + return -1; + } + + if (fseek(f, cur_pos, SEEK_SET) != 0) { + return -1; + } + + return len; +} +#endif /* OPENRESTY_LUAJIT */ + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_clfactory.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_clfactory.h new file mode 100644 index 000000000..8be7b17c4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_clfactory.h @@ -0,0 +1,30 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_clfactory.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CLFACTORY_H_INCLUDED_ +#define _NGX_STREAM_LUA_CLFACTORY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_clfactory_loadfile(lua_State *L, const char *filename); +ngx_int_t ngx_stream_lua_clfactory_loadbuffer(lua_State *L, const char *buff, + size_t size, const char *name); + + +#endif /* _NGX_STREAM_LUA_CLFACTORY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_common.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_common.h new file mode 100644 index 000000000..8d8cf48c3 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_common.h @@ -0,0 +1,530 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_common.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_COMMON_H_INCLUDED_ +#define _NGX_STREAM_LUA_COMMON_H_INCLUDED_ + + +#include "ngx_stream_lua_autoconf.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + + +#include "ngx_stream_lua_request.h" + + +#if (NGX_PCRE) + +#include + +#if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21) +# define LUA_HAVE_PCRE_JIT 1 +#else +# define LUA_HAVE_PCRE_JIT 0 +#endif + +#endif + + +#if !defined(nginx_version) || nginx_version < 1013006 +#error at least nginx 1.13.6 is required but found an older version +#endif + + + + +#if LUA_VERSION_NUM != 501 +# error unsupported Lua language version +#endif + + +#if !defined(LUAJIT_VERSION_NUM) || (LUAJIT_VERSION_NUM < 20000) +# error unsupported LuaJIT version +#endif + + +#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB) +# define NGX_STREAM_LUA_USE_OCSP 1 +#endif + + + + +#ifndef NGX_HAVE_SHA1 +# if defined(nginx_version) && nginx_version >= 1011002 +# define NGX_HAVE_SHA1 1 +# endif +#endif + + +#ifndef MD5_DIGEST_LENGTH +#define MD5_DIGEST_LENGTH 16 +#endif + + +#ifdef NGX_LUA_USE_ASSERT +# include +# define ngx_stream_lua_assert(a) assert(a) +#else +# define ngx_stream_lua_assert(a) +#endif + + +/* Nginx HTTP Lua Inline tag prefix */ + +#define NGX_STREAM_LUA_INLINE_TAG "nhli_" + +#define NGX_STREAM_LUA_INLINE_TAG_LEN \ + (sizeof(NGX_STREAM_LUA_INLINE_TAG) - 1) + +#define NGX_STREAM_LUA_INLINE_KEY_LEN \ + (NGX_STREAM_LUA_INLINE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) + +/* Nginx HTTP Lua File tag prefix */ + +#define NGX_STREAM_LUA_FILE_TAG "nhlf_" + +#define NGX_STREAM_LUA_FILE_TAG_LEN \ + (sizeof(NGX_STREAM_LUA_FILE_TAG) - 1) + +#define NGX_STREAM_LUA_FILE_KEY_LEN \ + (NGX_STREAM_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) + + +#define NGX_STREAM_CLIENT_CLOSED_REQUEST 499 + + + + +#ifndef NGX_STREAM_LUA_MAX_ARGS +#define NGX_STREAM_LUA_MAX_ARGS 100 +#endif + + +/* must be within 16 bit */ +#define NGX_STREAM_LUA_CONTEXT_CONTENT 0x0001 +#define NGX_STREAM_LUA_CONTEXT_LOG 0x0002 +#define NGX_STREAM_LUA_CONTEXT_TIMER 0x0004 +#define NGX_STREAM_LUA_CONTEXT_INIT_WORKER 0x0008 +#define NGX_STREAM_LUA_CONTEXT_BALANCER 0x0010 +#define NGX_STREAM_LUA_CONTEXT_PREREAD 0x0020 +#define NGX_STREAM_LUA_CONTEXT_SSL_CERT 0x0040 +#define NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO 0x0080 + + +#define NGX_STREAM_LUA_FFI_NO_REQ_CTX -100 +#define NGX_STREAM_LUA_FFI_BAD_CONTEXT -101 + + +#if (NGX_PTR_SIZE >= 8 && !defined(_WIN64)) +#define ngx_stream_lua_lightudata_mask(ludata) \ + ((void *) ((uintptr_t) (&ngx_stream_lua_##ludata) & ((1UL << 47) - 1))) + +#else +#define ngx_stream_lua_lightudata_mask(ludata) \ + (&ngx_stream_lua_##ludata) +#endif + + +typedef struct ngx_stream_lua_main_conf_s ngx_stream_lua_main_conf_t; +typedef struct ngx_stream_lua_srv_conf_s ngx_stream_lua_srv_conf_t; + + +typedef struct ngx_stream_lua_balancer_peer_data_s + ngx_stream_lua_balancer_peer_data_t; + + +typedef struct ngx_stream_lua_sema_mm_s ngx_stream_lua_sema_mm_t; + + +typedef ngx_int_t (*ngx_stream_lua_main_conf_handler_pt)(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L); +typedef ngx_int_t (*ngx_stream_lua_srv_conf_handler_pt)( + ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + + +typedef struct { + u_char *package; + lua_CFunction loader; +} ngx_stream_lua_preload_hook_t; + + +struct ngx_stream_lua_main_conf_s { + lua_State *lua; + ngx_pool_cleanup_t *vm_cleanup; + + ngx_str_t lua_path; + ngx_str_t lua_cpath; + + ngx_cycle_t *cycle; + ngx_pool_t *pool; + + ngx_int_t max_pending_timers; + ngx_int_t pending_timers; + + ngx_int_t max_running_timers; + ngx_int_t running_timers; + + ngx_connection_t *watcher; /* for watching the process exit event */ + +#if (NGX_PCRE) + ngx_int_t regex_cache_entries; + ngx_int_t regex_cache_max_entries; + ngx_int_t regex_match_limit; + +#if (LUA_HAVE_PCRE_JIT) + pcre_jit_stack *jit_stack; +#endif + +#endif + + ngx_array_t *shm_zones; /* of ngx_shm_zone_t* */ + + ngx_array_t *shdict_zones; /* shm zones of "shdict" */ + + ngx_array_t *preload_hooks; /* of ngx_stream_lua_preload_hook_t */ + + ngx_flag_t postponed_to_preread_phase_end; + + ngx_stream_lua_main_conf_handler_pt init_handler; + ngx_str_t init_src; + + ngx_stream_lua_main_conf_handler_pt init_worker_handler; + ngx_str_t init_worker_src; + + ngx_stream_lua_balancer_peer_data_t *balancer_peer_data; + /* neither yielding nor recursion is possible in + * balancer_by_lua*, so there cannot be any races among + * concurrent requests and it is safe to store the peer + * data pointer in the main conf. + */ + + ngx_uint_t shm_zones_inited; + + ngx_stream_lua_sema_mm_t *sema_mm; + + ngx_uint_t malloc_trim_cycle; /* a cycle is defined as the number + of reqeusts */ + ngx_uint_t malloc_trim_req_count; + + + ngx_flag_t set_sa_restart; + + unsigned requires_preread:1; + + unsigned requires_log:1; + unsigned requires_shm:1; + unsigned requires_capture_log:1; +}; + + + + +struct ngx_stream_lua_srv_conf_s { +#if (NGX_STREAM_SSL) + ngx_ssl_t *ssl; /* shared by SSL cosockets */ + ngx_uint_t ssl_protocols; + ngx_str_t ssl_ciphers; + ngx_uint_t ssl_verify_depth; + ngx_str_t ssl_trusted_certificate; + ngx_str_t ssl_crl; +#if (nginx_version >= 1019004) + ngx_array_t *ssl_conf_commands; +#endif + + struct { + ngx_stream_lua_srv_conf_handler_pt ssl_cert_handler; + ngx_str_t ssl_cert_src; + u_char *ssl_cert_src_key; + + ngx_stream_lua_srv_conf_handler_pt ssl_client_hello_handler; + ngx_str_t ssl_client_hello_src; + u_char *ssl_client_hello_src_key; + } srv; +#endif + + ngx_flag_t enable_code_cache; /* whether to enable + code cache */ + + ngx_stream_lua_handler_pt preread_handler; + + ngx_stream_lua_handler_pt content_handler; + ngx_stream_lua_handler_pt log_handler; + + u_char *preread_chunkname; + ngx_stream_complex_value_t preread_src; /* access_by_lua + inline script/script + file path */ + + u_char *preread_src_key; /* cached key for access_src */ + + u_char *content_chunkname; + + ngx_stream_complex_value_t content_src; + /* content_by_lua + * inline script/script + * file path */ + + u_char *content_src_key; /* cached key for content_src */ + + u_char *log_chunkname; + ngx_stream_complex_value_t log_src; + /* log_by_lua inline script/script + * file path */ + + u_char *log_src_key; + /* cached key for log_src */ + + + ngx_msec_t keepalive_timeout; + ngx_msec_t connect_timeout; + ngx_msec_t send_timeout; + ngx_msec_t read_timeout; + + size_t send_lowat; + size_t buffer_size; + + ngx_uint_t pool_size; + + + ngx_flag_t log_socket_errors; + ngx_flag_t check_client_abort; + + + struct { + ngx_str_t src; + u_char *src_key; + + ngx_stream_lua_srv_conf_handler_pt handler; + } balancer; + +}; + +typedef ngx_stream_lua_srv_conf_t ngx_stream_lua_loc_conf_t; + + +typedef enum { + NGX_STREAM_LUA_USER_CORO_NOP = 0, + NGX_STREAM_LUA_USER_CORO_RESUME = 1, + NGX_STREAM_LUA_USER_CORO_YIELD = 2, + NGX_STREAM_LUA_USER_THREAD_RESUME = 3 +} ngx_stream_lua_user_coro_op_t; + + +typedef enum { + NGX_STREAM_LUA_CO_RUNNING = 0, /* coroutine running */ + NGX_STREAM_LUA_CO_SUSPENDED = 1, /* coroutine suspended */ + NGX_STREAM_LUA_CO_NORMAL = 2, /* coroutine normal */ + NGX_STREAM_LUA_CO_DEAD = 3, /* coroutine dead */ + NGX_STREAM_LUA_CO_ZOMBIE = 4, /* coroutine zombie */ +} ngx_stream_lua_co_status_t; + + +typedef struct ngx_stream_lua_co_ctx_s ngx_stream_lua_co_ctx_t; + +typedef struct ngx_stream_lua_posted_thread_s ngx_stream_lua_posted_thread_t; + +struct ngx_stream_lua_posted_thread_s { + ngx_stream_lua_co_ctx_t *co_ctx; + ngx_stream_lua_posted_thread_t *next; +}; + + + + +struct ngx_stream_lua_co_ctx_s { + void *data; /* user state for cosockets */ + + lua_State *co; + ngx_stream_lua_co_ctx_t *parent_co_ctx; + + ngx_stream_lua_posted_thread_t *zombie_child_threads; + + ngx_stream_lua_cleanup_pt cleanup; + + + ngx_event_t sleep; /* used for ngx.sleep */ + + ngx_queue_t sem_wait_queue; + +#ifdef NGX_LUA_USE_ASSERT + int co_top; /* stack top after yielding/creation, + only for sanity checks */ +#endif + + int co_ref; /* reference to anchor the thread + coroutines (entry coroutine and user + threads) in the Lua registry, + preventing the thread coroutine + from beging collected by the + Lua GC */ + + unsigned waited_by_parent:1; /* whether being waited by + a parent coroutine */ + + unsigned co_status:3; /* the current coroutine's status */ + + unsigned flushing:1; /* indicates whether the current + coroutine is waiting for + ngx.flush(true) */ + + unsigned is_uthread:1; /* whether the current coroutine is + a user thread */ + + unsigned thread_spawn_yielded:1; /* yielded from + the ngx.thread.spawn() + call */ + unsigned sem_resume_status:1; + + unsigned is_wrap:1; /* set when creating coroutines via + coroutine.wrap */ + + unsigned propagate_error:1; /* set when propagating an error + from a coroutine to its + parent */ +}; + + +typedef struct { + lua_State *vm; + ngx_int_t count; +} ngx_stream_lua_vm_state_t; + + +typedef struct ngx_stream_lua_ctx_s { + /* for lua_coce_cache off: */ + ngx_stream_lua_vm_state_t *vm_state; + + ngx_stream_lua_request_t *request; + ngx_stream_lua_handler_pt resume_handler; + + ngx_stream_lua_co_ctx_t *cur_co_ctx; + /* co ctx for the current coroutine */ + + /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */ + ngx_list_t *user_co_ctx; /* coroutine contexts for user + coroutines */ + + ngx_stream_lua_co_ctx_t entry_co_ctx; /* coroutine context for the + entry coroutine */ + + ngx_stream_lua_co_ctx_t *on_abort_co_ctx; /* coroutine context for the + on_abort thread */ + + int ctx_ref; /* reference to anchor + request ctx data in lua + registry */ + + unsigned flushing_coros; /* number of coroutines waiting on + ngx.flush(true) */ + + ngx_chain_t *out; /* buffered output chain for HTTP 1.0 */ + ngx_chain_t *free_bufs; + ngx_chain_t *busy_bufs; + ngx_chain_t *free_recv_bufs; + + ngx_stream_lua_cleanup_pt *cleanup; + ngx_stream_lua_cleanup_t *free_cleanup; /* free list of cleanup records */ + + + + ngx_int_t exit_code; + + void *downstream; + /* can be either + * ngx_stream_lua_socket_tcp_upstream_t + * or ngx_stream_lua_co_ctx_t */ + + + ngx_stream_lua_posted_thread_t *posted_threads; + + int uthreads; /* number of active user threads */ + + uint16_t context; /* the current running directive context + (or running phase) for the current + Lua chunk */ + + + unsigned waiting_more_body:1; /* 1: waiting for more + request body data; + 0: no need to wait */ + + unsigned co_op:2; /* coroutine API operation */ + + unsigned exited:1; + + unsigned eof:1; /* 1: last_buf has been sent; + 0: last_buf not sent yet */ + + unsigned capture:1; /* 1: response body of current request + is to be captured by the lua + capture filter, + 0: not to be captured */ + + + unsigned read_body_done:1; /* 1: request body has been all + read; 0: body has not been + all read */ + + unsigned headers_set:1; /* whether the user has set custom + response headers */ + + unsigned entered_preread_phase:1; + + unsigned entered_content_phase:1; + + unsigned buffering:1; /* HTTP 1.0 response body buffering flag */ + + unsigned no_abort:1; /* prohibit "world abortion" via ngx.exit() + and etc */ + + unsigned header_sent:1; /* r->header_sent is not sufficient for + * this because special header filters + * like ngx_image_filter may intercept + * the header. so we should always test + * both flags. see the test case in + * t/020-subrequest.t */ + + unsigned seen_last_in_filter:1; /* used by body_filter_by_lua* */ + unsigned seen_last_for_subreq:1; /* used by body capture filter */ + unsigned writing_raw_req_socket:1; /* used by raw downstream + socket */ + unsigned acquired_raw_req_socket:1; /* whether a raw req socket + is acquired */ + unsigned seen_body_data:1; + unsigned peek_needs_more_data:1; /* whether req socket is waiting + for more data in preread buf */ +} ngx_stream_lua_ctx_t; + + + + +extern ngx_module_t ngx_stream_lua_module; + + + +#endif /* _NGX_STREAM_LUA_COMMON_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_config.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_config.c new file mode 100644 index 000000000..36dbfbe7e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_config.c @@ -0,0 +1,78 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_config.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_config.h" +#include "api/ngx_stream_lua_api.h" + + +static int ngx_stream_lua_config_prefix(lua_State *L); +static int ngx_stream_lua_config_configure(lua_State *L); + + +void +ngx_stream_lua_inject_config_api(lua_State *L) +{ + /* ngx.config */ + + lua_createtable(L, 0, 6 /* nrec */); /* .config */ + +#if (NGX_DEBUG) + lua_pushboolean(L, 1); +#else + lua_pushboolean(L, 0); +#endif + lua_setfield(L, -2, "debug"); + + lua_pushcfunction(L, ngx_stream_lua_config_prefix); + lua_setfield(L, -2, "prefix"); + + lua_pushinteger(L, nginx_version); + lua_setfield(L, -2, "nginx_version"); + + lua_pushinteger(L, ngx_stream_lua_version); + lua_setfield(L, -2, "ngx_lua_version"); + + lua_pushcfunction(L, ngx_stream_lua_config_configure); + lua_setfield(L, -2, "nginx_configure"); + + lua_pushliteral(L, "stream"); + lua_setfield(L, -2, "subsystem"); + + lua_setfield(L, -2, "config"); +} + + +static int +ngx_stream_lua_config_prefix(lua_State *L) +{ + lua_pushlstring(L, (char *) ngx_cycle->prefix.data, + ngx_cycle->prefix.len); + return 1; +} + + +static int +ngx_stream_lua_config_configure(lua_State *L) +{ + lua_pushliteral(L, NGX_CONFIGURE); + return 1; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_config.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_config.h new file mode 100644 index 000000000..025f4a668 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_config.h @@ -0,0 +1,27 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_config.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CONFIG_H_INCLUDED_ +#define _NGX_STREAM_LUA_CONFIG_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_config_api(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_CONFIG_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_consts.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_consts.c new file mode 100644 index 000000000..ea19f9068 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_consts.c @@ -0,0 +1,51 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_consts.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_consts.h" + + +void +ngx_stream_lua_inject_core_consts(lua_State *L) +{ + /* {{{ core constants */ + lua_pushinteger(L, NGX_OK); + lua_setfield(L, -2, "OK"); + + lua_pushinteger(L, NGX_AGAIN); + lua_setfield(L, -2, "AGAIN"); + + lua_pushinteger(L, NGX_DONE); + lua_setfield(L, -2, "DONE"); + + lua_pushinteger(L, NGX_DECLINED); + lua_setfield(L, -2, "DECLINED"); + + lua_pushinteger(L, NGX_ERROR); + lua_setfield(L, -2, "ERROR"); + + lua_pushlightuserdata(L, NULL); + lua_setfield(L, -2, "null"); + /* }}} */ +} + + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_consts.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_consts.h new file mode 100644 index 000000000..355f8aac0 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_consts.h @@ -0,0 +1,29 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_consts.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CONSTS_H_INCLUDED_ +#define _NGX_STREAM_LUA_CONSTS_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + + + +void ngx_stream_lua_inject_core_consts(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_CONSTS_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_contentby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_contentby.c new file mode 100644 index 000000000..0e8635122 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_contentby.c @@ -0,0 +1,353 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_contentby.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_exception.h" +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_probe.h" + + + + +ngx_int_t +ngx_stream_lua_content_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_event_t *rev; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_loc_conf_t *llcf; + + dd("content by chunk"); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + ngx_stream_lua_assert(ctx != NULL); + + dd("reset ctx"); + ngx_stream_lua_reset_ctx(r, L, ctx); + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_stream_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + return NGX_ERROR; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_stream_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + /* {{{ register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + /* }}} */ + + ctx->context = NGX_STREAM_LUA_CONTEXT_CONTENT; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + r->connection->read->handler = ngx_stream_lua_request_handler; + r->connection->write->handler = ngx_stream_lua_request_handler; + + if (llcf->check_client_abort) { + r->read_event_handler = ngx_stream_lua_rd_check_broken_connection; + + + rev = r->connection->read; + + if (!rev->active) { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + + } else { + r->read_event_handler = ngx_stream_lua_block_reading; + } + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + return rc; + } + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0); + } + + if (rc == NGX_DONE) { + return ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1); + } + + return NGX_OK; +} + + +void +ngx_stream_lua_content_wev_handler(ngx_stream_lua_request_t *r) +{ + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua ngx_stream_lua_content_wev_handler"); + + (void) ctx->resume_handler(r); +} + + +void +ngx_stream_lua_content_handler(ngx_stream_session_t *s) +{ + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_ctx_t *ctx; + ngx_int_t rc; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream lua content handler"); + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); + + if (lscf->content_handler == NULL) { + dd("no content handler found"); + ngx_stream_finalize_session(s, NGX_DECLINED); + + return; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(s); + if (ctx == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + dd("entered? %d", (int) ctx->entered_content_phase); + + if (ctx->entered_content_phase) { + dd("calling wev handler"); + rc = ctx->resume_handler(ctx->request); + dd("wev handler returns %d", (int) rc); + + ngx_stream_lua_finalize_request(ctx->request, rc); + return; + } + + dd("setting entered"); + + ctx->entered_content_phase = 1; + + dd("calling content handler"); + ngx_stream_lua_finalize_request(ctx->request, + lscf->content_handler(ctx->request)); + + return; +} + + + + +ngx_int_t +ngx_stream_lua_content_handler_file(ngx_stream_lua_request_t *r) +{ + lua_State *L; + ngx_int_t rc; + u_char *script_path; + ngx_str_t eval_src; + + ngx_stream_lua_loc_conf_t *llcf; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (ngx_stream_complex_value(r->session, &llcf->content_src, &eval_src) + != NGX_OK) + { + return NGX_ERROR; + } + + script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path, + llcf->content_src_key); + if (rc != NGX_OK) { + + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_content_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_content_handler_inline(ngx_stream_lua_request_t *r) +{ + lua_State *L; + ngx_int_t rc; + + ngx_stream_lua_loc_conf_t *llcf; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua inline script (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + llcf->content_src.value.data, + llcf->content_src.value.len, + llcf->content_src_key, + (const char *) + llcf->content_chunkname); + if (rc != NGX_OK) { + return NGX_ERROR; + } + + return ngx_stream_lua_content_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_content_run_posted_threads(lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, int n) +{ + ngx_int_t rc; + + ngx_stream_lua_posted_thread_t *pt; + + dd("run posted threads: %p", ctx->posted_threads); + + for ( ;; ) { + pt = ctx->posted_threads; + if (pt == NULL) { + goto done; + } + + ctx->posted_threads = pt->next; + + ngx_stream_lua_probe_run_posted_thread(r, pt->co_ctx->co, + (int) pt->co_ctx->co_status); + + dd("posted thread status: %d", pt->co_ctx->co_status); + + if (pt->co_ctx->co_status != NGX_STREAM_LUA_CO_RUNNING) { + continue; + } + + ctx->cur_co_ctx = pt->co_ctx; + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + n++; + continue; + } + + if (rc == NGX_OK) { + while (n > 0) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + n--; + } + + return NGX_OK; + } + + /* rc == NGX_ERROR || rc > NGX_OK */ + + return rc; + } + +done: + + if (n == 1) { + return NGX_DONE; + } + + if (n == 0) { + return NGX_DONE; + } + + /* n > 1 */ + + do { + ngx_stream_lua_finalize_request(r, NGX_DONE); + } while (--n > 1); + + return NGX_DONE; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_contentby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_contentby.h new file mode 100644 index 000000000..59766fb6d --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_contentby.h @@ -0,0 +1,37 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_contentby.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CONTENT_BY_H_INCLUDED_ +#define _NGX_STREAM_LUA_CONTENT_BY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_content_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); +void ngx_stream_lua_content_wev_handler(ngx_stream_lua_request_t *r); +ngx_int_t ngx_stream_lua_content_handler_file(ngx_stream_lua_request_t *r); +ngx_int_t ngx_stream_lua_content_handler_inline(ngx_stream_lua_request_t *r); + +void ngx_stream_lua_content_handler(ngx_stream_session_t *r); + +ngx_int_t ngx_stream_lua_content_run_posted_threads(lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, int n); + + +#endif /* _NGX_STREAM_LUA_CONTENT_BY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_control.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_control.c new file mode 100644 index 000000000..86dda5d13 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_control.c @@ -0,0 +1,164 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_control.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_control.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_coroutine.h" + + + + +static int ngx_stream_lua_on_abort(lua_State *L); + + +void +ngx_stream_lua_inject_control_api(ngx_log_t *log, lua_State *L) +{ + + /* ngx.on_abort */ + + lua_pushcfunction(L, ngx_stream_lua_on_abort); + lua_setfield(L, -2, "on_abort"); +} + + + + +static int +ngx_stream_lua_on_abort(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx = NULL; + ngx_stream_lua_loc_conf_t *llcf; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_fake_request2(L, r, ctx); + + if (ctx->on_abort_co_ctx) { + lua_pushnil(L); + lua_pushliteral(L, "duplicate call"); + return 2; + } + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + if (!llcf->check_client_abort) { + lua_pushnil(L); + lua_pushliteral(L, "lua_check_client_abort is off"); + return 2; + } + + ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx); + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, -2); + + dd("on_wait thread 1: %p", lua_tothread(L, -1)); + + coctx->co_ref = luaL_ref(L, -2); + lua_pop(L, 1); + + coctx->is_uthread = 1; + ctx->on_abort_co_ctx = coctx; + + dd("on_wait thread 2: %p", coctx->co); + + coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED; + coctx->parent_co_ctx = ctx->cur_co_ctx; + + lua_pushinteger(L, 1); + return 1; +} + + +int +ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, + size_t *errlen) +{ + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err; + return NGX_ERROR; + } + + + if (ngx_stream_lua_ffi_check_context(ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_BALANCER + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_PREREAD, + err, errlen) != NGX_OK) + { + return NGX_ERROR; + } + + if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO )) + { + +#if (NGX_STREAM_SSL) + + ctx->exit_code = status; + ctx->exited = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua exit with code %d", status); + + + return NGX_OK; + +#else + + return NGX_ERROR; + +#endif + } + + + ctx->exit_code = status; + ctx->exited = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua exit with code %i", ctx->exit_code); + + if (ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) { + return NGX_DONE; + } + + return NGX_OK; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_control.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_control.h new file mode 100644 index 000000000..0533314c9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_control.h @@ -0,0 +1,28 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_control.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CONTROL_H_INCLUDED_ +#define _NGX_STREAM_LUA_CONTROL_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_control_api(ngx_log_t *log, lua_State *L); + + +#endif /* _NGX_STREAM_LUA_CONTROL_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_coroutine.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_coroutine.c new file mode 100644 index 000000000..f12e89bf1 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_coroutine.c @@ -0,0 +1,449 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_coroutine.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_coroutine.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_probe.h" + + +/* + * Design: + * + * In order to support using ngx.* API in Lua coroutines, we have to create + * new coroutine in the main coroutine instead of the calling coroutine + */ + + +static int ngx_stream_lua_coroutine_create(lua_State *L); +static int ngx_stream_lua_coroutine_wrap(lua_State *L); +static int ngx_stream_lua_coroutine_resume(lua_State *L); +static int ngx_stream_lua_coroutine_yield(lua_State *L); +static int ngx_stream_lua_coroutine_status(lua_State *L); + + +static const ngx_str_t + ngx_stream_lua_co_status_names[] = + { + ngx_string("running"), + ngx_string("suspended"), + ngx_string("normal"), + ngx_string("dead"), + ngx_string("zombie") + }; + + + +static int +ngx_stream_lua_coroutine_create(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + return ngx_stream_lua_coroutine_create_helper(L, r, ctx, NULL); +} + + +static int +ngx_stream_lua_coroutine_wrap_runner(lua_State *L) +{ + /* retrieve closure and insert it at the bottom of + * the stack for coroutine.resume() */ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + + return ngx_stream_lua_coroutine_resume(L); +} + + +static int +ngx_stream_lua_coroutine_wrap(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx = NULL; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx); + + coctx->is_wrap = 1; + + lua_pushcclosure(L, ngx_stream_lua_coroutine_wrap_runner, 1); + + return 1; +} + + +int +ngx_stream_lua_coroutine_create_helper(lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, + ngx_stream_lua_co_ctx_t **pcoctx) +{ + lua_State *vm; /* the Lua VM */ + lua_State *co; /* new coroutine to be created */ + + /* co ctx for the new coroutine */ + ngx_stream_lua_co_ctx_t *coctx; + + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + vm = ngx_stream_lua_get_lua_vm(r, ctx); + + /* create new coroutine on root Lua state, so it always yields + * to main Lua thread + */ + co = lua_newthread(vm); + + ngx_stream_lua_probe_user_coroutine_create(r, L, co); + + coctx = ngx_stream_lua_get_co_ctx(co, ctx); + if (coctx == NULL) { + coctx = ngx_stream_lua_create_co_ctx(r, ctx); + if (coctx == NULL) { + return luaL_error(L, "no memory"); + } + + } else { + ngx_memzero(coctx, sizeof(ngx_stream_lua_co_ctx_t)); + coctx->co_ref = LUA_NOREF; + } + + coctx->co = co; + coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED; + +#ifdef OPENRESTY_LUAJIT + ngx_stream_lua_set_req(co, r); +#else + /* make new coroutine share globals of the parent coroutine. + * NOTE: globals don't have to be separated! */ + ngx_stream_lua_get_globals_table(L); + lua_xmove(L, co, 1); + ngx_stream_lua_set_globals_table(co); +#endif + + lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ + + lua_pushvalue(L, 1); /* copy entry function to top of L*/ + lua_xmove(L, co, 1); /* move entry function from L to co */ + + if (pcoctx) { + *pcoctx = coctx; + } + +#ifdef NGX_LUA_USE_ASSERT + coctx->co_top = 1; +#endif + + return 1; /* return new coroutine to Lua */ +} + + +static int +ngx_stream_lua_coroutine_resume(lua_State *L) +{ + lua_State *co; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_co_ctx_t *p_coctx; /* parent co ctx */ + + co = lua_tothread(L, 1); + + luaL_argcheck(L, co, 1, "coroutine expected"); + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_PREREAD + ); + + p_coctx = ctx->cur_co_ctx; + if (p_coctx == NULL) { + return luaL_error(L, "no parent co ctx found"); + } + + coctx = ngx_stream_lua_get_co_ctx(co, ctx); + if (coctx == NULL) { + return luaL_error(L, "no co ctx found"); + } + + ngx_stream_lua_probe_user_coroutine_resume(r, L, co); + + if (coctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) { + dd("coroutine resume: %d", coctx->co_status); + + lua_pushboolean(L, 0); + lua_pushfstring(L, "cannot resume %s coroutine", + ngx_stream_lua_co_status_names[coctx->co_status].data); + return 2; + } + + p_coctx->co_status = NGX_STREAM_LUA_CO_NORMAL; + + coctx->parent_co_ctx = p_coctx; + + dd("set coroutine to running"); + coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + + ctx->co_op = NGX_STREAM_LUA_USER_CORO_RESUME; + ctx->cur_co_ctx = coctx; + + /* yield and pass args to main thread, and resume target coroutine from + * there */ + return lua_yield(L, lua_gettop(L) - 1); +} + + +static int +ngx_stream_lua_coroutine_yield(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_PREREAD + ); + + coctx = ctx->cur_co_ctx; + + coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED; + + ctx->co_op = NGX_STREAM_LUA_USER_CORO_YIELD; + + if (!coctx->is_uthread && coctx->parent_co_ctx) { + dd("set coroutine to running"); + coctx->parent_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + + ngx_stream_lua_probe_user_coroutine_yield(r, + coctx->parent_co_ctx->co, L); + + } else { + ngx_stream_lua_probe_user_coroutine_yield(r, NULL, L); + } + + /* yield and pass retvals to main thread, + * and resume parent coroutine there */ + return lua_yield(L, lua_gettop(L)); +} + + +void +ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) +{ + int rc; + + /* new coroutine table */ + lua_createtable(L, 0 /* narr */, 16 /* nrec */); + + /* get old coroutine table */ + lua_getglobal(L, "coroutine"); + + /* set running to the old one */ + lua_getfield(L, -1, "running"); + lua_setfield(L, -3, "running"); + + lua_getfield(L, -1, "create"); + lua_setfield(L, -3, "_create"); + + lua_getfield(L, -1, "wrap"); + lua_setfield(L, -3, "_wrap"); + + lua_getfield(L, -1, "resume"); + lua_setfield(L, -3, "_resume"); + + lua_getfield(L, -1, "yield"); + lua_setfield(L, -3, "_yield"); + + lua_getfield(L, -1, "status"); + lua_setfield(L, -3, "_status"); + + /* pop the old coroutine */ + lua_pop(L, 1); + + lua_pushcfunction(L, ngx_stream_lua_coroutine_create); + lua_setfield(L, -2, "__create"); + + lua_pushcfunction(L, ngx_stream_lua_coroutine_wrap); + lua_setfield(L, -2, "__wrap"); + + lua_pushcfunction(L, ngx_stream_lua_coroutine_resume); + lua_setfield(L, -2, "__resume"); + + lua_pushcfunction(L, ngx_stream_lua_coroutine_yield); + lua_setfield(L, -2, "__yield"); + + lua_pushcfunction(L, ngx_stream_lua_coroutine_status); + lua_setfield(L, -2, "__status"); + + lua_setglobal(L, "coroutine"); + + /* inject coroutine APIs */ + { + const char buf[] = + "local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\n" +#ifdef OPENRESTY_LUAJIT + "local get_req = require 'thread.exdata'\n" +#else + "local getfenv = getfenv\n" +#endif + "for _, key in ipairs(keys) do\n" + "local std = coroutine['_' .. key]\n" + "local ours = coroutine['__' .. key]\n" + "local raw_ctx = ngx._phase_ctx\n" + "coroutine[key] = function (...)\n" +#ifdef OPENRESTY_LUAJIT + "local r = get_req()\n" +#else + "local r = getfenv(0).__ngx_req\n" +#endif + "if r ~= nil then\n" +#ifdef OPENRESTY_LUAJIT + "local ctx = raw_ctx()\n" +#else + "local ctx = raw_ctx(r)\n" +#endif + "return ours(...)\n" + "end\n" + "return std(...)\n" + "end\n" + "end\n" + "package.loaded.coroutine = coroutine" +#if 0 + "debug.sethook(function () collectgarbage() end, 'rl', 1)" +#endif + ; + + rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine_api"); + } + + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to load Lua code for coroutine_api: %i: %s", + rc, lua_tostring(L, -1)); + + lua_pop(L, 1); + return; + } + + rc = lua_pcall(L, 0, 0, 0); + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to run the Lua code for coroutine_api: %i: %s", + rc, lua_tostring(L, -1)); + lua_pop(L, 1); + } +} + + +static int +ngx_stream_lua_coroutine_status(lua_State *L) +{ + lua_State *co; /* new coroutine to be created */ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ + + co = lua_tothread(L, 1); + + luaL_argcheck(L, co, 1, "coroutine expected"); + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_PREREAD + ); + + coctx = ngx_stream_lua_get_co_ctx(co, ctx); + if (coctx == NULL) { + lua_pushlstring(L, (const char *) + ngx_stream_lua_co_status_names[NGX_STREAM_LUA_CO_DEAD] + .data, + ngx_stream_lua_co_status_names[NGX_STREAM_LUA_CO_DEAD] + .len); + return 1; + } + + dd("co status: %d", coctx->co_status); + + lua_pushlstring(L, (const char *) + ngx_stream_lua_co_status_names[coctx->co_status].data, + ngx_stream_lua_co_status_names[coctx->co_status].len); + return 1; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_coroutine.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_coroutine.h new file mode 100644 index 000000000..cad9693d0 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_coroutine.h @@ -0,0 +1,32 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_coroutine.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_COROUTINE_H_INCLUDED_ +#define _NGX_STREAM_LUA_COROUTINE_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L); + +int ngx_stream_lua_coroutine_create_helper(lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, + ngx_stream_lua_co_ctx_t **pcoctx); + + +#endif /* _NGX_STREAM_LUA_COROUTINE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ctx.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ctx.c new file mode 100644 index 000000000..096dd8b39 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ctx.c @@ -0,0 +1,210 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_ctx.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_ssl.h" +#include "ngx_stream_lua_ctx.h" + + +typedef struct { + int ref; + lua_State *vm; +} ngx_stream_lua_ngx_ctx_cleanup_data_t; + + +static ngx_int_t ngx_stream_lua_ngx_ctx_add_cleanup(ngx_stream_lua_request_t *r, + ngx_pool_t *pool, int ref); +static void ngx_stream_lua_ngx_ctx_cleanup(void *data); + + +int +ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, int index) +{ + ngx_pool_t *pool; + + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + + if (ctx->ctx_ref == LUA_NOREF) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua create ngx.ctx table for the current request"); + + lua_pushliteral(L, ngx_stream_lua_ctx_tables_key); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, index); + ctx->ctx_ref = luaL_ref(L, -2); + lua_pop(L, 1); + + pool = r->pool; + if (ngx_stream_lua_ngx_ctx_add_cleanup(r, pool, ctx->ctx_ref) != NGX_OK) { + return luaL_error(L, "no memory"); + } + + return 0; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua fetching existing ngx.ctx table for the current " + "request"); + + lua_pushliteral(L, ngx_stream_lua_ctx_tables_key); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, ctx->ctx_ref); + lua_pushvalue(L, index); + ctx->ctx_ref = luaL_ref(L, -2); + lua_pop(L, 1); + + return 0; +} + + +int +ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r, int *in_ssl_phase, + int *ssl_ctx_ref) +{ + ngx_stream_lua_ctx_t *ctx; +#if (NGX_STREAM_SSL) + ngx_stream_lua_ssl_ctx_t *ssl_ctx; +#endif + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_STREAM_LUA_FFI_NO_REQ_CTX; + } + + if (ctx->ctx_ref >= 0 || in_ssl_phase == NULL) { + return ctx->ctx_ref; + } + + *in_ssl_phase = ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO); + *ssl_ctx_ref = LUA_NOREF; + +#if (NGX_STREAM_SSL) + if (r->connection->ssl != NULL) { + ssl_ctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection); + + if (ssl_ctx != NULL) { + *ssl_ctx_ref = ssl_ctx->ctx_ref; + } + } +#endif + + return LUA_NOREF; +} + + +int +ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref) +{ + ngx_pool_t *pool; + ngx_stream_lua_ctx_t *ctx; +#if (NGX_STREAM_SSL) + ngx_connection_t *c; + ngx_stream_lua_ssl_ctx_t *ssl_ctx; +#endif + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_STREAM_LUA_FFI_NO_REQ_CTX; + } + +#if (NGX_STREAM_SSL) + if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO)) + { + ssl_ctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection); + if (ssl_ctx == NULL) { + return NGX_ERROR; + } + + ssl_ctx->ctx_ref = ref; + c = ngx_ssl_get_connection(r->connection->ssl->connection); + pool = c->pool; + + } else { + pool = r->pool; + } + +#else + pool = r->pool; +#endif + + ctx->ctx_ref = ref; + + if (ngx_stream_lua_ngx_ctx_add_cleanup(r, pool, ref) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_ngx_ctx_add_cleanup(ngx_stream_lua_request_t *r, ngx_pool_t *pool, + int ref) +{ + lua_State *L; + ngx_pool_cleanup_t *cln; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_ngx_ctx_cleanup_data_t *data; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + L = ngx_stream_lua_get_lua_vm(r, ctx); + + cln = ngx_pool_cleanup_add(pool, + sizeof(ngx_stream_lua_ngx_ctx_cleanup_data_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_stream_lua_ngx_ctx_cleanup; + + data = cln->data; + data->vm = L; + data->ref = ref; + + return NGX_OK; +} + + +static void +ngx_stream_lua_ngx_ctx_cleanup(void *data) +{ + lua_State *L; + + ngx_stream_lua_ngx_ctx_cleanup_data_t *clndata = data; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "lua release ngx.ctx at ref %d", clndata->ref); + + L = clndata->vm; + + lua_pushliteral(L, ngx_stream_lua_ctx_tables_key); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, clndata->ref); + lua_pop(L, 1); +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ctx.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ctx.h new file mode 100644 index 000000000..f8dfb86db --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ctx.h @@ -0,0 +1,29 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_ctx.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_CTX_H_INCLUDED_ +#define _NGX_STREAM_LUA_CTX_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +int ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, int index); + + +#endif /* _NGX_STREAM_LUA_CTX_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_directive.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_directive.c new file mode 100644 index 000000000..5580106f4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_directive.c @@ -0,0 +1,1338 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_directive.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_common.h" +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_logby.h" +#include "ngx_stream_lua_initby.h" +#include "ngx_stream_lua_initworkerby.h" +#include "ngx_stream_lua_shdict.h" +#include "ngx_stream_lua_lex.h" +#include "ngx_stream_lua_log.h" +#include "ngx_stream_lua_log_ringbuf.h" +#include "api/ngx_stream_lua_api.h" + +#include "ngx_stream_lua_prereadby.h" + + +typedef struct ngx_stream_lua_block_parser_ctx_s + ngx_stream_lua_block_parser_ctx_t; + + + +static u_char *ngx_stream_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, + size_t tag_len, size_t *chunkname_len); +static ngx_int_t ngx_stream_lua_conf_read_lua_token(ngx_conf_t *cf, + ngx_stream_lua_block_parser_ctx_t *ctx); +static u_char *ngx_stream_lua_strlstrn(u_char *s1, u_char *last, u_char *s2, + size_t n); + + +struct ngx_stream_lua_block_parser_ctx_s { + ngx_uint_t start_line; + int token_len; +}; + + +enum { + FOUND_LEFT_CURLY = 0, + FOUND_RIGHT_CURLY, + FOUND_LEFT_LBRACKET_STR, + FOUND_LBRACKET_STR = FOUND_LEFT_LBRACKET_STR, + FOUND_LEFT_LBRACKET_CMT, + FOUND_LBRACKET_CMT = FOUND_LEFT_LBRACKET_CMT, + FOUND_RIGHT_LBRACKET, + FOUND_COMMENT_LINE, + FOUND_DOUBLE_QUOTED, + FOUND_SINGLE_QUOTED +}; + + +char * +ngx_stream_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_lua_main_conf_t *lmcf = conf; + ngx_str_t *value, name; + ngx_shm_zone_t *zone; + ngx_shm_zone_t **zp; + ngx_stream_lua_shdict_ctx_t *ctx; + ssize_t size; + + if (lmcf->shdict_zones == NULL) { + lmcf->shdict_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t)); + if (lmcf->shdict_zones == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_array_init(lmcf->shdict_zones, cf->pool, 2, + sizeof(ngx_shm_zone_t *)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + value = cf->args->elts; + + ctx = NULL; + + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid lua shared dict name \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + name = value[1]; + + size = ngx_parse_size(&value[2]); + + if (size <= 8191) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid lua shared dict size \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_lua_shdict_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->name = name; + ctx->main_conf = lmcf; + ctx->log = &cf->cycle->new_log; + + zone = ngx_stream_lua_shared_memory_add(cf, &name, (size_t) size, + &ngx_stream_lua_module); + if (zone == NULL) { + return NGX_CONF_ERROR; + } + + if (zone->data) { + ctx = zone->data; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "lua_shared_dict \"%V\" is already defined as " + "\"%V\"", &name, &ctx->name); + return NGX_CONF_ERROR; + } + + zone->init = ngx_stream_lua_shdict_init_zone; + zone->data = ctx; + + zp = ngx_array_push(lmcf->shdict_zones); + if (zp == NULL) { + return NGX_CONF_ERROR; + } + + *zp = zone; + + lmcf->requires_shm = 1; + + return NGX_CONF_OK; +} + + +char * +ngx_stream_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + ngx_flag_t *fp; + char *ret; + + ret = ngx_conf_set_flag_slot(cf, cmd, conf); + if (ret != NGX_CONF_OK) { + return ret; + } + + fp = (ngx_flag_t *) (p + cmd->offset); + + if (!*fp) { + ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, + "stream lua_code_cache is off; this will hurt " + "performance"); + } + + return NGX_CONF_OK; +} + + +char * +ngx_stream_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "lua_load_resty_core is deprecated (the lua-resty-core " + "library is required since " + "ngx_stream_lua v0.0.8)"); + + return NGX_CONF_OK; +} + + +char * +ngx_stream_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_lua_main_conf_t *lmcf = conf; + + ngx_str_t *value; + + if (lmcf->lua_cpath.len != 0) { + return "is duplicate"; + } + + dd("enter"); + + value = cf->args->elts; + + lmcf->lua_cpath.len = value[1].len; + lmcf->lua_cpath.data = value[1].data; + + return NGX_CONF_OK; +} + + +char * +ngx_stream_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_lua_main_conf_t *lmcf = conf; + + ngx_str_t *value; + + if (lmcf->lua_path.len != 0) { + return "is duplicate"; + } + + dd("enter"); + + value = cf->args->elts; + + lmcf->lua_path.len = value[1].len; + lmcf->lua_path.data = value[1].data; + + return NGX_CONF_OK; +} + + + + + + + + +char * +ngx_stream_lua_preread_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_preread_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_preread_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + size_t chunkname_len; + u_char *p, *chunkname; + ngx_str_t *value; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_srv_conf_t *lscf = conf; + + ngx_stream_compile_complex_value_t ccv; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->preread_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (value[1].len == 0) { + /* Oops...Invalid server conf */ + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid server config: no runnable Lua code"); + + return NGX_CONF_ERROR; + } + + if (cmd->post == ngx_stream_lua_preread_handler_inline) { + chunkname = ngx_stream_lua_gen_chunk_name(cf, "preread_by_lua", + sizeof("preread_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + lscf->preread_chunkname = chunkname; + + /* Don't eval nginx variables for inline lua code */ + + lscf->preread_src.value = value[1]; + + p = ngx_palloc(cf->pool, + chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->preread_src_key = p; + + p = ngx_copy(p, chunkname, chunkname_len); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &lscf->preread_src; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (lscf->preread_src.lengths == NULL) { + /* no variable found */ + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->preread_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, + NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + } + + lscf->preread_handler = (ngx_stream_lua_handler_pt) cmd->post; + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module); + + lmcf->requires_preread = 1; + + return NGX_CONF_OK; +} + +char * +ngx_stream_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_content_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + size_t chunkname_len; + u_char *p; + u_char *chunkname; + ngx_str_t *value; + + ngx_stream_core_srv_conf_t *cxcf; + + + ngx_stream_compile_complex_value_t ccv; + + ngx_stream_lua_loc_conf_t *llcf = conf; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (llcf->content_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + dd("value[0]: %.*s", (int) value[0].len, value[0].data); + dd("value[1]: %.*s", (int) value[1].len, value[1].data); + + if (value[1].len == 0) { + /* Oops...Invalid location conf */ + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid location config: no runnable Lua code"); + return NGX_CONF_ERROR; + } + + if (cmd->post == ngx_stream_lua_content_handler_inline) { + chunkname = ngx_stream_lua_gen_chunk_name(cf, "content_by_lua", + sizeof("content_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + llcf->content_chunkname = chunkname; + + dd("chunkname: %s", chunkname); + + /* Don't eval nginx variables for inline lua code */ + + llcf->content_src.value = value[1]; + + p = ngx_palloc(cf->pool, + chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + llcf->content_src_key = p; + + p = ngx_copy(p, chunkname, chunkname_len); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &llcf->content_src; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (llcf->content_src.lengths == NULL) { + /* no variable found */ + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + llcf->content_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, + NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + } + + llcf->content_handler = (ngx_stream_lua_handler_pt) cmd->post; + + + /* register location content handler */ + cxcf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); + if (cxcf == NULL) { + return NGX_CONF_ERROR; + } + + cxcf->handler = ngx_stream_lua_content_handler; + + return NGX_CONF_OK; +} + + +char * +ngx_stream_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_log_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + size_t chunkname_len; + u_char *p, *chunkname; + ngx_str_t *value; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_loc_conf_t *llcf = conf; + ngx_stream_compile_complex_value_t ccv; + + dd("enter"); + + /* must specify a log handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (llcf->log_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (value[1].len == 0) { + /* Oops...Invalid location conf */ + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid location config: no runnable Lua code"); + + return NGX_CONF_ERROR; + } + + if (cmd->post == ngx_stream_lua_log_handler_inline) { + chunkname = ngx_stream_lua_gen_chunk_name(cf, "log_by_lua", + sizeof("log_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + llcf->log_chunkname = chunkname; + + /* Don't eval nginx variables for inline lua code */ + + llcf->log_src.value = value[1]; + + p = ngx_palloc(cf->pool, + chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + llcf->log_src_key = p; + + p = ngx_copy(p, chunkname, chunkname_len); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &llcf->log_src; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (llcf->log_src.lengths == NULL) { + /* no variable found */ + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + llcf->log_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, + NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + } + + llcf->log_handler = (ngx_stream_lua_handler_pt) cmd->post; + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module); + + lmcf->requires_log = 1; + + return NGX_CONF_OK; +} + + + + +char * +ngx_stream_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_init_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + u_char *name; + ngx_str_t *value; + ngx_stream_lua_main_conf_t *lmcf = conf; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lmcf->init_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (value[1].len == 0) { + /* Oops...Invalid location conf */ + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid location config: no runnable Lua code"); + return NGX_CONF_ERROR; + } + + lmcf->init_handler = (ngx_stream_lua_main_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_stream_lua_init_by_file) { + name = ngx_stream_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lmcf->init_src.data = name; + lmcf->init_src.len = ngx_strlen(name); + + } else { + lmcf->init_src = value[1]; + } + + return NGX_CONF_OK; +} + + +char * +ngx_stream_lua_init_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_init_worker_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + u_char *name; + ngx_str_t *value; + + ngx_stream_lua_main_conf_t *lmcf = conf; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lmcf->init_worker_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + lmcf->init_worker_handler = (ngx_stream_lua_main_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_stream_lua_init_worker_by_file) { + name = ngx_stream_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lmcf->init_worker_src.data = name; + lmcf->init_worker_src.len = ngx_strlen(name); + + } else { + lmcf->init_worker_src = value[1]; + } + + return NGX_CONF_OK; +} + + + + +static u_char * +ngx_stream_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len, + size_t *chunkname_len) +{ + u_char *p, *out; + size_t len; + + len = sizeof("=(:)") - 1 + tag_len + cf->conf_file->file.name.len + + NGX_INT64_LEN + 1; + + out = ngx_palloc(cf->pool, len); + if (out == NULL) { + return NULL; + } + + if (cf->conf_file->file.name.len) { + p = cf->conf_file->file.name.data + cf->conf_file->file.name.len; + while (--p >= cf->conf_file->file.name.data) { + if (*p == '/' || *p == '\\') { + p++; + goto found; + } + } + + p++; + + } else { + p = cf->conf_file->file.name.data; + } + +found: + + p = ngx_snprintf(out, len, "=%*s(%*s:%d)%Z", + tag_len, tag, cf->conf_file->file.name.data + + cf->conf_file->file.name.len - p, + p, cf->conf_file->line); + + *chunkname_len = p - out - 1; /* exclude the trailing '\0' byte */ + + return out; +} + + +/* a specialized version of the standard ngx_conf_parse() function */ +char * +ngx_stream_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd) +{ + ngx_stream_lua_block_parser_ctx_t ctx; + + int level = 1; + char *rv; + u_char *p; + size_t len; + ngx_str_t *src, *dst; + ngx_int_t rc; + ngx_uint_t i, start_line; + ngx_array_t *saved; + enum { + parse_block = 0, + parse_param + } type; + + if (cf->conf_file->file.fd != NGX_INVALID_FILE) { + + type = parse_block; + + } else { + type = parse_param; + } + + saved = cf->args; + + cf->args = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t)); + if (cf->args == NULL) { + return NGX_CONF_ERROR; + } + + ctx.token_len = 0; + start_line = cf->conf_file->line; + + dd("init start line: %d", (int) start_line); + + ctx.start_line = start_line; + + for ( ;; ) { + rc = ngx_stream_lua_conf_read_lua_token(cf, &ctx); + + dd("parser start line: %d", (int) start_line); + + switch (rc) { + + case NGX_ERROR: + goto done; + + case FOUND_LEFT_CURLY: + + ctx.start_line = cf->conf_file->line; + + if (type == parse_param) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "block directives are not supported " + "in -g option"); + goto failed; + } + + level++; + dd("seen block start: level=%d", (int) level); + break; + + case FOUND_RIGHT_CURLY: + + level--; + dd("seen block done: level=%d", (int) level); + + if (type != parse_block || level < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected \"}\": level %d, " + "starting at line %ui", level, + start_line); + goto failed; + } + + if (level == 0) { + ngx_stream_lua_assert(cf->handler); + + src = cf->args->elts; + + for (len = 0, i = 0; i < cf->args->nelts; i++) { + len += src[i].len; + } + + dd("saved nelts: %d", (int) saved->nelts); + dd("temp nelts: %d", (int) cf->args->nelts); +#if 0 + ngx_stream_lua_assert(saved->nelts == 1); +#endif + + dst = ngx_array_push(saved); + if (dst == NULL) { + return NGX_CONF_ERROR; + } + dst->len = len; + dst->len--; /* skip the trailing '}' block terminator */ + + p = ngx_palloc(cf->pool, len); + if (p == NULL) { + return NGX_CONF_ERROR; + } + dst->data = p; + + for (i = 0; i < cf->args->nelts; i++) { + p = ngx_copy(p, src[i].data, src[i].len); + } + + p[-1] = '\0'; /* override the last '}' char to null */ + + cf->args = saved; + + rv = (*cf->handler)(cf, cmd, cf->handler_conf); + if (rv == NGX_CONF_OK) { + goto done; + } + + if (rv == NGX_CONF_ERROR) { + goto failed; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); + + goto failed; + } + + break; + + case FOUND_LBRACKET_STR: + case FOUND_LBRACKET_CMT: + case FOUND_RIGHT_LBRACKET: + case FOUND_COMMENT_LINE: + case FOUND_DOUBLE_QUOTED: + case FOUND_SINGLE_QUOTED: + break; + + default: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown return value from the lexer: %i", rc); + goto failed; + } + } + +failed: + + rc = NGX_ERROR; + +done: + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_lua_conf_read_lua_token(ngx_conf_t *cf, + ngx_stream_lua_block_parser_ctx_t *ctx) +{ + enum { + OVEC_SIZE = 2 + }; + int i, rc; + int ovec[OVEC_SIZE]; + u_char *start, *p, *q, ch; + off_t file_size; + size_t len, buf_size; + ssize_t n, size; + ngx_uint_t start_line; + ngx_str_t *word; + ngx_buf_t *b; +#if defined(nginx_version) && nginx_version >= 1009002 + ngx_buf_t *dump; +#endif + + b = cf->conf_file->buffer; +#if defined(nginx_version) && nginx_version >= 1009002 + dump = cf->conf_file->dump; +#endif + start = b->pos; + start_line = cf->conf_file->line; + buf_size = b->end - b->start; + + dd("lexer start line: %d", (int) start_line); + + file_size = ngx_file_size(&cf->conf_file->file.info); + + for ( ;; ) { + + if (b->pos >= b->last + || (b->last - b->pos < (b->end - b->start) / 3 + && cf->conf_file->file.offset < file_size)) + { + + if (cf->conf_file->file.offset >= file_size) { + + cf->conf_file->line = ctx->start_line; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of file, expecting " + "terminating characters for lua code " + "block"); + return NGX_ERROR; + } + + len = b->last - start; + + if (len == buf_size) { + + cf->conf_file->line = start_line; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too long lua code block, probably " + "missing terminating characters"); + + return NGX_ERROR; + } + + if (len) { + ngx_memmove(b->start, start, len); + } + + size = (ssize_t) (file_size - cf->conf_file->file.offset); + + if (size > b->end - (b->start + len)) { + size = b->end - (b->start + len); + } + + n = ngx_read_file(&cf->conf_file->file, b->start + len, size, + cf->conf_file->file.offset); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + if (n != size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_read_file_n " returned " + "only %z bytes instead of %z", + n, size); + return NGX_ERROR; + } + + b->pos = b->start + (b->pos - start); + b->last = b->start + len + n; + start = b->start; + +#if defined(nginx_version) && nginx_version >= 1009002 + if (dump) { + dump->last = ngx_cpymem(dump->last, b->start + len, size); + } +#endif + } + + rc = ngx_stream_lua_lex(b->pos, b->last - b->pos, ovec); + + if (rc < 0) { /* no match */ + /* alas. the lexer does not yet support streaming processing. need + * more work below */ + + if (cf->conf_file->file.offset >= file_size) { + + cf->conf_file->line = ctx->start_line; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of file, expecting " + "terminating characters for lua code " + "block"); + return NGX_ERROR; + } + + len = b->last - b->pos; + + if (len == buf_size) { + + cf->conf_file->line = start_line; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too long lua code block, probably " + "missing terminating characters"); + + return NGX_ERROR; + } + + if (len) { + ngx_memcpy(b->start, b->pos, len); + } + + size = (ssize_t) (file_size - cf->conf_file->file.offset); + + if (size > b->end - (b->start + len)) { + size = b->end - (b->start + len); + } + + n = ngx_read_file(&cf->conf_file->file, b->start + len, size, + cf->conf_file->file.offset); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + if (n != size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_read_file_n " returned " + "only %z bytes instead of %z", + n, size); + return NGX_ERROR; + } + + b->pos = b->start + len; + b->last = b->pos + n; + start = b->start; + + continue; + } + + if (rc == FOUND_LEFT_LBRACKET_STR || rc == FOUND_LEFT_LBRACKET_CMT) { + + /* we update the line numbers for best error messages when the + * closing long bracket is missing */ + + for (i = 0; i < ovec[0]; i++) { + ch = b->pos[i]; + if (ch == LF) { + cf->conf_file->line++; + } + } + + b->pos += ovec[0]; + ovec[1] -= ovec[0]; + ovec[0] = 0; + + if (rc == FOUND_LEFT_LBRACKET_CMT) { + p = &b->pos[2]; /* we skip the leading "--" prefix */ + rc = FOUND_LBRACKET_CMT; + + } else { + p = b->pos; + rc = FOUND_LBRACKET_STR; + } + + /* we temporarily rewrite [=*[ in the input buffer to ]=*] to + * construct the pattern for the corresponding closing long + * bracket without additional buffers. */ + + ngx_stream_lua_assert(p[0] == '['); + p[0] = ']'; + + ngx_stream_lua_assert(b->pos[ovec[1] - 1] == '['); + b->pos[ovec[1] - 1] = ']'; + + /* search for the corresponding closing bracket */ + + dd("search pattern for the closing long bracket: \"%.*s\" (len=%d)", + (int) (b->pos + ovec[1] - p), p, (int) (b->pos + ovec[1] - p)); + + q = ngx_stream_lua_strlstrn(b->pos + ovec[1], b->last, p, + b->pos + ovec[1] - p - 1); + + if (q == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "Lua code block missing the closing " + "long bracket \"%*s\"", + b->pos + ovec[1] - p, p); + return NGX_ERROR; + } + + /* restore the original opening long bracket */ + + p[0] = '['; + b->pos[ovec[1] - 1] = '['; + + ovec[1] = q - b->pos + b->pos + ovec[1] - p; + + dd("found long bracket token: \"%.*s\"", + (int) (ovec[1] - ovec[0]), b->pos + ovec[0]); + } + + for (i = 0; i < ovec[1]; i++) { + ch = b->pos[i]; + if (ch == LF) { + cf->conf_file->line++; + } + } + + b->pos += ovec[1]; + ctx->token_len = ovec[1] - ovec[0]; + + break; + } + + word = ngx_array_push(cf->args); + if (word == NULL) { + return NGX_ERROR; + } + + word->data = ngx_pnalloc(cf->temp_pool, b->pos - start); + if (word->data == NULL) { + return NGX_ERROR; + } + + len = b->pos - start; + ngx_memcpy(word->data, start, len); + word->len = len; + + return rc; +} + + +char * +ngx_stream_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#ifndef HAVE_INTERCEPT_ERROR_LOG_PATCH + return "not found: missing the capture error log patch for nginx"; +#else + ngx_str_t *value; + ssize_t size; + u_char *data; + ngx_cycle_t *cycle; + + ngx_stream_lua_main_conf_t *lmcf = conf; + ngx_stream_lua_log_ringbuf_t *ringbuf; + + value = cf->args->elts; + cycle = cf->cycle; + + if (lmcf->requires_capture_log) { + return "is duplicate"; + } + + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid capture error log size \"%V\"", + &value[1]); + return NGX_CONF_ERROR; + } + + size = ngx_parse_size(&value[1]); + + if (size < NGX_MAX_ERROR_STR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid capture error log size \"%V\", " + "minimum size is %d", &value[1], + NGX_MAX_ERROR_STR); + return NGX_CONF_ERROR; + } + + if (cycle->intercept_error_log_handler) { + return "capture error log handler has been hooked"; + } + + ringbuf = (ngx_stream_lua_log_ringbuf_t *) + ngx_palloc(cf->pool, sizeof(ngx_stream_lua_log_ringbuf_t)); + if (ringbuf == NULL) { + return NGX_CONF_ERROR; + } + + data = ngx_palloc(cf->pool, size); + if (data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_stream_lua_log_ringbuf_init(ringbuf, data, size); + + lmcf->requires_capture_log = 1; + cycle->intercept_error_log_handler = (ngx_log_intercept_pt) + ngx_stream_lua_capture_log_handler; + cycle->intercept_error_log_data = ringbuf; + + return NGX_CONF_OK; +#endif +} + + +/* + * ngx_stream_lua_strlstrn() is intended to search for static substring + * with known length in string until the argument last. The argument n + * must be length of the second substring - 1. + */ + +static u_char * +ngx_stream_lua_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + last -= n; + + do { + do { + if (s1 >= last) { + return NULL; + } + + c1 = (ngx_uint_t) *s1++; + + dd("testing char '%c' vs '%c'", (int) c1, (int) c2); + + } while (c1 != c2); + + dd("testing against pattern \"%.*s\"", (int) n, s2); + + } while (ngx_strncmp(s1, s2, n) != 0); + + return --s1; +} + + +static ngx_int_t +ngx_stream_lua_undefined_var(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->not_found = 1; + + return NGX_OK; +} + + +char * +ngx_stream_lua_add_variable(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_stream_variable_t *var; + ngx_str_t *value; + ngx_int_t ret; + + value = cf->args->elts; + + if (value[1].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + value[1].len--; + value[1].data++; + + var = ngx_stream_add_variable(cf, value + 1, NGX_STREAM_VAR_CHANGEABLE + |NGX_STREAM_VAR_WEAK); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + if (var->get_handler == NULL) { + var->get_handler = ngx_stream_lua_undefined_var; + } + + ret = ngx_stream_get_variable_index(cf, value + 1); + if (ret == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_directive.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_directive.h new file mode 100644 index 000000000..6b36bb134 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_directive.h @@ -0,0 +1,70 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_directive.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_DIRECTIVE_H_INCLUDED_ +#define _NGX_STREAM_LUA_DIRECTIVE_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +char *ngx_stream_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +char *ngx_stream_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_init_worker_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +char *ngx_stream_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_stream_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +char * +ngx_stream_lua_preread_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char * +ngx_stream_lua_preread_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char * +ngx_stream_lua_add_variable(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +char *ngx_stream_lua_conf_lua_block_parse(ngx_conf_t *cf, + ngx_command_t *cmd); + + +char *ngx_stream_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +#endif /* _NGX_STREAM_LUA_DIRECTIVE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_exception.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_exception.c new file mode 100644 index 000000000..c1aaed323 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_exception.c @@ -0,0 +1,66 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_exception.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_exception.h" +#include "ngx_stream_lua_util.h" + + +/* longjmp mark for restoring nginx execution after Lua VM crashing */ +jmp_buf ngx_stream_lua_exception; + +/** + * Override default Lua panic handler, output VM crash reason to nginx error + * log, and restore execution to the nearest jmp-mark. + * + * @param L Lua state pointer + * @retval Long jump to the nearest jmp-mark, never returns. + * @note nginx request pointer should be stored in Lua thread's globals table + * in order to make logging working. + * */ +int +ngx_stream_lua_atpanic(lua_State *L) +{ +#ifdef NGX_LUA_ABORT_AT_PANIC + abort(); +#else + u_char *s = NULL; + size_t len = 0; + + if (lua_type(L, -1) == LUA_TSTRING) { + s = (u_char *) lua_tolstring(L, -1, &len); + } + + if (s == NULL) { + s = (u_char *) "unknown reason"; + len = sizeof("unknown reason") - 1; + } + + ngx_log_stderr(0, "lua atpanic: Lua VM crashed, reason: %*s", len, s); + ngx_quit = 1; + + /* restore nginx execution */ + NGX_LUA_EXCEPTION_THROW(1); + + /* impossible to reach here */ +#endif +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_exception.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_exception.h new file mode 100644 index 000000000..2e90863fb --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_exception.h @@ -0,0 +1,41 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_exception.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_EXCEPTION_H_INCLUDED_ +#define _NGX_STREAM_LUA_EXCEPTION_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +#define NGX_LUA_EXCEPTION_TRY \ + if (setjmp(ngx_stream_lua_exception) == 0) + +#define NGX_LUA_EXCEPTION_CATCH \ + else + +#define NGX_LUA_EXCEPTION_THROW(x) \ + longjmp(ngx_stream_lua_exception, (x)) + + +extern jmp_buf ngx_stream_lua_exception; + + +int ngx_stream_lua_atpanic(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_EXCEPTION_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initby.c new file mode 100644 index 000000000..c6bbf0d36 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initby.c @@ -0,0 +1,50 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_initby.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif + +#include "ddebug.h" +#include "ngx_stream_lua_initby.h" +#include "ngx_stream_lua_util.h" + + +ngx_int_t +ngx_stream_lua_init_by_inline(ngx_log_t *log, ngx_stream_lua_main_conf_t *lmcf, + lua_State *L) +{ + int status; + + status = luaL_loadbuffer(L, (char *) lmcf->init_src.data, + lmcf->init_src.len, "=init_by_lua") + || ngx_stream_lua_do_call(log, L); + + return ngx_stream_lua_report(log, L, status, "init_by_lua"); +} + + +ngx_int_t +ngx_stream_lua_init_by_file(ngx_log_t *log, ngx_stream_lua_main_conf_t *lmcf, + lua_State *L) +{ + int status; + + status = luaL_loadfile(L, (char *) lmcf->init_src.data) + || ngx_stream_lua_do_call(log, L); + + return ngx_stream_lua_report(log, L, status, "init_by_lua_file"); +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initby.h new file mode 100644 index 000000000..f2e2c40ce --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initby.h @@ -0,0 +1,31 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_initby.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_INITBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_INITBY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_init_by_inline(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L); + +ngx_int_t ngx_stream_lua_init_by_file(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L); + + +#endif /* _NGX_STREAM_LUA_INITBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initworkerby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initworkerby.c new file mode 100644 index 000000000..786ba3441 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initworkerby.c @@ -0,0 +1,366 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_initworkerby.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_initworkerby.h" +#include "ngx_stream_lua_util.h" + +#include "ngx_stream_lua_contentby.h" + + + +static u_char *ngx_stream_lua_log_init_worker_error(ngx_log_t *log, + u_char *buf, size_t len); + + +ngx_int_t +ngx_stream_lua_init_worker(ngx_cycle_t *cycle) +{ + char *rv; + void *cur, *prev; + ngx_uint_t i; + ngx_conf_t conf; + ngx_cycle_t *fake_cycle; + ngx_module_t **modules; + ngx_open_file_t *file, *ofile; + ngx_list_part_t *part; + ngx_connection_t *c = NULL; + ngx_stream_module_t *module; + ngx_stream_lua_request_t *r = NULL; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_conf_ctx_t *conf_ctx, stream_ctx; + + ngx_stream_lua_main_conf_t *lmcf; + + ngx_conf_file_t *conf_file; + ngx_stream_session_t *s; + + ngx_stream_core_srv_conf_t *cscf, *top_cscf; + ngx_stream_lua_srv_conf_t *lscf, *top_lscf; + + lmcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_lua_module); + + if (lmcf == NULL || lmcf->lua == NULL) { + return NGX_OK; + } + + /* lmcf != NULL && lmcf->lua != NULL */ + +#if !(NGX_WIN32) + if (ngx_process == NGX_PROCESS_HELPER +# ifdef HAVE_PRIVILEGED_PROCESS_PATCH + && !ngx_is_privileged_agent +# endif + ) + { + /* disable init_worker_by_lua* and destroy lua VM in cache processes */ + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "lua close the global Lua VM %p in the " + "cache helper process %P", lmcf->lua, ngx_pid); + + lmcf->vm_cleanup->handler(lmcf->vm_cleanup->data); + lmcf->vm_cleanup->handler = NULL; + + return NGX_OK; + } + + +#endif /* NGX_WIN32 */ + +#if (NGX_STREAM_LUA_HAVE_SA_RESTART) + if (lmcf->set_sa_restart) { + ngx_stream_lua_set_sa_restart(ngx_cycle->log); + } +#endif + + if (lmcf->init_worker_handler == NULL) { + return NGX_OK; + } + + conf_ctx = (ngx_stream_conf_ctx_t *) + cycle->conf_ctx[ngx_stream_module.index]; + stream_ctx.main_conf = conf_ctx->main_conf; + + top_cscf = conf_ctx->srv_conf[ngx_stream_core_module.ctx_index]; + top_lscf = conf_ctx->srv_conf[ngx_stream_lua_module.ctx_index]; + + ngx_memzero(&conf, sizeof(ngx_conf_t)); + + conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, cycle->log); + if (conf.temp_pool == NULL) { + return NGX_ERROR; + } + + conf.temp_pool->log = cycle->log; + + /* we fake a temporary ngx_cycle_t here because some + * modules' merge conf handler may produce side effects in + * cf->cycle (like ngx_proxy vs cf->cycle->paths). + * also, we cannot allocate our temp cycle on the stack + * because some modules like ngx_stream_core_module reference + * addresses within cf->cycle (i.e., via "&cf->cycle->new_log") + */ + + fake_cycle = ngx_palloc(cycle->pool, sizeof(ngx_cycle_t)); + if (fake_cycle == NULL) { + goto failed; + } + + ngx_memcpy(fake_cycle, cycle, sizeof(ngx_cycle_t)); + + ngx_queue_init(&fake_cycle->reusable_connections_queue); + + if (ngx_array_init(&fake_cycle->listening, cycle->pool, + cycle->listening.nelts || 1, + sizeof(ngx_listening_t)) + != NGX_OK) + { + goto failed; + } + + if (ngx_array_init(&fake_cycle->paths, cycle->pool, cycle->paths.nelts || 1, + sizeof(ngx_path_t *)) + != NGX_OK) + { + goto failed; + } + + part = &cycle->open_files.part; + ofile = part->elts; + + if (ngx_list_init(&fake_cycle->open_files, cycle->pool, part->nelts || 1, + sizeof(ngx_open_file_t)) + != NGX_OK) + { + goto failed; + } + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + ofile = part->elts; + i = 0; + } + + file = ngx_list_push(&fake_cycle->open_files); + if (file == NULL) { + goto failed; + } + + ngx_memcpy(file, ofile, sizeof(ngx_open_file_t)); + } + + if (ngx_list_init(&fake_cycle->shared_memory, cycle->pool, 1, + sizeof(ngx_shm_zone_t)) + != NGX_OK) + { + goto failed; + } + + conf_file = ngx_pcalloc(fake_cycle->pool, sizeof(ngx_conf_file_t)); + if (conf_file == NULL) { + return NGX_ERROR; + } + + /* workaround to make ngx_stream_core_create_srv_conf not SEGFAULT */ + conf_file->file.name.data = (u_char *) "dummy"; + conf_file->file.name.len = sizeof("dummy") - 1; + conf_file->line = 1; + conf.conf_file = conf_file; + + conf.ctx = &stream_ctx; + conf.cycle = fake_cycle; + conf.pool = fake_cycle->pool; + conf.log = cycle->log; + + + stream_ctx.srv_conf = ngx_pcalloc(conf.pool, + sizeof(void *) * ngx_stream_max_module); + if (stream_ctx.srv_conf == NULL) { + return NGX_ERROR; + } + +#if defined(nginx_version) && nginx_version >= 1009011 + modules = cycle->modules; +#else + modules = ngx_modules; +#endif + + for (i = 0; modules[i]; i++) { + if (modules[i]->type != NGX_STREAM_MODULE) { + continue; + } + + module = modules[i]->ctx; + + if (module->create_srv_conf) { + cur = module->create_srv_conf(&conf); + if (cur == NULL) { + return NGX_ERROR; + } + + stream_ctx.srv_conf[modules[i]->ctx_index] = cur; + + if (modules[i]->ctx_index == ngx_stream_core_module.ctx_index) { + cscf = cur; + /* just to silence the error in + * ngx_stream_core_merge_srv_conf */ + cscf->handler = ngx_stream_lua_content_handler; + } + + if (module->merge_srv_conf) { + if (modules[i] == &ngx_stream_lua_module) { + prev = top_lscf; + + } else if (modules[i] == &ngx_stream_core_module) { + prev = top_cscf; + + } else { + prev = module->create_srv_conf(&conf); + if (prev == NULL) { + return NGX_ERROR; + } + } + + rv = module->merge_srv_conf(&conf, prev, cur); + if (rv != NGX_CONF_OK) { + goto failed; + } + } + } + + } + + ngx_destroy_pool(conf.temp_pool); + conf.temp_pool = NULL; + + c = ngx_stream_lua_create_fake_connection(NULL); + if (c == NULL) { + goto failed; + } + + c->log->handler = ngx_stream_lua_log_init_worker_error; + + s = ngx_stream_lua_create_fake_session(c); + if (s == NULL) { + goto failed; + } + + s->main_conf = stream_ctx.main_conf; + s->srv_conf = stream_ctx.srv_conf; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); + + if (top_lscf->log_socket_errors != NGX_CONF_UNSET) { + lscf->log_socket_errors = top_lscf->log_socket_errors; + } + + if (top_cscf->resolver != NULL) { + cscf->resolver = top_cscf->resolver; + } + + if (top_cscf->resolver_timeout != NGX_CONF_UNSET_MSEC) { + cscf->resolver_timeout = top_cscf->resolver_timeout; + } + +#if defined(nginx_version) && nginx_version >= 1009000 + ngx_set_connection_log(s->connection, cscf->error_log); + +#else +#endif + + ctx = ngx_stream_lua_create_ctx(s); + if (ctx == NULL) { + goto failed; + } + + r = ctx->request; + + ctx->context = NGX_STREAM_LUA_CONTEXT_INIT_WORKER; + ctx->cur_co_ctx = NULL; + r->read_event_handler = ngx_stream_lua_block_reading; + + ngx_stream_lua_set_req(lmcf->lua, r); + + (void) lmcf->init_worker_handler(cycle->log, lmcf, lmcf->lua); + + ngx_destroy_pool(c->pool); + return NGX_OK; + +failed: + + if (conf.temp_pool) { + ngx_destroy_pool(conf.temp_pool); + } + + if (c) { + ngx_stream_lua_close_fake_connection(c); + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_stream_lua_init_worker_by_inline(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L) +{ + int status; + + status = luaL_loadbuffer(L, (char *) lmcf->init_worker_src.data, + lmcf->init_worker_src.len, "=init_worker_by_lua") + || ngx_stream_lua_do_call(log, L); + + return ngx_stream_lua_report(log, L, status, "init_worker_by_lua"); +} + + +ngx_int_t +ngx_stream_lua_init_worker_by_file(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L) +{ + int status; + + status = luaL_loadfile(L, (char *) lmcf->init_worker_src.data) + || ngx_stream_lua_do_call(log, L); + + return ngx_stream_lua_report(log, L, status, "init_worker_by_lua_file"); +} + + +static u_char * +ngx_stream_lua_log_init_worker_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + return ngx_snprintf(buf, len, ", context: init_worker_by_lua*"); +} diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initworkerby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initworkerby.h new file mode 100644 index 000000000..43f6de3bd --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_initworkerby.h @@ -0,0 +1,33 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_initworkerby.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_INITWORKERBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_INITWORKERBY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_init_worker_by_inline(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L); + +ngx_int_t ngx_stream_lua_init_worker_by_file(ngx_log_t *log, + ngx_stream_lua_main_conf_t *lmcf, lua_State *L); + +ngx_int_t ngx_stream_lua_init_worker(ngx_cycle_t *cycle); + + +#endif /* _NGX_STREAM_LUA_INITWORKERBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_input_filters.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_input_filters.c new file mode 100644 index 000000000..159185cca --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_input_filters.c @@ -0,0 +1,146 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_input_filters.c.tt2 + */ + + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t +ngx_stream_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in, + size_t *rest, ssize_t bytes, ngx_log_t *log) +{ + if (bytes == 0) { + return NGX_ERROR; + } + + if ((size_t) bytes >= *rest) { + + buf_in->buf->last += *rest; + src->pos += *rest; + *rest = 0; + + return NGX_OK; + } + + /* bytes < *rest */ + + buf_in->buf->last += bytes; + src->pos += bytes; + *rest -= bytes; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_stream_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log) +{ + if (bytes == 0) { + return NGX_OK; + } + + buf_in->buf->last += bytes; + src->pos += bytes; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_stream_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *max, + ssize_t bytes, ngx_log_t *log) +{ + if (bytes == 0) { + return NGX_ERROR; + } + + if (bytes >= (ssize_t) *max) { + bytes = (ssize_t) *max; + } + + buf_in->buf->last += bytes; + src->pos += bytes; + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log) +{ + u_char *dst; + u_char c; +#if (NGX_DEBUG) + u_char *begin; +#endif + +#if (NGX_DEBUG) + begin = src->pos; +#endif + + if (bytes == 0) { + return NGX_ERROR; + } + + dd("already read: %p: %.*s", buf_in, + (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos); + + dd("data read: %.*s", (int) bytes, src->pos); + + dst = buf_in->buf->last; + + while (bytes--) { + + c = *src->pos++; + + switch (c) { + case '\n': + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0, + "stream lua read the final line part: " + "\"%*s\"", src->pos - 1 - begin, begin); + + buf_in->buf->last = dst; + + dd("read a line: %p: %.*s", buf_in, + (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos); + + return NGX_OK; + + case '\r': + /* ignore it */ + break; + + default: + *dst++ = c; + break; + } + } + +#if (NGX_DEBUG) + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0, + "stream lua read partial line data: %*s", + dst - begin, begin); +#endif + + buf_in->buf->last = dst; + + return NGX_AGAIN; +} diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_input_filters.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_input_filters.h new file mode 100644 index 000000000..e65d572fa --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_input_filters.h @@ -0,0 +1,37 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_input_filters.h.tt2 + */ + + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ +#define _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in, + size_t *rest, ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_stream_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_stream_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, + size_t *max, ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_stream_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log); + + +#endif /* _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_lex.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_lex.c new file mode 100644 index 000000000..cb1fbc04c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_lex.c @@ -0,0 +1,8259 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_lex.c.tt2 + */ + +/* + * Copyright (C) Yichun Zhang (agentzh) + * + * WARNING: DO NOT EVER EDIT THIS FILE!! + * + * This file was automatically generated by the re.pl script of sregex's + * "dfa-multi-re" git branch. + */ + + +#include "ngx_stream_lua_lex.h" +#include +#include +#include +#include +#include +#include + + +#if __GNUC__ > 3 +# define likely(x) __builtin_expect((x),1) +# define unlikely(x) __builtin_expect((x),0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + + +#ifndef u_char +#define u_char unsigned char +#endif + + +enum { + NO_MATCH = -1, +}; + + +/* + * ngx_stream_lua_lex: the "ovec" array should be allocated by the + * caller with at least 2 elements. + */ +int +ngx_stream_lua_lex(const u_char *const s, size_t len, int *const ovec) +{ + unsigned i = 0; + int matched_0 = -1; + int matched_1 = -1; + int matched_id = NO_MATCH; /* (pending) matched regex ID */ + int c; + int caps0_0 = -1; + int caps0_10 = -1; + int caps0_12 = -1; + int caps0_14 = -1; + int caps0_2 = -1; + int caps0_4 = -1; + int caps0_6 = -1; + int caps0_8 = -1; + int caps1_0 = -1; + int caps1_10 = -1; + int caps1_12 = -1; + int caps1_14 = -1; + int caps1_2 = -1; + int caps1_4 = -1; + int caps1_6 = -1; + int caps1_8 = -1; + int caps2_0 = -1; + int caps2_10 = -1; + int caps2_2 = -1; + int caps2_4 = -1; + int caps2_6 = -1; + int caps2_8 = -1; + int caps3_10 = -1; + + { /* DFA node {0} 0 */ + if (unlikely(i >= len)) { + i++; + goto st0_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 0 to row 1 */ + /* transfer caps from row 0 to row 2 */ + /* capture stores */ + caps0_6 = i - 1; + caps1_10 = i - 1; + goto st4; + break; + } + case 91: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_4 = i - 1; + goto st5; + break; + } + case 93: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_8 = i - 1; + goto st6; + break; + } + case 123: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st1; + } /* end state */ + + goto st0_error; + +st1: { /* DFA node {1} 1 */ + if (unlikely(i >= len)) { + i++; + goto st1_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 0 to row 1 */ + /* transfer caps from row 0 to row 2 */ + /* capture stores */ + caps0_6 = i - 1; + caps1_10 = i - 1; + goto st4; + break; + } + case 91: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_4 = i - 1; + goto st5; + break; + } + case 93: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_8 = i - 1; + goto st6; + break; + } + case 123: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 0 to row 1 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st1; + } /* end state */ + + goto st1_error; + +st2: { /* DFA node {59,1} 2 */ + if (unlikely(i >= len)) { + i++; + goto st2_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + goto st14; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st9; + } /* end state */ + + goto st2_error; + +st3: { /* DFA node {72,1} 3 */ + if (unlikely(i >= len)) { + i++; + goto st3_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + goto st23; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st18; + } /* end state */ + + goto st3_error; + +st4: { /* DFA node {30,50,1} 4 */ + if (unlikely(i >= len)) { + i++; + goto st4_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 2 to row 0 */ + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 2 to row 0 */ + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st27; + break; + } + case 91: { + /* transfer caps from row 2 to row 0 */ + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps0_4 = i - 1; + goto st5; + break; + } + case 93: { + /* transfer caps from row 2 to row 0 */ + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps0_8 = i - 1; + goto st6; + break; + } + case 123: { + /* transfer caps from row 2 to row 0 */ + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 2 to row 0 */ + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 0 */ + goto st1; + } /* end state */ + + goto st4_error; + +st5: { /* DFA node {21,1} 5 */ + if (unlikely(i >= len)) { + i++; + goto st5_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 1 to row 0 */ + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps0_6 = i - 1; + caps1_10 = i - 1; + goto st4; + break; + } + case 61: { + goto st28; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st29; + break; + } + case 93: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_8 = i - 1; + goto st6; + break; + } + case 123: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 1 to row 0 */ + goto st1; + } /* end state */ + + goto st5_error; + +st6: { /* DFA node {41,1} 6 */ + if (unlikely(i >= len)) { + i++; + goto st6_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 1 to row 0 */ + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps0_6 = i - 1; + caps1_10 = i - 1; + goto st4; + break; + } + case 61: { + goto st30; + break; + } + case 91: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_4 = i - 1; + goto st5; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st31; + break; + } + case 123: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 1 to row 0 */ + goto st1; + } /* end state */ + + goto st6_error; + +st7: { /* DFA node {11,1} 7 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st8: { /* DFA node {16,1} 8 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st9: { /* DFA node {65,1} 9 */ + if (unlikely(i >= len)) { + i++; + goto st9_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + goto st14; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st9; + } /* end state */ + + goto st9_error; + +st10: { /* DFA node {67,59,1} 10 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st11: { /* DFA node {65,72,1} 11 */ + if (unlikely(i >= len)) { + i++; + goto st11_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + goto st40; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st35; + } /* end state */ + + goto st11_error; + +st12: { /* DFA node {65,30,50,1} 12 */ + if (unlikely(i >= len)) { + i++; + goto st12_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 3 to row 4 */ + /* transfer caps from row 3 to row 5 */ + /* capture stores */ + goto st44; + break; + } + case 91: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + /* transfer caps from row 3 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 1 */ + goto st9; + } /* end state */ + + goto st12_error; + +st13: { /* DFA node {65,21,1} 13 */ + if (unlikely(i >= len)) { + i++; + goto st13_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 61: { + goto st45; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st46; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st13_error; + +st14: { /* DFA node {62,1} 14 */ + if (unlikely(i >= len)) { + i++; + goto st14_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st48; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st49; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st50; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st51; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st52; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st53; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st54; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st47; + } /* end state */ + + goto st14_error; + +st15: { /* DFA node {65,41,1} 15 */ + if (unlikely(i >= len)) { + i++; + goto st15_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 61: { + goto st55; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st56; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st15_error; + +st16: { /* DFA node {65,11,1} 16 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st17: { /* DFA node {65,16,1} 17 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st18: { /* DFA node {78,1} 18 */ + if (unlikely(i >= len)) { + i++; + goto st18_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + goto st23; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st18; + } /* end state */ + + goto st18_error; + +st19: { /* DFA node {78,59,1} 19 */ + if (unlikely(i >= len)) { + i++; + goto st19_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + goto st65; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st60; + } /* end state */ + + goto st19_error; + +st20: { /* DFA node {80,72,1} 20 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st21: { /* DFA node {78,30,50,1} 21 */ + if (unlikely(i >= len)) { + i++; + goto st21_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 3 to row 4 */ + /* transfer caps from row 3 to row 5 */ + /* capture stores */ + goto st70; + break; + } + case 91: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + /* transfer caps from row 3 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 1 */ + goto st18; + } /* end state */ + + goto st21_error; + +st22: { /* DFA node {78,21,1} 22 */ + if (unlikely(i >= len)) { + i++; + goto st22_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 61: { + goto st71; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st72; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st22_error; + +st23: { /* DFA node {75,1} 23 */ + if (unlikely(i >= len)) { + i++; + goto st23_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st74; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st75; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st76; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st77; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st78; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st79; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st80; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st73; + } /* end state */ + + goto st23_error; + +st24: { /* DFA node {78,41,1} 24 */ + if (unlikely(i >= len)) { + i++; + goto st24_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 61: { + goto st81; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st82; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st24_error; + +st25: { /* DFA node {78,11,1} 25 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st26: { /* DFA node {78,16,1} 26 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st27: { /* DFA node {31,51,30,50,1} 27 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 91) { + goto st88; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 90) + || (c >= 92 && c <= 255)) + { + /* transfer caps from row 1 to row 0 */ + caps0_10 = caps1_10; + goto st87; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st28: { /* DFA node {23,1} 28 */ + if (unlikely(i >= len)) { + i++; + goto st28_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 1 to row 0 */ + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps0_6 = i - 1; + caps1_10 = i - 1; + goto st4; + break; + } + case 61: { + goto st28; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st29; + break; + } + case 93: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_8 = i - 1; + goto st6; + break; + } + case 123: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 1 to row 0 */ + goto st1; + } /* end state */ + + goto st28_error; + +st29: { /* DFA node {25,21,1} 29 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_4; + /* capture stores */ + matched_1 = i - 1; + matched_id = 2; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st30: { /* DFA node {43,1} 30 */ + if (unlikely(i >= len)) { + i++; + goto st30_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_12 = i - 1; + goto st2; + break; + } + case 39: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_14 = i - 1; + goto st3; + break; + } + case 45: { + /* transfer caps from row 1 to row 0 */ + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps0_6 = i - 1; + caps1_10 = i - 1; + goto st4; + break; + } + case 61: { + goto st30; + break; + } + case 91: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_4 = i - 1; + goto st5; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st31; + break; + } + case 123: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_0 = i - 1; + goto st7; + break; + } + case 125: { + /* transfer caps from row 1 to row 0 */ + /* capture stores */ + caps0_2 = i - 1; + goto st8; + break; + } + default: + break; + } + /* (c >= 0 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 1 to row 0 */ + goto st1; + } /* end state */ + + goto st30_error; + +st31: { /* DFA node {45,41,1} 31 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_8; + /* capture stores */ + matched_1 = i - 1; + matched_id = 4; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st35: { /* DFA node {65,78,1} 35 */ + if (unlikely(i >= len)) { + i++; + goto st35_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + goto st40; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st35; + } /* end state */ + + goto st35_error; + +st36: { /* DFA node {67,78,59,1} 36 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st37: { /* DFA node {65,80,72,1} 37 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st38: { /* DFA node {65,78,30,50,1} 38 */ + if (unlikely(i >= len)) { + i++; + goto st38_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 4 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 4 to row 5 */ + /* transfer caps from row 4 to row 6 */ + /* capture stores */ + goto st91; + break; + } + case 91: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 4 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 4 to row 2 */ + goto st35; + } /* end state */ + + goto st38_error; + +st39: { /* DFA node {65,78,21,1} 39 */ + if (unlikely(i >= len)) { + i++; + goto st39_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 61: { + goto st92; + break; + } + case 91: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st93; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st39_error; + +st40: { /* DFA node {62,75,1} 40 */ + if (unlikely(i >= len)) { + i++; + goto st40_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st95; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st96; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st97; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st98; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st99; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st100; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st101; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st94; + } /* end state */ + + goto st40_error; + +st41: { /* DFA node {65,78,41,1} 41 */ + if (unlikely(i >= len)) { + i++; + goto st41_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 61: { + goto st102; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st103; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st41_error; + +st42: { /* DFA node {65,78,11,1} 42 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st43: { /* DFA node {65,78,16,1} 43 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st44: { /* DFA node {65,31,51,30,50,1} 44 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st109; + } + if (c == 91) { + goto st110; + } + if (c == 92) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st111; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st108; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st45: { /* DFA node {65,23,1} 45 */ + if (unlikely(i >= len)) { + i++; + goto st45_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 61: { + goto st45; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st46; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st45_error; + +st46: { /* DFA node {65,25,21,1} 46 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_4; + /* capture stores */ + matched_1 = i - 1; + matched_id = 2; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st47: { /* DFA node {63,1} 47 */ + if (unlikely(i >= len)) { + i++; + goto st47_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + goto st14; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st9; + } /* end state */ + + goto st47_error; + +st48: { /* DFA node {63,59,1} 48 */ + if (unlikely(i >= len)) { + i++; + goto st48_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st48_error; + +st49: { /* DFA node {63,72,1} 49 */ + if (unlikely(i >= len)) { + i++; + goto st49_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + goto st40; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st35; + } /* end state */ + + goto st49_error; + +st50: { /* DFA node {63,30,50,1} 50 */ + if (unlikely(i >= len)) { + i++; + goto st50_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 3 to row 4 */ + /* transfer caps from row 3 to row 5 */ + /* capture stores */ + goto st44; + break; + } + case 91: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + /* transfer caps from row 3 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 1 */ + goto st9; + } /* end state */ + + goto st50_error; + +st51: { /* DFA node {63,21,1} 51 */ + if (unlikely(i >= len)) { + i++; + goto st51_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 61: { + goto st45; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st46; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st15; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st51_error; + +st52: { /* DFA node {63,41,1} 52 */ + if (unlikely(i >= len)) { + i++; + goto st52_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 61: { + goto st55; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st56; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st52_error; + +st53: { /* DFA node {63,11,1} 53 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st54: { /* DFA node {63,16,1} 54 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st55: { /* DFA node {65,43,1} 55 */ + if (unlikely(i >= len)) { + i++; + goto st55_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st10; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st11; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st12; + break; + } + case 61: { + goto st55; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st13; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st14; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st56; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st16; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st17; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st9; + } /* end state */ + + goto st55_error; + +st56: { /* DFA node {65,45,41,1} 56 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_8; + /* capture stores */ + matched_1 = i - 1; + matched_id = 4; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st57: { /* DFA node {65} 57 */ + if (unlikely(i >= len)) { + i++; + goto st57_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + goto st58; + break; + } + case 92: { + goto st59; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } /* end state */ + + goto st57_error; + +st58: { /* DFA node {67} 58 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st59: { /* DFA node {62} 59 */ + if (unlikely(i >= len)) { + i++; + goto st59_error; + } + + c = s[i]; + i++; + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st112; + } + } /* end state */ + + goto st59_error; + +st60: { /* DFA node {78,65,1} 60 */ + if (unlikely(i >= len)) { + i++; + goto st60_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + goto st65; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st60; + } /* end state */ + + goto st60_error; + +st61: { /* DFA node {78,67,59,1} 61 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st62: { /* DFA node {80,65,72,1} 62 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st63: { /* DFA node {78,65,30,50,1} 63 */ + if (unlikely(i >= len)) { + i++; + goto st63_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 4 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 4 to row 5 */ + /* transfer caps from row 4 to row 6 */ + /* capture stores */ + goto st113; + break; + } + case 91: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 4 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 4 to row 2 */ + goto st60; + } /* end state */ + + goto st63_error; + +st64: { /* DFA node {78,65,21,1} 64 */ + if (unlikely(i >= len)) { + i++; + goto st64_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 61: { + goto st114; + break; + } + case 91: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st115; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st64_error; + +st65: { /* DFA node {75,62,1} 65 */ + if (unlikely(i >= len)) { + i++; + goto st65_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st117; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st118; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st119; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st120; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st121; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st122; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st123; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c == 92) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st116; + } /* end state */ + + goto st65_error; + +st66: { /* DFA node {78,65,41,1} 66 */ + if (unlikely(i >= len)) { + i++; + goto st66_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 61: { + goto st124; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st125; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st66_error; + +st67: { /* DFA node {78,65,11,1} 67 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st68: { /* DFA node {78,65,16,1} 68 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st70: { /* DFA node {78,31,51,30,50,1} 70 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 39) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st131; + } + if (c == 91) { + goto st132; + } + if (c == 92) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st133; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st130; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st71: { /* DFA node {78,23,1} 71 */ + if (unlikely(i >= len)) { + i++; + goto st71_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 61: { + goto st71; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st72; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st71_error; + +st72: { /* DFA node {78,25,21,1} 72 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_4; + /* capture stores */ + matched_1 = i - 1; + matched_id = 2; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st73: { /* DFA node {76,1} 73 */ + if (unlikely(i >= len)) { + i++; + goto st73_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 1 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 1 to row 2 */ + /* transfer caps from row 1 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 91: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + goto st23; + break; + } + case 93: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 1 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st18; + } /* end state */ + + goto st73_error; + +st74: { /* DFA node {76,59,1} 74 */ + if (unlikely(i >= len)) { + i++; + goto st74_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + goto st65; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st60; + } /* end state */ + + goto st74_error; + +st75: { /* DFA node {76,72,1} 75 */ + if (unlikely(i >= len)) { + i++; + goto st75_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st75_error; + +st76: { /* DFA node {76,30,50,1} 76 */ + if (unlikely(i >= len)) { + i++; + goto st76_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 3 to row 4 */ + /* transfer caps from row 3 to row 5 */ + /* capture stores */ + goto st70; + break; + } + case 91: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + /* transfer caps from row 3 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 3 to row 1 */ + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 1 */ + goto st18; + } /* end state */ + + goto st76_error; + +st77: { /* DFA node {76,21,1} 77 */ + if (unlikely(i >= len)) { + i++; + goto st77_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 61: { + goto st71; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st72; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_8 = i - 1; + goto st24; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st77_error; + +st78: { /* DFA node {76,41,1} 78 */ + if (unlikely(i >= len)) { + i++; + goto st78_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 61: { + goto st81; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st82; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st78_error; + +st79: { /* DFA node {76,11,1} 79 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st80: { /* DFA node {76,16,1} 80 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st81: { /* DFA node {78,43,1} 81 */ + if (unlikely(i >= len)) { + i++; + goto st81_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_12 = i - 1; + goto st19; + break; + } + case 39: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_14 = i - 1; + goto st20; + break; + } + case 45: { + /* transfer caps from row 2 to row 1 */ + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps1_6 = i - 1; + caps2_10 = i - 1; + goto st21; + break; + } + case 61: { + goto st81; + break; + } + case 91: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_4 = i - 1; + goto st22; + break; + } + case 92: { + /* transfer caps from row 2 to row 1 */ + goto st23; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st82; + break; + } + case 123: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_0 = i - 1; + goto st25; + break; + } + case 125: { + /* transfer caps from row 2 to row 1 */ + /* capture stores */ + caps1_2 = i - 1; + goto st26; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 2 to row 1 */ + goto st18; + } /* end state */ + + goto st81_error; + +st82: { /* DFA node {78,45,41,1} 82 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_8; + /* capture stores */ + matched_1 = i - 1; + matched_id = 4; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st83: { /* DFA node {78} 83 */ + if (unlikely(i >= len)) { + i++; + goto st83_error; + } + + c = s[i]; + i++; + switch (c) { + case 39: { + goto st84; + break; + } + case 92: { + goto st85; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } /* end state */ + + goto st83_error; + +st84: { /* DFA node {80} 84 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st85: { /* DFA node {75} 85 */ + if (unlikely(i >= len)) { + i++; + goto st85_error; + } + + c = s[i]; + i++; + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st134; + } + } /* end state */ + + goto st85_error; + +st87: { /* DFA node {53} 87 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st87; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st88: { /* DFA node {32,53} 88 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 61) { + goto st135; + } + if (c == 91) { + goto st136; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 92 && c <= 255)) + { + /* transfer caps from row 1 to row 0 */ + caps0_10 = caps1_10; + goto st87; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st91: { /* DFA node {65,78,31,51,30,50,1} 91 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 3 to matched */ + matched_0 = caps3_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st138; + } + if (c == 39) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st139; + } + if (c == 91) { + goto st140; + } + if (c == 92) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st141; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st137; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st92: { /* DFA node {65,78,23,1} 92 */ + if (unlikely(i >= len)) { + i++; + goto st92_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 61: { + goto st92; + break; + } + case 91: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st93; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st92_error; + +st93: { /* DFA node {65,78,25,21,1} 93 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_4; + /* capture stores */ + matched_1 = i - 1; + matched_id = 2; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st94: { /* DFA node {63,76,1} 94 */ + if (unlikely(i >= len)) { + i++; + goto st94_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + goto st40; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st35; + } /* end state */ + + goto st94_error; + +st95: { /* DFA node {63,76,59,1} 95 */ + if (unlikely(i >= len)) { + i++; + goto st95_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st95_error; + +st96: { /* DFA node {63,76,72,1} 96 */ + if (unlikely(i >= len)) { + i++; + goto st96_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st96_error; + +st97: { /* DFA node {63,76,30,50,1} 97 */ + if (unlikely(i >= len)) { + i++; + goto st97_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 4 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 4 to row 5 */ + /* transfer caps from row 4 to row 6 */ + /* capture stores */ + goto st91; + break; + } + case 91: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 4 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 4 to row 2 */ + goto st35; + } /* end state */ + + goto st97_error; + +st98: { /* DFA node {63,76,21,1} 98 */ + if (unlikely(i >= len)) { + i++; + goto st98_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 61: { + goto st92; + break; + } + case 91: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st93; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st41; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st98_error; + +st99: { /* DFA node {63,76,41,1} 99 */ + if (unlikely(i >= len)) { + i++; + goto st99_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 61: { + goto st102; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st103; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st99_error; + +st100: { /* DFA node {63,76,11,1} 100 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st101: { /* DFA node {63,76,16,1} 101 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st102: { /* DFA node {65,78,43,1} 102 */ + if (unlikely(i >= len)) { + i++; + goto st102_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st36; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st37; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st38; + break; + } + case 61: { + goto st102; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st39; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st40; + break; + } + case 93: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st103; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st42; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st43; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st35; + } /* end state */ + + goto st102_error; + +st103: { /* DFA node {65,78,45,41,1} 103 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_8; + /* capture stores */ + matched_1 = i - 1; + matched_id = 4; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st104: { /* DFA node {65,78} 104 */ + if (unlikely(i >= len)) { + i++; + goto st104_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + goto st105; + break; + } + case 39: { + goto st106; + break; + } + case 92: { + goto st107; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } /* end state */ + + goto st104_error; + +st105: { /* DFA node {67,78} 105 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st106: { /* DFA node {65,80} 106 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st107: { /* DFA node {62,75} 107 */ + if (unlikely(i >= len)) { + i++; + goto st107_error; + } + + c = s[i]; + i++; + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st142; + } + } /* end state */ + + goto st107_error; + +st108: { /* DFA node {65,53} 108 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + goto st109; + } + if (c == 92) { + goto st111; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st108; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st109: { /* DFA node {67,53} 109 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st110: { /* DFA node {65,32,53} 110 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st109; + } + if (c == 61) { + goto st143; + } + if (c == 91) { + goto st144; + } + if (c == 92) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st111; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st108; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st111: { /* DFA node {62,53} 111 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st145; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st112: { /* DFA node {63} 112 */ + if (unlikely(i >= len)) { + i++; + goto st112_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + goto st58; + break; + } + case 92: { + goto st59; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } /* end state */ + + goto st112_error; + +st113: { /* DFA node {78,65,31,51,30,50,1} 113 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 3 to matched */ + matched_0 = caps3_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st147; + } + if (c == 39) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st148; + } + if (c == 91) { + goto st149; + } + if (c == 92) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st150; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st146; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st114: { /* DFA node {78,65,23,1} 114 */ + if (unlikely(i >= len)) { + i++; + goto st114_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 61: { + goto st114; + break; + } + case 91: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st115; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st114_error; + +st115: { /* DFA node {78,65,25,21,1} 115 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_4; + /* capture stores */ + matched_1 = i - 1; + matched_id = 2; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st116: { /* DFA node {76,63,1} 116 */ + if (unlikely(i >= len)) { + i++; + goto st116_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 2 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 2 to row 3 */ + /* transfer caps from row 2 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 91: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + goto st65; + break; + } + case 93: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 2 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + goto st60; + } /* end state */ + + goto st116_error; + +st117: { /* DFA node {76,63,59,1} 117 */ + if (unlikely(i >= len)) { + i++; + goto st117_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st117_error; + +st118: { /* DFA node {76,63,72,1} 118 */ + if (unlikely(i >= len)) { + i++; + goto st118_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st118_error; + +st119: { /* DFA node {76,63,30,50,1} 119 */ + if (unlikely(i >= len)) { + i++; + goto st119_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 4 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 4 to row 5 */ + /* transfer caps from row 4 to row 6 */ + /* capture stores */ + goto st113; + break; + } + case 91: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 4 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 4 to row 2 */ + /* transfer caps from row 4 to row 3 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 4 to row 2 */ + goto st60; + } /* end state */ + + goto st119_error; + +st120: { /* DFA node {76,63,21,1} 120 */ + if (unlikely(i >= len)) { + i++; + goto st120_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 61: { + goto st114; + break; + } + case 91: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st115; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_8 = i - 1; + goto st66; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st120_error; + +st121: { /* DFA node {76,63,41,1} 121 */ + if (unlikely(i >= len)) { + i++; + goto st121_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 61: { + goto st124; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st125; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st121_error; + +st122: { /* DFA node {76,63,11,1} 122 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_0; + /* capture stores */ + matched_1 = i - 1; + matched_id = 0; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st123: { /* DFA node {76,63,16,1} 123 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_2; + /* capture stores */ + matched_1 = i - 1; + matched_id = 1; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st124: { /* DFA node {78,65,43,1} 124 */ + if (unlikely(i >= len)) { + i++; + goto st124_error; + } + + c = s[i]; + i++; + switch (c) { + case 10: { + /* transfer caps from row 3 to row 0 */ + goto st1; + break; + } + case 34: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st61; + break; + } + case 39: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + goto st62; + break; + } + case 45: { + /* transfer caps from row 3 to row 2 */ + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + caps2_6 = i - 1; + caps3_10 = i - 1; + goto st63; + break; + } + case 61: { + goto st124; + break; + } + case 91: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_4 = i - 1; + goto st64; + break; + } + case 92: { + /* transfer caps from row 3 to row 2 */ + goto st65; + break; + } + case 93: { + /* transfer caps from row 3 to row 4 */ + /* capture stores */ + goto st125; + break; + } + case 123: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_0 = i - 1; + goto st67; + break; + } + case 125: { + /* transfer caps from row 3 to row 2 */ + /* capture stores */ + caps2_2 = i - 1; + goto st68; + break; + } + default: + break; + } + /* (c >= 0 && c <= 9) + * || (c >= 11 && c <= 33) + * || (c >= 35 && c <= 38) + * || (c >= 40 && c <= 44) + * || (c >= 46 && c <= 60) + * || (c >= 62 && c <= 90) + * || (c >= 94 && c <= 122) + * || (c == 124) + * || (c >= 126 && c <= 255) + */ + /* transfer caps from row 3 to row 2 */ + goto st60; + } /* end state */ + + goto st124_error; + +st125: { /* DFA node {78,65,45,41,1} 125 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_8; + /* capture stores */ + matched_1 = i - 1; + matched_id = 4; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st126: { /* DFA node {78,65} 126 */ + if (unlikely(i >= len)) { + i++; + goto st126_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + goto st127; + break; + } + case 39: { + goto st128; + break; + } + case 92: { + goto st129; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } /* end state */ + + goto st126_error; + +st127: { /* DFA node {78,67} 127 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st128: { /* DFA node {80,65} 128 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st129: { /* DFA node {75,62} 129 */ + if (unlikely(i >= len)) { + i++; + goto st129_error; + } + + c = s[i]; + i++; + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st151; + } + } /* end state */ + + goto st129_error; + +st130: { /* DFA node {78,53} 130 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 39) { + goto st131; + } + if (c == 92) { + goto st133; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st130; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st131: { /* DFA node {80,53} 131 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st132: { /* DFA node {78,32,53} 132 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 39) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st131; + } + if (c == 61) { + goto st152; + } + if (c == 91) { + goto st153; + } + if (c == 92) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st133; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st130; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st133: { /* DFA node {75,53} 133 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st154; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st134: { /* DFA node {76} 134 */ + if (unlikely(i >= len)) { + i++; + goto st134_error; + } + + c = s[i]; + i++; + switch (c) { + case 39: { + goto st84; + break; + } + case 92: { + goto st85; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } /* end state */ + + goto st134_error; + +st135: { /* DFA node {34,53} 135 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 61) { + goto st135; + } + if (c == 91) { + goto st136; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 92 && c <= 255)) + { + /* transfer caps from row 1 to row 0 */ + caps0_10 = caps1_10; + goto st87; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st136: { /* DFA node {36,53} 136 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_6; + /* capture stores */ + matched_1 = i - 1; + matched_id = 3; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st137: { /* DFA node {65,78,53} 137 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + goto st138; + } + if (c == 39) { + goto st139; + } + if (c == 92) { + goto st141; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st137; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st138: { /* DFA node {67,78,53} 138 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st139: { /* DFA node {65,80,53} 139 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st140: { /* DFA node {65,78,32,53} 140 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 3 to matched */ + matched_0 = caps3_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st138; + } + if (c == 39) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st139; + } + if (c == 61) { + goto st156; + } + if (c == 91) { + goto st157; + } + if (c == 92) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st141; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st137; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st141: { /* DFA node {62,75,53} 141 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st158; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st142: { /* DFA node {63,76} 142 */ + if (unlikely(i >= len)) { + i++; + goto st142_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + goto st105; + break; + } + case 39: { + goto st106; + break; + } + case 92: { + goto st107; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } /* end state */ + + goto st142_error; + +st143: { /* DFA node {65,34,53} 143 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st109; + } + if (c == 61) { + goto st143; + } + if (c == 91) { + goto st144; + } + if (c == 92) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st111; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st108; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st144: { /* DFA node {65,36,53} 144 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_6; + /* capture stores */ + matched_1 = i - 1; + matched_id = 3; + if (c != -1) { + if (c == 34) { + goto st58; + } + if (c == 92) { + goto st59; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st57; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st145: { /* DFA node {63,53} 145 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + goto st109; + } + if (c == 92) { + goto st111; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st108; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st146: { /* DFA node {78,65,53} 146 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + goto st147; + } + if (c == 39) { + goto st148; + } + if (c == 92) { + goto st150; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st146; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st147: { /* DFA node {78,67,53} 147 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_12; + /* capture stores */ + matched_1 = i - 1; + matched_id = 6; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st148: { /* DFA node {80,65,53} 148 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 0 to matched */ + matched_0 = caps0_14; + /* capture stores */ + matched_1 = i - 1; + matched_id = 7; + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st149: { /* DFA node {78,65,32,53} 149 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 3 to matched */ + matched_0 = caps3_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st147; + } + if (c == 39) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st148; + } + if (c == 61) { + goto st159; + } + if (c == 91) { + goto st160; + } + if (c == 92) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st150; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st146; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st150: { /* DFA node {75,62,53} 150 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) { + goto st161; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st151: { /* DFA node {76,63} 151 */ + if (unlikely(i >= len)) { + i++; + goto st151_error; + } + + c = s[i]; + i++; + switch (c) { + case 34: { + goto st127; + break; + } + case 39: { + goto st128; + break; + } + case 92: { + goto st129; + break; + } + default: + break; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } /* end state */ + + goto st151_error; + +st152: { /* DFA node {78,34,53} 152 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 39) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st131; + } + if (c == 61) { + goto st152; + } + if (c == 91) { + goto st153; + } + if (c == 92) { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st133; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 2 to row 1 */ + caps1_10 = caps2_10; + goto st130; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st153: { /* DFA node {78,36,53} 153 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_6; + /* capture stores */ + matched_1 = i - 1; + matched_id = 3; + if (c != -1) { + if (c == 39) { + goto st84; + } + if (c == 92) { + goto st85; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st83; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st154: { /* DFA node {76,53} 154 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 1 to matched */ + matched_0 = caps1_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 39) { + goto st131; + } + if (c == 92) { + goto st133; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st130; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st156: { /* DFA node {65,78,34,53} 156 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 3 to matched */ + matched_0 = caps3_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st138; + } + if (c == 39) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st139; + } + if (c == 61) { + goto st156; + } + if (c == 91) { + goto st157; + } + if (c == 92) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st141; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st137; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st157: { /* DFA node {65,78,36,53} 157 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_6; + /* capture stores */ + matched_1 = i - 1; + matched_id = 3; + if (c != -1) { + if (c == 34) { + goto st105; + } + if (c == 39) { + goto st106; + } + if (c == 92) { + goto st107; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st104; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st158: { /* DFA node {63,76,53} 158 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + goto st138; + } + if (c == 39) { + goto st139; + } + if (c == 92) { + goto st141; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st137; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st159: { /* DFA node {78,65,34,53} 159 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 3 to matched */ + matched_0 = caps3_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st147; + } + if (c == 39) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st148; + } + if (c == 61) { + goto st159; + } + if (c == 91) { + goto st160; + } + if (c == 92) { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st150; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 60) + || (c >= 62 && c <= 90) + || (c >= 93 && c <= 255)) + { + /* transfer caps from row 3 to row 2 */ + caps2_10 = caps3_10; + goto st146; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st160: { /* DFA node {78,65,36,53} 160 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_6; + /* capture stores */ + matched_1 = i - 1; + matched_id = 3; + if (c != -1) { + if (c == 34) { + goto st127; + } + if (c == 39) { + goto st128; + } + if (c == 92) { + goto st129; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st126; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st161: { /* DFA node {76,63,53} 161 */ + c = i < len ? s[i] : -1; + i++; + /* transfer caps from row 2 to matched */ + matched_0 = caps2_10; + /* capture stores */ + matched_1 = i - 1; + matched_id = 5; + if (c != -1) { + if (c == 34) { + goto st147; + } + if (c == 39) { + goto st148; + } + if (c == 92) { + goto st150; + } + if ((c >= 0 && c <= 9) + || (c >= 11 && c <= 33) + || (c >= 35 && c <= 38) + || (c >= 40 && c <= 91) + || (c >= 93 && c <= 255)) + { + goto st146; + } + } + } /* end state */ + + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + +st0_error: +st1_error: +st2_error: +st3_error: +st4_error: +st5_error: +st6_error: +st9_error: +st11_error: +st12_error: +st13_error: +st14_error: +st15_error: +st18_error: +st19_error: +st21_error: +st22_error: +st23_error: +st24_error: +st28_error: +st30_error: +st35_error: +st38_error: +st39_error: +st40_error: +st41_error: +st45_error: +st47_error: +st48_error: +st49_error: +st50_error: +st51_error: +st52_error: +st55_error: +st57_error: +st59_error: +st60_error: +st63_error: +st64_error: +st65_error: +st66_error: +st71_error: +st73_error: +st74_error: +st75_error: +st76_error: +st77_error: +st78_error: +st81_error: +st83_error: +st85_error: +st92_error: +st94_error: +st95_error: +st96_error: +st97_error: +st98_error: +st99_error: +st102_error: +st104_error: +st107_error: +st112_error: +st114_error: +st116_error: +st117_error: +st118_error: +st119_error: +st120_error: +st121_error: +st124_error: +st126_error: +st129_error: +st134_error: +st142_error: +st151_error: + + if (matched_0 != -1) { + ovec[0] = matched_0; + ovec[1] = matched_1; + return matched_id; /* fallback */ + } + return NO_MATCH; +} diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_lex.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_lex.h new file mode 100644 index 000000000..4155da767 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_lex.h @@ -0,0 +1,25 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_lex.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_LEX_H_INCLUDED_ +#define _NGX_STREAM_LUA_LEX_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +int ngx_stream_lua_lex(const u_char *const s, size_t len, int *const ovec); + + +#endif diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log.c new file mode 100644 index 000000000..c6e9c4cf5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log.c @@ -0,0 +1,463 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_log.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_log.h" +#include "ngx_stream_lua_util.h" + +#include "ngx_stream_lua_log_ringbuf.h" + + +static int ngx_stream_lua_print(lua_State *L); +static int ngx_stream_lua_ngx_log(lua_State *L); +static int log_wrapper(ngx_log_t *log, const char *ident, + ngx_uint_t level, lua_State *L); +static void ngx_stream_lua_inject_log_consts(lua_State *L); + + +/** + * Wrapper of nginx log functionality. Take a log level param and varargs of + * log message params. + * + * @param L Lua state pointer + * @retval always 0 (don't return values to Lua) + * */ +int +ngx_stream_lua_ngx_log(lua_State *L) +{ + ngx_log_t *log; + ngx_stream_lua_request_t *r; + const char *msg; + int level; + + r = ngx_stream_lua_get_req(L); + + if (r && r->connection && r->connection->log) { + log = r->connection->log; + + } else { + log = ngx_cycle->log; + } + + level = luaL_checkint(L, 1); + if (level < NGX_LOG_STDERR || level > NGX_LOG_DEBUG) { + msg = lua_pushfstring(L, "bad log level: %d", level); + return luaL_argerror(L, 1, msg); + } + + /* remove log-level param from stack */ + lua_remove(L, 1); + + return log_wrapper(log, "stream [lua] ", (ngx_uint_t) level, L); +} + + +/** + * Override Lua print function, output message to nginx error logs. Equal to + * ngx.log(ngx.NOTICE, ...). + * + * @param L Lua state pointer + * @retval always 0 (don't return values to Lua) + * */ +int +ngx_stream_lua_print(lua_State *L) +{ + ngx_log_t *log; + ngx_stream_lua_request_t *r; + + r = ngx_stream_lua_get_req(L); + + if (r && r->connection && r->connection->log) { + log = r->connection->log; + + } else { + log = ngx_cycle->log; + } + + return log_wrapper(log, "stream [lua] ", NGX_LOG_NOTICE, L); +} + + +static int +log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, + lua_State *L) +{ + u_char *buf; + u_char *p, *q; + ngx_str_t name; + int nargs, i; + size_t size, len; + size_t src_len = 0; + int type; + const char *msg; + lua_Debug ar; + + if (level > log->log_level) { + return 0; + } + +#if 1 + /* add debug info */ + + lua_getstack(L, 1, &ar); + lua_getinfo(L, "Snl", &ar); + + /* get the basename of the Lua source file path, stored in q */ + name.data = (u_char *) ar.short_src; + if (name.data == NULL) { + name.len = 0; + + } else { + p = name.data; + while (*p != '\0') { + if (*p == '/' || *p == '\\') { + name.data = p + 1; + } + p++; + } + + name.len = p - name.data; + } + +#endif + + nargs = lua_gettop(L); + + size = name.len + NGX_INT_T_LEN + sizeof(":: ") - 1; + + if (*ar.namewhat != '\0' && *ar.what == 'L') { + src_len = ngx_strlen(ar.name); + size += src_len + sizeof("(): ") - 1; + } + + for (i = 1; i <= nargs; i++) { + type = lua_type(L, i); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_tolstring(L, i, &len); + size += len; + break; + + case LUA_TNIL: + size += sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, i)) { + size += sizeof("true") - 1; + + } else { + size += sizeof("false") - 1; + } + + break; + + case LUA_TTABLE: + if (!luaL_callmeta(L, i, "__tostring")) { + return luaL_argerror(L, i, "expected table to have " + "__tostring metamethod"); + } + + lua_tolstring(L, -1, &len); + size += len; + break; + + case LUA_TLIGHTUSERDATA: + if (lua_touserdata(L, i) == NULL) { + size += sizeof("null") - 1; + break; + } + + continue; + + default: + msg = lua_pushfstring(L, "string, number, boolean, or nil " + "expected, got %s", + lua_typename(L, type)); + return luaL_argerror(L, i, msg); + } + } + + buf = lua_newuserdata(L, size); + + p = ngx_copy(buf, name.data, name.len); + + *p++ = ':'; + + p = ngx_snprintf(p, NGX_INT_T_LEN, "%d", + ar.currentline > 0 ? ar.currentline : ar.linedefined); + + *p++ = ':'; *p++ = ' '; + + if (*ar.namewhat != '\0' && *ar.what == 'L') { + p = ngx_copy(p, ar.name, src_len); + *p++ = '('; + *p++ = ')'; + *p++ = ':'; + *p++ = ' '; + } + + for (i = 1; i <= nargs; i++) { + type = lua_type(L, i); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + q = (u_char *) lua_tolstring(L, i, &len); + p = ngx_copy(p, q, len); + break; + + case LUA_TNIL: + *p++ = 'n'; + *p++ = 'i'; + *p++ = 'l'; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, i)) { + *p++ = 't'; + *p++ = 'r'; + *p++ = 'u'; + *p++ = 'e'; + + } else { + *p++ = 'f'; + *p++ = 'a'; + *p++ = 'l'; + *p++ = 's'; + *p++ = 'e'; + } + + break; + + case LUA_TTABLE: + luaL_callmeta(L, i, "__tostring"); + q = (u_char *) lua_tolstring(L, -1, &len); + p = ngx_copy(p, q, len); + break; + + case LUA_TLIGHTUSERDATA: + *p++ = 'n'; + *p++ = 'u'; + *p++ = 'l'; + *p++ = 'l'; + + break; + + default: + return luaL_error(L, "impossible to reach here"); + } + } + + if (p - buf > (off_t) size) { + return luaL_error(L, "buffer error: %d > %d", (int) (p - buf), + (int) size); + } + + ngx_log_error(level, log, 0, "%s%*s", ident, (size_t) (p - buf), buf); + + return 0; +} + + +void +ngx_stream_lua_inject_log_api(lua_State *L) +{ + ngx_stream_lua_inject_log_consts(L); + + lua_pushcfunction(L, ngx_stream_lua_ngx_log); + lua_setfield(L, -2, "log"); + + lua_pushcfunction(L, ngx_stream_lua_print); + lua_setglobal(L, "print"); +} + + +static void +ngx_stream_lua_inject_log_consts(lua_State *L) +{ + /* {{{ nginx log level constants */ + lua_pushinteger(L, NGX_LOG_STDERR); + lua_setfield(L, -2, "STDERR"); + + lua_pushinteger(L, NGX_LOG_EMERG); + lua_setfield(L, -2, "EMERG"); + + lua_pushinteger(L, NGX_LOG_ALERT); + lua_setfield(L, -2, "ALERT"); + + lua_pushinteger(L, NGX_LOG_CRIT); + lua_setfield(L, -2, "CRIT"); + + lua_pushinteger(L, NGX_LOG_ERR); + lua_setfield(L, -2, "ERR"); + + lua_pushinteger(L, NGX_LOG_WARN); + lua_setfield(L, -2, "WARN"); + + lua_pushinteger(L, NGX_LOG_NOTICE); + lua_setfield(L, -2, "NOTICE"); + + lua_pushinteger(L, NGX_LOG_INFO); + lua_setfield(L, -2, "INFO"); + + lua_pushinteger(L, NGX_LOG_DEBUG); + lua_setfield(L, -2, "DEBUG"); + /* }}} */ +} + + +#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH +ngx_int_t +ngx_stream_lua_capture_log_handler(ngx_log_t *log, + ngx_uint_t level, u_char *buf, size_t n) +{ + ngx_stream_lua_log_ringbuf_t *ringbuf; + + dd("enter"); + + ringbuf = (ngx_stream_lua_log_ringbuf_t *) + ngx_cycle->intercept_error_log_data; + + if (level > ringbuf->filter_level) { + return NGX_OK; + } + + ngx_stream_lua_log_ringbuf_write(ringbuf, level, buf, n); + + dd("capture log: %s\n", buf); + + return NGX_OK; +} +#endif + + +int +ngx_stream_lua_ffi_errlog_set_filter_level(int level, u_char *err, + size_t *errlen) +{ +#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH + ngx_stream_lua_log_ringbuf_t *ringbuf; + + ringbuf = ngx_cycle->intercept_error_log_data; + + if (ringbuf == NULL) { + *errlen = ngx_snprintf(err, *errlen, + "directive \"lua_capture_error_log\" is not set") + - err; + return NGX_ERROR; + } + + if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) { + *errlen = ngx_snprintf(err, *errlen, "bad log level: %d", level) + - err; + return NGX_ERROR; + } + + ringbuf->filter_level = level; + return NGX_OK; +#else + *errlen = ngx_snprintf(err, *errlen, + "missing the capture error log patch for nginx") + - err; + return NGX_ERROR; +#endif +} + + +int +ngx_stream_lua_ffi_errlog_get_msg(char **log, int *loglevel, u_char *err, + size_t *errlen, double *log_time) +{ +#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH + ngx_uint_t loglen; + + ngx_stream_lua_log_ringbuf_t *ringbuf; + + ringbuf = ngx_cycle->intercept_error_log_data; + + if (ringbuf == NULL) { + *errlen = ngx_snprintf(err, *errlen, + "directive \"lua_capture_error_log\" is not set") + - err; + return NGX_ERROR; + } + + if (ringbuf->count == 0) { + return NGX_DONE; + } + + ngx_stream_lua_log_ringbuf_read(ringbuf, loglevel, (void **) log, &loglen, + log_time); + return loglen; +#else + *errlen = ngx_snprintf(err, *errlen, + "missing the capture error log patch for nginx") + - err; + return NGX_ERROR; +#endif +} + + +int +ngx_stream_lua_ffi_errlog_get_sys_filter_level(ngx_stream_lua_request_t *r) +{ + ngx_log_t *log; + int log_level; + + if (r && r->connection && r->connection->log) { + log = r->connection->log; + + } else { + log = ngx_cycle->log; + } + + log_level = log->log_level; + if (log_level == NGX_LOG_DEBUG_ALL) { + log_level = NGX_LOG_DEBUG; + } + + return log_level; +} + + +int +ngx_stream_lua_ffi_raw_log(ngx_stream_lua_request_t *r, int level, u_char *s, + size_t s_len) +{ + ngx_log_t *log; + + if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) { + return NGX_ERROR; + } + + if (r && r->connection && r->connection->log) { + log = r->connection->log; + + } else { + log = ngx_cycle->log; + } + + ngx_log_error((unsigned) level, log, 0, "%*s", s_len, s); + + return NGX_OK; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log.h new file mode 100644 index 000000000..9ad0234ba --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log.h @@ -0,0 +1,33 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_log.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_LOG_H_INCLUDED_ +#define _NGX_STREAM_LUA_LOG_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_log_api(lua_State *L); + +#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH +ngx_int_t ngx_stream_lua_capture_log_handler(ngx_log_t *log, + ngx_uint_t level, u_char *buf, size_t n); +#endif + + +#endif /* _NGX_STREAM_LUA_LOG_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log_ringbuf.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log_ringbuf.c new file mode 100644 index 000000000..bcdc63893 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log_ringbuf.c @@ -0,0 +1,236 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_log_ringbuf.c.tt2 + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_common.h" +#include "ngx_stream_lua_log_ringbuf.h" + + +typedef struct { + double time; + unsigned len; + unsigned log_level; +} ngx_stream_lua_log_ringbuf_header_t; + + +enum { + HEADER_LEN = sizeof(ngx_stream_lua_log_ringbuf_header_t) +}; + + +static void *ngx_stream_lua_log_ringbuf_next_header( + ngx_stream_lua_log_ringbuf_t *rb); +static void ngx_stream_lua_log_ringbuf_append( + ngx_stream_lua_log_ringbuf_t *rb, int log_level, void *buf, int n); +static size_t ngx_stream_lua_log_ringbuf_free_spaces( + ngx_stream_lua_log_ringbuf_t *rb); + + +void +ngx_stream_lua_log_ringbuf_init(ngx_stream_lua_log_ringbuf_t *rb, void *buf, + size_t len) +{ + rb->data = buf; + rb->size = len; + + rb->tail = rb->data; + rb->head = rb->data; + rb->sentinel = rb->data + rb->size; + rb->count = 0; + rb->filter_level = NGX_LOG_DEBUG; + + return; +} + + +void +ngx_stream_lua_log_ringbuf_reset(ngx_stream_lua_log_ringbuf_t *rb) +{ + rb->tail = rb->data; + rb->head = rb->data; + rb->sentinel = rb->data + rb->size; + rb->count = 0; + + return; +} + + +/* + * get the next data header, it'll skip the useless data space or + * placehold data + */ +static void * +ngx_stream_lua_log_ringbuf_next_header(ngx_stream_lua_log_ringbuf_t *rb) +{ + /* useless data */ + if (rb->size - (rb->head - rb->data) < HEADER_LEN) + { + return rb->data; + } + + /* placehold data */ + if (rb->head >= rb->sentinel) { + return rb->data; + } + + return rb->head; +} + + +/* append data to ring buffer directly */ +static void +ngx_stream_lua_log_ringbuf_append(ngx_stream_lua_log_ringbuf_t *rb, + int log_level, void *buf, int n) +{ + ngx_stream_lua_log_ringbuf_header_t *head; + ngx_time_t *tp; + + head = (ngx_stream_lua_log_ringbuf_header_t *) rb->tail; + head->len = n; + head->log_level = log_level; + + tp = ngx_timeofday(); + head->time = tp->sec + tp->msec / 1000.0L; + + rb->tail += HEADER_LEN; + ngx_memcpy(rb->tail, buf, n); + rb->tail += n; + rb->count++; + + if (rb->tail > rb->sentinel) { + rb->sentinel = rb->tail; + } + + return; +} + + +/* throw away data at head */ +static void +ngx_stream_lua_log_ringbuf_throw_away(ngx_stream_lua_log_ringbuf_t *rb) +{ + ngx_stream_lua_log_ringbuf_header_t *head; + + if (rb->count == 0) { + return; + } + + head = (ngx_stream_lua_log_ringbuf_header_t *) rb->head; + + rb->head += HEADER_LEN + head->len; + rb->count--; + + if (rb->count == 0) { + ngx_stream_lua_log_ringbuf_reset(rb); + } + + rb->head = ngx_stream_lua_log_ringbuf_next_header(rb); + + return; +} + + +/* size of free spaces */ +static size_t +ngx_stream_lua_log_ringbuf_free_spaces(ngx_stream_lua_log_ringbuf_t *rb) +{ + if (rb->count == 0) { + return rb->size; + } + + if (rb->tail > rb->head) { + return rb->data + rb->size - rb->tail; + } + + return rb->head - rb->tail; +} + + +/* + * try to write log data to ring buffer, throw away old data + * if there was not enough free spaces. + */ +ngx_int_t +ngx_stream_lua_log_ringbuf_write(ngx_stream_lua_log_ringbuf_t *rb, + int log_level, void *buf, size_t n) +{ + if (n + HEADER_LEN > rb->size) { + return NGX_ERROR; + } + + if (ngx_stream_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) { + /* if the right space is not enough, mark it as placehold data */ + if ((size_t)(rb->data + rb->size - rb->tail) < n + HEADER_LEN) { + + while (rb->head >= rb->tail && rb->count) { + /* head is after tail, so we will throw away all data between + * head and sentinel */ + ngx_stream_lua_log_ringbuf_throw_away(rb); + } + + rb->sentinel = rb->tail; + rb->tail = rb->data; + } + + while (ngx_stream_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) { + ngx_stream_lua_log_ringbuf_throw_away(rb); + } + } + + ngx_stream_lua_log_ringbuf_append(rb, log_level, buf, n); + + return NGX_OK; +} + + +/* read log from ring buffer, do reset if all of the logs were readed. */ +ngx_int_t +ngx_stream_lua_log_ringbuf_read(ngx_stream_lua_log_ringbuf_t *rb, + int *log_level, void **buf, size_t *n, double *log_time) +{ + ngx_stream_lua_log_ringbuf_header_t *head; + + if (rb->count == 0) { + return NGX_ERROR; + } + + head = (ngx_stream_lua_log_ringbuf_header_t *) rb->head; + + if (rb->head >= rb->sentinel) { + return NGX_ERROR; + } + + *log_level = head->log_level; + *n = head->len; + rb->head += HEADER_LEN; + *buf = rb->head; + rb->head += head->len; + + if (log_time) { + *log_time = head->time; + } + + rb->count--; + + if (rb->count == 0) { + ngx_stream_lua_log_ringbuf_reset(rb); + } + + rb->head = ngx_stream_lua_log_ringbuf_next_header(rb); + + return NGX_OK; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log_ringbuf.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log_ringbuf.h new file mode 100644 index 000000000..32b33c835 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_log_ringbuf.h @@ -0,0 +1,39 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_log_ringbuf.h.tt2 + */ + + +#ifndef _NGX_STREAM_LUA_RINGBUF_H_INCLUDED_ +#define _NGX_STREAM_LUA_RINGBUF_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +typedef struct { + ngx_uint_t filter_level; + char *tail; /* writed point */ + char *head; /* readed point */ + char *data; /* buffer */ + char *sentinel; + size_t size; /* buffer total size */ + size_t count; /* count of logs */ +} ngx_stream_lua_log_ringbuf_t; + + +void ngx_stream_lua_log_ringbuf_init(ngx_stream_lua_log_ringbuf_t *rb, + void *buf, size_t len); +void ngx_stream_lua_log_ringbuf_reset(ngx_stream_lua_log_ringbuf_t *rb); +ngx_int_t ngx_stream_lua_log_ringbuf_read(ngx_stream_lua_log_ringbuf_t *rb, + int *log_level, void **buf, size_t *n, double *log_time); +ngx_int_t ngx_stream_lua_log_ringbuf_write(ngx_stream_lua_log_ringbuf_t *rb, + int log_level, void *buf, size_t n); + + +#endif /* _NGX_STREAM_LUA_RINGBUF_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_logby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_logby.c new file mode 100644 index 000000000..8d8379229 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_logby.c @@ -0,0 +1,275 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_logby.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_logby.h" +#include "ngx_stream_lua_exception.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_pcrefix.h" +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_misc.h" +#include "ngx_stream_lua_consts.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_exception.h" +#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM) +#include +#endif + + +static ngx_int_t ngx_stream_lua_log_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); + + +static void +ngx_stream_lua_log_by_lua_env(lua_State *L, ngx_stream_lua_request_t *r) +{ + /* set nginx request pointer to current lua thread's globals table */ + ngx_stream_lua_set_req(L, r); + +#ifndef OPENRESTY_LUAJIT + /** + * we want to create empty environment for current script + * + * newt = {} + * newt["_G"] = newt + * setmetatable(newt, {__index = _G}) + * + * if a function or symbol is not defined in our env, __index will lookup + * in the global env. + * + * all variables created in the script-env will be thrown away at the end + * of the script run. + * */ + ngx_stream_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */); + + /* {{{ make new env inheriting main thread's globals table */ + lua_createtable(L, 0, 1); /* the metatable for the new env */ + ngx_stream_lua_get_globals_table(L); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */ + /* }}} */ + + lua_setfenv(L, -2); /* set new running env for the code closure */ +#endif /* OPENRESTY_LUAJIT */ +} + + +ngx_int_t +ngx_stream_lua_log_handler(ngx_stream_session_t *r) +{ +#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM) + ngx_uint_t trim_cycle, trim_nreq; + ngx_stream_lua_main_conf_t *lmcf; +#if (NGX_DEBUG) + ngx_int_t trim_ret; +#endif +#endif + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_ctx_t *ctx; + +#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM) + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + trim_cycle = lmcf->malloc_trim_cycle; + + if (trim_cycle > 0) { + + dd("cycle: %d", (int) trim_cycle); + + trim_nreq = ++lmcf->malloc_trim_req_count; + + if (trim_nreq >= trim_cycle) { + lmcf->malloc_trim_req_count = 0; + +#if (NGX_DEBUG) + trim_ret = malloc_trim(1); + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "malloc_trim(1) returned %d", trim_ret); +#else + (void) malloc_trim(1); +#endif + } + } +# if (NGX_DEBUG) + else { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "malloc_trim() disabled"); + } +# endif +#endif + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua log handler"); + + llcf = ngx_stream_get_module_srv_conf(r, ngx_stream_lua_module); + + if (llcf->log_handler == NULL) { + dd("no log handler found"); + return NGX_DECLINED; + } + + ctx = ngx_stream_get_module_ctx(r, ngx_stream_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + } + + ctx->context = NGX_STREAM_LUA_CONTEXT_LOG; + + dd("calling log handler"); + return llcf->log_handler(ctx->request); +} + + +ngx_int_t +ngx_stream_lua_log_handler_inline(ngx_stream_lua_request_t *r) +{ + lua_State *L; + ngx_int_t rc; + ngx_stream_lua_loc_conf_t *llcf; + + dd("log by lua inline"); + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua inline script (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + llcf->log_src.value.data, + llcf->log_src.value.len, + llcf->log_src_key, + (const char *) llcf->log_chunkname); + if (rc != NGX_OK) { + return NGX_ERROR; + } + + return ngx_stream_lua_log_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_log_handler_file(ngx_stream_lua_request_t *r) +{ + lua_State *L; + ngx_int_t rc; + u_char *script_path; + ngx_stream_lua_loc_conf_t *llcf; + ngx_str_t eval_src; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (ngx_stream_complex_value(r->session, &llcf->log_src, &eval_src) + != NGX_OK) + { + return NGX_ERROR; + } + + script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path, + llcf->log_src_key); + if (rc != NGX_OK) { + return NGX_ERROR; + } + + return ngx_stream_lua_log_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_log_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) +{ + ngx_int_t rc; + u_char *err_msg; + size_t len; +#if (NGX_PCRE) + ngx_pool_t *old_pool; +#endif + + /* set Lua VM panic handler */ + lua_atpanic(L, ngx_stream_lua_atpanic); + + NGX_LUA_EXCEPTION_TRY { + + /* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */ + ngx_stream_lua_log_by_lua_env(L, r); + +#if (NGX_PCRE) + /* XXX: work-around to nginx regex subsystem */ + old_pool = ngx_stream_lua_pcre_malloc_init(r->pool); +#endif + + lua_pushcfunction(L, ngx_stream_lua_traceback); + lua_insert(L, 1); /* put it under chunk and args */ + + /* protected call user code */ + rc = lua_pcall(L, 0, 1, 1); + + lua_remove(L, 1); /* remove traceback function */ + +#if (NGX_PCRE) + /* XXX: work-around to nginx regex subsystem */ + ngx_stream_lua_pcre_malloc_done(old_pool); +#endif + + if (rc != 0) { + /* error occurred when running loaded code */ + err_msg = (u_char *) lua_tolstring(L, -1, &len); + + if (err_msg == NULL) { + err_msg = (u_char *) "unknown reason"; + len = sizeof("unknown reason") - 1; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to run log_by_lua*: %*s", len, err_msg); + + lua_settop(L, 0); /* clear remaining elems on stack */ + + return NGX_ERROR; + } + + } NGX_LUA_EXCEPTION_CATCH { + + dd("nginx execution restored"); + return NGX_ERROR; + } + + /* clear Lua stack */ + lua_settop(L, 0); + + return NGX_OK; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_logby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_logby.h new file mode 100644 index 000000000..713e7e398 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_logby.h @@ -0,0 +1,30 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_logby.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_LOGBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_LOGBY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_log_handler(ngx_stream_session_t *r); + +ngx_int_t ngx_stream_lua_log_handler_inline(ngx_stream_lua_request_t *r); +ngx_int_t ngx_stream_lua_log_handler_file(ngx_stream_lua_request_t *r); + + +#endif /* _NGX_STREAM_LUA_LOGBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_misc.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_misc.c new file mode 100644 index 000000000..1e4bd8ae4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_misc.c @@ -0,0 +1,65 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_misc.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_misc.h" +#include "ngx_stream_lua_util.h" + + + + +int +ngx_stream_lua_ffi_get_resp_status(ngx_stream_lua_request_t *r) +{ + return r->session->status; +} + + + + +int +ngx_stream_lua_ffi_get_conf_env(u_char *name, u_char **env_buf, + size_t *name_len) +{ + ngx_uint_t i; + ngx_str_t *var; + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + var = ccf->env.elts; + + for (i = 0; i < ccf->env.nelts; i++) { + if (var[i].data[var[i].len] == '=' + && ngx_strncmp(name, var[i].data, var[i].len) == 0) + { + *env_buf = var[i].data; + *name_len = var[i].len; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_misc.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_misc.h new file mode 100644 index 000000000..64f99e15e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_misc.h @@ -0,0 +1,27 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_misc.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_MISC_H_INCLUDED_ +#define _NGX_STREAM_LUA_MISC_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + + + +#endif /* _NGX_STREAM_LUA_MISC_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_module.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_module.c new file mode 100644 index 000000000..7f4c7c0e0 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_module.c @@ -0,0 +1,1090 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_module.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_initby.h" +#include "ngx_stream_lua_initworkerby.h" +#include "ngx_stream_lua_probe.h" +#include "ngx_stream_lua_balancer.h" +#include "ngx_stream_lua_logby.h" +#include "ngx_stream_lua_semaphore.h" +#include "ngx_stream_lua_ssl_client_helloby.h" +#include "ngx_stream_lua_ssl_certby.h" + + +#include "ngx_stream_lua_prereadby.h" + + +static void *ngx_stream_lua_create_main_conf(ngx_conf_t *cf); +static char *ngx_stream_lua_init_main_conf(ngx_conf_t *cf, void *conf); +static void *ngx_stream_lua_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + + + +static ngx_int_t ngx_stream_lua_init(ngx_conf_t *cf); +static char *ngx_stream_lua_lowat_check(ngx_conf_t *cf, void *post, void *data); +#if (NGX_STREAM_SSL) +static ngx_int_t ngx_stream_lua_set_ssl(ngx_conf_t *cf, + ngx_stream_lua_loc_conf_t *llcf); +#if (nginx_version >= 1019004) +static char *ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); +#endif +#endif +static char *ngx_stream_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_conf_post_t ngx_stream_lua_lowat_post = + { ngx_stream_lua_lowat_check }; +#if (NGX_STREAM_SSL) +#if (nginx_version >= 1019004) +static ngx_conf_post_t ngx_stream_lua_ssl_conf_command_post = + { ngx_stream_lua_ssl_conf_command_check }; +#endif +#endif + + + +#if (NGX_STREAM_SSL) + +static ngx_conf_bitmask_t ngx_stream_lua_ssl_protocols[] = { + { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, + { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, + { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, + { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, + { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, +#ifdef NGX_SSL_TLSv1_3 + { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, +#endif + { ngx_null_string, 0 } +}; + +#endif + + + + +static ngx_command_t ngx_stream_lua_cmds[] = { + + { ngx_string("lua_load_resty_core"), + NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG, + ngx_stream_lua_load_resty_core, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("lua_max_running_timers"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, max_running_timers), + NULL }, + + { ngx_string("lua_max_pending_timers"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, max_pending_timers), + NULL }, + + { ngx_string("lua_shared_dict"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2, + ngx_stream_lua_shared_dict, + 0, + 0, + NULL }, + + { ngx_string("lua_sa_restart"), + NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, set_sa_restart), + NULL }, + + { ngx_string("lua_capture_error_log"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_capture_error_log, + 0, + 0, + NULL }, + +#if (NGX_PCRE) + { ngx_string("lua_regex_cache_max_entries"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, regex_cache_max_entries), + NULL }, + + { ngx_string("lua_regex_match_limit"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, regex_match_limit), + NULL }, +#endif + + { ngx_string("lua_package_cpath"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_package_cpath, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("lua_package_path"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_package_path, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("lua_code_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_FLAG, + ngx_stream_lua_code_cache, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_loc_conf_t, enable_code_cache), + NULL }, + + + { ngx_string("lua_socket_log_errors"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_loc_conf_t, log_socket_errors), + NULL }, + + { ngx_string("init_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_init_by_lua_block, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_init_by_inline }, + + { ngx_string("init_by_lua"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_init_by_lua, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_init_by_inline }, + + { ngx_string("init_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_init_by_lua, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_init_by_file }, + + { ngx_string("init_worker_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_init_worker_by_lua_block, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_init_worker_by_inline }, + + { ngx_string("init_worker_by_lua"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_init_worker_by_lua, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_init_worker_by_inline }, + + { ngx_string("init_worker_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_init_worker_by_lua, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_init_worker_by_file }, + + /* preread_by_lua_file rel/or/abs/path/to/script */ + { ngx_string("preread_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_preread_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_preread_handler_file }, + + /* preread_by_lua_block { } */ + { ngx_string("preread_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_preread_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_preread_handler_inline }, + + + /* content_by_lua "" */ + { ngx_string("content_by_lua"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_content_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_content_handler_inline }, + + /* content_by_lua_block { } */ + { ngx_string("content_by_lua_block"), + NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_content_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_content_handler_inline }, + + /* content_by_lua_file rel/or/abs/path/to/script */ + { ngx_string("content_by_lua_file"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_content_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_content_handler_file }, + + + + /* log_by_lua_block { } */ + { ngx_string("log_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_log_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_log_handler_inline }, + + { ngx_string("log_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_stream_lua_log_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_log_handler_file }, + + { ngx_string("preread_by_lua_no_postpone"), + NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, postponed_to_preread_phase_end), + NULL }, + + { ngx_string("balancer_by_lua_block"), + NGX_STREAM_UPS_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_balancer_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_balancer_handler_inline }, + + { ngx_string("balancer_by_lua_file"), + NGX_STREAM_UPS_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_balancer_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_balancer_handler_file }, + + + { ngx_string("lua_socket_keepalive_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, keepalive_timeout), + NULL }, + + { ngx_string("lua_socket_connect_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, connect_timeout), + NULL }, + + { ngx_string("lua_socket_send_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, send_timeout), + NULL }, + + { ngx_string("lua_socket_send_lowat"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, send_lowat), + &ngx_stream_lua_lowat_post }, + + { ngx_string("lua_socket_buffer_size"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, buffer_size), + NULL }, + + { ngx_string("lua_socket_pool_size"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, pool_size), + NULL }, + + { ngx_string("lua_socket_read_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, read_timeout), + NULL }, + + + { ngx_string("lua_check_client_abort"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF + |NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, check_client_abort), + NULL }, + + + +#if (NGX_STREAM_SSL) + + { ngx_string("lua_ssl_protocols"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, ssl_protocols), + &ngx_stream_lua_ssl_protocols }, + + { ngx_string("lua_ssl_ciphers"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, ssl_ciphers), + NULL }, + + { ngx_string("ssl_client_hello_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_ssl_client_hello_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_ssl_client_hello_handler_inline }, + + { ngx_string("ssl_client_hello_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_ssl_client_hello_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_ssl_client_hello_handler_file }, + + { ngx_string("ssl_certificate_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_ssl_cert_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_ssl_cert_handler_inline }, + + { ngx_string("ssl_certificate_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_ssl_cert_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_ssl_cert_handler_file }, + + + { ngx_string("lua_ssl_verify_depth"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, ssl_verify_depth), + NULL }, + + { ngx_string("lua_ssl_trusted_certificate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, ssl_trusted_certificate), + NULL }, + + { ngx_string("lua_ssl_crl"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, ssl_crl), + NULL }, + +#if (nginx_version >= 1019004) + { ngx_string("lua_ssl_conf_command"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, ssl_conf_commands), + &ngx_stream_lua_ssl_conf_command_post }, +#endif +#endif /* NGX_STREAM_SSL */ + + { ngx_string("lua_malloc_trim"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_malloc_trim, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("lua_add_variable"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_add_variable, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +ngx_stream_module_t ngx_stream_lua_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_lua_init, /* postconfiguration */ + + ngx_stream_lua_create_main_conf, /* create main configuration */ + ngx_stream_lua_init_main_conf, /* init main configuration */ + + ngx_stream_lua_create_srv_conf, /* create server configuration */ + ngx_stream_lua_merge_srv_conf, /* merge server configuration */ + +}; + + +ngx_module_t ngx_stream_lua_module = { + NGX_MODULE_V1, + &ngx_stream_lua_module_ctx, /* module context */ + ngx_stream_lua_cmds, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_stream_lua_init_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_lua_init(ngx_conf_t *cf) +{ + ngx_int_t rc; + volatile ngx_cycle_t *saved_cycle; + ngx_array_t *arr; + ngx_pool_cleanup_t *cln; + + ngx_stream_handler_pt *h; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_core_main_conf_t *cmcf; + + + if (ngx_process == NGX_PROCESS_SIGNALLER || ngx_test_config) { + return NGX_OK; + } + + lmcf = ngx_stream_conf_get_module_main_conf(cf, + ngx_stream_lua_module); + + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + if (lmcf->requires_preread) { + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_lua_preread_handler; + } + + if (lmcf->postponed_to_preread_phase_end == NGX_CONF_UNSET) { + lmcf->postponed_to_preread_phase_end = 0; + } + + dd("requires log: %d", (int) lmcf->requires_log); + + if (lmcf->requires_log) { + arr = &cmcf->phases[NGX_STREAM_LOG_PHASE].handlers; + h = ngx_array_push(arr); + if (h == NULL) { + return NGX_ERROR; + } + + if (arr->nelts > 1) { + + /* + * if there are other log handlers, move them back and put ourself + * to the front of the list + */ + + h = arr->elts; + ngx_memmove(&h[1], h, + (arr->nelts - 1) * sizeof(ngx_stream_handler_pt)); + } + + *h = ngx_stream_lua_log_handler; + } + + + /* add the cleanup of semaphores after the lua_close */ + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->data = lmcf; + cln->handler = ngx_stream_lua_sema_mm_cleanup; + + + + if (lmcf->lua == NULL) { + dd("initializing lua vm"); + +#ifndef OPENRESTY_LUAJIT + if (ngx_process != NGX_PROCESS_SIGNALLER && !ngx_test_config) { + ngx_log_error(NGX_LOG_ALERT, cf->log, 0, + "detected a LuaJIT version which is not OpenResty's" + "; many optimizations will be disabled and " + "performance will be compromised (see " + "https://github.com/openresty/luajit2 for " + "OpenResty's LuaJIT or, even better, consider using " + "the OpenResty releases from https://openresty.org/" + "en/download.html)"); + } +#endif + + + rc = ngx_stream_lua_init_vm(&lmcf->lua, NULL, cf->cycle, cf->pool, + lmcf, cf->log, NULL); + if (rc != NGX_OK) { + if (rc == NGX_DECLINED) { + ngx_stream_lua_assert(lmcf->lua != NULL); + + ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, + "failed to load the 'resty.core' module " + "(https://github.com/openresty/lua-resty" + "-core); ensure you are using an OpenResty " + "release from https://openresty.org/en/" + "download.html (reason: %s)", + lua_tostring(lmcf->lua, -1)); + + } else { + /* rc == NGX_ERROR */ + ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, + "failed to initialize Lua VM"); + } + + return NGX_ERROR; + } + + /* rc == NGX_OK */ + + ngx_stream_lua_assert(lmcf->lua != NULL); + + if (!lmcf->requires_shm && lmcf->init_handler) { + saved_cycle = ngx_cycle; + ngx_cycle = cf->cycle; + + rc = lmcf->init_handler(cf->log, lmcf, lmcf->lua); + + ngx_cycle = saved_cycle; + + if (rc != NGX_OK) { + /* an error happened */ + return NGX_ERROR; + } + } + + dd("Lua VM initialized!"); + } + + return NGX_OK; +} + + +static char * +ngx_stream_lua_lowat_check(ngx_conf_t *cf, void *post, void *data) +{ +#if (NGX_FREEBSD) + ssize_t *np = data; + + if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"lua_send_lowat\" must be less than %d " + "(sysctl net.inet.tcp.sendspace)", + ngx_freebsd_net_inet_tcp_sendspace); + + return NGX_CONF_ERROR; + } + +#elif !(NGX_HAVE_SO_SNDLOWAT) + ssize_t *np = data; + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"lua_send_lowat\" is not supported, ignored"); + + *np = 0; + +#endif + + return NGX_CONF_OK; +} + + +static void * +ngx_stream_lua_create_main_conf(ngx_conf_t *cf) +{ + ngx_int_t rc; + + ngx_stream_lua_main_conf_t *lmcf; + + lmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_lua_main_conf_t)); + if (lmcf == NULL) { + return NULL; + } + + /* set by ngx_pcalloc: + * lmcf->lua = NULL; + * lmcf->lua_path = { 0, NULL }; + * lmcf->lua_cpath = { 0, NULL }; + * lmcf->pending_timers = 0; + * lmcf->running_timers = 0; + * lmcf->watcher = NULL; + * lmcf->regex_cache_entries = 0; + * lmcf->jit_stack = NULL; + * lmcf->shm_zones = NULL; + * lmcf->init_handler = NULL; + * lmcf->init_src = { 0, NULL }; + * lmcf->shm_zones_inited = 0; + * lmcf->shdict_zones = NULL; + * lmcf->preload_hooks = NULL; + * lmcf->requires_header_filter = 0; + * lmcf->requires_body_filter = 0; + * lmcf->requires_capture_filter = 0; + * lmcf->requires_rewrite = 0; + * lmcf->requires_access = 0; + * lmcf->requires_log = 0; + * lmcf->requires_shm = 0; + */ + + lmcf->pool = cf->pool; + lmcf->max_pending_timers = NGX_CONF_UNSET; + lmcf->max_running_timers = NGX_CONF_UNSET; +#if (NGX_PCRE) + lmcf->regex_cache_max_entries = NGX_CONF_UNSET; + lmcf->regex_match_limit = NGX_CONF_UNSET; +#endif + + lmcf->postponed_to_preread_phase_end = NGX_CONF_UNSET; + + lmcf->set_sa_restart = NGX_CONF_UNSET; + +#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM) + lmcf->malloc_trim_cycle = NGX_CONF_UNSET_UINT; +#endif + + rc = ngx_stream_lua_sema_mm_init(cf, lmcf); + if (rc != NGX_OK) { + return NULL; + } + + dd("nginx Lua module main config structure initialized!"); + + return lmcf; +} + + +static char * +ngx_stream_lua_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_stream_lua_main_conf_t *lmcf = conf; + +#if (NGX_PCRE) + if (lmcf->regex_cache_max_entries == NGX_CONF_UNSET) { + lmcf->regex_cache_max_entries = 1024; + } + + if (lmcf->regex_match_limit == NGX_CONF_UNSET) { + lmcf->regex_match_limit = 0; + } +#endif + + if (lmcf->max_pending_timers == NGX_CONF_UNSET) { + lmcf->max_pending_timers = 1024; + } + + if (lmcf->max_running_timers == NGX_CONF_UNSET) { + lmcf->max_running_timers = 256; + } + +#if (NGX_STREAM_LUA_HAVE_SA_RESTART) + if (lmcf->set_sa_restart == NGX_CONF_UNSET) { + lmcf->set_sa_restart = 1; + } +#endif + +#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM) + if (lmcf->malloc_trim_cycle == NGX_CONF_UNSET_UINT) { + lmcf->malloc_trim_cycle = 1000; /* number of reqs */ + } +#endif + + lmcf->cycle = cf->cycle; + + return NGX_CONF_OK; +} + + + + + + +static void * +ngx_stream_lua_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_lua_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_lua_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* set by ngx_pcalloc: + * lscf->srv.ssl_client_hello_handler = NULL; + * lscf->srv.ssl_client_hello_src = { 0, NULL }; + * lscf->srv.ssl_client_hello_src_key = NULL; + * + * lscf->srv.ssl_cert_handler = NULL; + * lscf->srv.ssl_cert_src = { 0, NULL }; + * lscf->srv.ssl_cert_src_key = NULL; + * + * lscf->srv.ssl_session_store_handler = NULL; + * lscf->srv.ssl_session_store_src = { 0, NULL }; + * lscf->srv.ssl_session_store_src_key = NULL; + * + * lscf->srv.ssl_session_fetch_handler = NULL; + * lscf->srv.ssl_session_fetch_src = { 0, NULL }; + * lscf->srv.ssl_session_fetch_src_key = NULL; + * + * lscf->balancer.handler = NULL; + * lscf->balancer.src = { 0, NULL }; + * lscf->balancer.src_key = NULL; + */ + + conf->enable_code_cache = NGX_CONF_UNSET; + conf->check_client_abort = NGX_CONF_UNSET; + + conf->keepalive_timeout = NGX_CONF_UNSET_MSEC; + conf->connect_timeout = NGX_CONF_UNSET_MSEC; + conf->send_timeout = NGX_CONF_UNSET_MSEC; + conf->read_timeout = NGX_CONF_UNSET_MSEC; + conf->send_lowat = NGX_CONF_UNSET_SIZE; + conf->buffer_size = NGX_CONF_UNSET_SIZE; + conf->pool_size = NGX_CONF_UNSET_UINT; + + conf->log_socket_errors = NGX_CONF_UNSET; + +#if (NGX_STREAM_SSL) + conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; +#endif + + return conf; +} + + +static char * +ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_lua_srv_conf_t *prev = parent; + ngx_stream_lua_srv_conf_t *conf = child; + +#if (NGX_STREAM_SSL) + ngx_stream_ssl_conf_t *sscf; + + dd("merge srv conf"); + + sscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module); + if (sscf && sscf->listen) { + if (conf->srv.ssl_client_hello_src.len == 0) { + conf->srv.ssl_client_hello_src = prev->srv.ssl_client_hello_src; + conf->srv.ssl_client_hello_src_key = + prev->srv.ssl_client_hello_src_key; + conf->srv.ssl_client_hello_handler = + prev->srv.ssl_client_hello_handler; + } + + if (conf->srv.ssl_client_hello_src.len) { + if (sscf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl configured for the server"); + + return NGX_CONF_ERROR; + } +#ifdef LIBRESSL_VERSION_NUMBER + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support by " + "ssl_client_hello_by_lua*"); + return NGX_CONF_ERROR; + +#else + +# ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + + SSL_CTX_set_client_hello_cb(sscf->ssl.ctx, + ngx_stream_lua_ssl_client_hello_handler, + NULL); + +# else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "OpenSSL too old to support " + "ssl_client_hello_by_lua*"); + return NGX_CONF_ERROR; + +# endif +#endif + } + + if (conf->srv.ssl_cert_src.len == 0) { + conf->srv.ssl_cert_src = prev->srv.ssl_cert_src; + conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key; + conf->srv.ssl_cert_handler = prev->srv.ssl_cert_handler; + } + + if (conf->srv.ssl_cert_src.len) { + if (sscf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl configured for the server"); + + return NGX_CONF_ERROR; + } + +#ifdef LIBRESSL_VERSION_NUMBER + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL is not supported by ssl_certificate_by_lua*"); + return NGX_CONF_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER >= 0x1000205fL + + SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_stream_lua_ssl_cert_handler, NULL); + +# else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "OpenSSL too old to support ssl_certificate_by_lua*"); + return NGX_CONF_ERROR; + +# endif + +#endif + } + } + + +#endif /* NGX_STREAM_SSL */ + +#if (NGX_STREAM_SSL) + + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3 + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2); + + ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, + "DEFAULT"); + + ngx_conf_merge_uint_value(conf->ssl_verify_depth, + prev->ssl_verify_depth, 1); + ngx_conf_merge_str_value(conf->ssl_trusted_certificate, + prev->ssl_trusted_certificate, ""); + ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, ""); +#if (nginx_version >= 1019004) + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands, + NULL); +#endif + + if (ngx_stream_lua_set_ssl(cf, conf) != NGX_OK) { + return NGX_CONF_ERROR; + } + +#endif + + ngx_conf_merge_value(conf->enable_code_cache, prev->enable_code_cache, 1); + ngx_conf_merge_value(conf->check_client_abort, prev->check_client_abort, 0); + + ngx_conf_merge_msec_value(conf->keepalive_timeout, + prev->keepalive_timeout, 60000); + + ngx_conf_merge_msec_value(conf->connect_timeout, + prev->connect_timeout, 60000); + + ngx_conf_merge_msec_value(conf->send_timeout, + prev->send_timeout, 60000); + + ngx_conf_merge_msec_value(conf->read_timeout, + prev->read_timeout, 60000); + + ngx_conf_merge_size_value(conf->send_lowat, + prev->send_lowat, 0); + + ngx_conf_merge_size_value(conf->buffer_size, + prev->buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_uint_value(conf->pool_size, prev->pool_size, 30); + + ngx_conf_merge_value(conf->log_socket_errors, prev->log_socket_errors, 1); + + if (conf->preread_src.value.len == 0) { + conf->preread_src = prev->preread_src; + conf->preread_handler = prev->preread_handler; + conf->preread_src_key = prev->preread_src_key; + conf->preread_chunkname = prev->preread_chunkname; + } + + return NGX_CONF_OK; +} + + + + +#if (NGX_STREAM_SSL) + +static ngx_int_t +ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf) +{ + ngx_pool_cleanup_t *cln; + + lscf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); + if (lscf->ssl == NULL) { + return NGX_ERROR; + } + + lscf->ssl->log = cf->log; + + if (ngx_ssl_create(lscf->ssl, lscf->ssl_protocols, NULL) != NGX_OK) { + return NGX_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_ssl_cleanup_ctx; + cln->data = lscf->ssl; + + if (SSL_CTX_set_cipher_list(lscf->ssl->ctx, + (const char *) lscf->ssl_ciphers.data) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "SSL_CTX_set_cipher_list(\"%V\") failed", + &lscf->ssl_ciphers); + return NGX_ERROR; + } + + if (lscf->ssl_trusted_certificate.len + && ngx_ssl_trusted_certificate(cf, lscf->ssl, + &lscf->ssl_trusted_certificate, + lscf->ssl_verify_depth) + != NGX_OK) + { + return NGX_ERROR; + } + + dd("ssl crl: %.*s", (int) lscf->ssl_crl.len, lscf->ssl_crl.data); + + if (ngx_ssl_crl(cf, lscf->ssl, &lscf->ssl_crl) != NGX_OK) { + return NGX_ERROR; + } + +#if (nginx_version >= 1019004) + if (ngx_ssl_conf_commands(cf, lscf->ssl, lscf->ssl_conf_commands) + != NGX_OK) { + return NGX_ERROR; + } +#endif + return NGX_OK; +} + +#if (nginx_version >= 1019004) +static char * +ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#endif + + return NGX_CONF_OK; +} +#endif +#endif /* NGX_STREAM_SSL */ + + +static char * +ngx_stream_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM) + + ngx_int_t nreqs; + ngx_str_t *value; + + ngx_stream_lua_main_conf_t *lmcf = conf; + + value = cf->args->elts; + + nreqs = ngx_atoi(value[1].data, value[1].len); + if (nreqs == NGX_ERROR) { + return "invalid number in the 1st argument"; + } + + lmcf->malloc_trim_cycle = (ngx_uint_t) nreqs; + + if (nreqs == 0) { + return NGX_CONF_OK; + } + + lmcf->requires_log = 1; + +#else + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "lua_malloc_trim is not supported " + "on this platform, ignored"); + +#endif + return NGX_CONF_OK; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_output.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_output.c new file mode 100644 index 000000000..cfb33cb10 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_output.c @@ -0,0 +1,739 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_output.c.tt2 + */ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_output.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_contentby.h" +#include + + +static int ngx_stream_lua_ngx_say(lua_State *L); +static int ngx_stream_lua_ngx_print(lua_State *L); +static int ngx_stream_lua_ngx_flush(lua_State *L); +static int ngx_stream_lua_ngx_eof(lua_State *L); + + +static int ngx_stream_lua_ngx_echo(lua_State *L, unsigned newline); +static void ngx_stream_lua_flush_cleanup(void *data); + + +static int +ngx_stream_lua_ngx_print(lua_State *L) +{ + dd("calling lua print"); + return ngx_stream_lua_ngx_echo(L, 0); +} + + +static int +ngx_stream_lua_ngx_say(lua_State *L) +{ + dd("calling"); + return ngx_stream_lua_ngx_echo(L, 1); +} + + +static int +ngx_stream_lua_ngx_echo(lua_State *L, unsigned newline) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + const char *p; + size_t len; + size_t size; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_int_t rc; + int i; + int nargs; + int type; + const char *msg; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + if (r->connection->type == SOCK_DGRAM) { + return luaL_error(L, "API disabled in the current context"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_PREREAD); + + + if (ctx->eof) { + lua_pushnil(L); + lua_pushliteral(L, "seen eof"); + return 2; + } + + nargs = lua_gettop(L); + size = 0; + + for (i = 1; i <= nargs; i++) { + + type = lua_type(L, i); + + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + + lua_tolstring(L, i, &len); + size += len; + break; + + case LUA_TNIL: + + size += sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + + if (lua_toboolean(L, i)) { + size += sizeof("true") - 1; + + } else { + size += sizeof("false") - 1; + } + + break; + + case LUA_TTABLE: + + size += ngx_stream_lua_calc_strlen_in_table(L, i, i, + 0 /* strict */); + break; + + case LUA_TLIGHTUSERDATA: + + dd("userdata: %p", lua_touserdata(L, i)); + + if (lua_touserdata(L, i) == NULL) { + size += sizeof("null") - 1; + break; + } + + continue; + + default: + + msg = lua_pushfstring(L, "string, number, boolean, nil, " + "ngx.null, or array table expected, " + "but got %s", lua_typename(L, type)); + + return luaL_argerror(L, i, msg); + } + } + + if (newline) { + size += sizeof("\n") - 1; + } + + if (size == 0) { + + lua_pushinteger(L, 1); + return 1; + } + + ctx->seen_body_data = 1; + + cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_bufs, size); + + if (cl == NULL) { + return luaL_error(L, "no memory"); + } + + b = cl->buf; + + for (i = 1; i <= nargs; i++) { + type = lua_type(L, i); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = lua_tolstring(L, i, &len); + b->last = ngx_copy(b->last, (u_char *) p, len); + break; + + case LUA_TNIL: + *b->last++ = 'n'; + *b->last++ = 'i'; + *b->last++ = 'l'; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, i)) { + *b->last++ = 't'; + *b->last++ = 'r'; + *b->last++ = 'u'; + *b->last++ = 'e'; + + } else { + *b->last++ = 'f'; + *b->last++ = 'a'; + *b->last++ = 'l'; + *b->last++ = 's'; + *b->last++ = 'e'; + } + + break; + + case LUA_TTABLE: + b->last = ngx_stream_lua_copy_str_in_table(L, i, b->last); + break; + + case LUA_TLIGHTUSERDATA: + *b->last++ = 'n'; + *b->last++ = 'u'; + *b->last++ = 'l'; + *b->last++ = 'l'; + break; + + default: + return luaL_error(L, "impossible to reach here"); + } + } + + if (newline) { + *b->last++ = '\n'; + } + +#if 0 + if (b->last != b->end) { + return luaL_error(L, "buffer error: %p != %p", b->last, b->end); + } +#endif + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + newline ? "lua say response" : "lua print response"); + + rc = ngx_stream_lua_send_chain_link(r, ctx, cl); + + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "nginx output filter error"); + return 2; + } + + dd("downstream write: %d, buf len: %d", (int) rc, + (int) (b->last - b->pos)); + + lua_pushinteger(L, 1); + return 1; +} + + +size_t +ngx_stream_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i, + unsigned strict) +{ + double key; + int max; + int i; + int type; + size_t size; + size_t len; + const char *msg; + + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + + dd("table index: %d", index); + + max = 0; + + lua_pushnil(L); /* stack: table key */ + while (lua_next(L, index) != 0) { /* stack: table key value */ + dd("key type: %s", luaL_typename(L, -2)); + + if (lua_type(L, -2) == LUA_TNUMBER) { + + key = lua_tonumber(L, -2); + + dd("key value: %d", (int) key); + + if (floor(key) == key && key >= 1) { + if (key > max) { + max = (int) key; + } + + lua_pop(L, 1); /* stack: table key */ + continue; + } + } + + /* not an array (non positive integer key) */ + lua_pop(L, 2); /* stack: table */ + + luaL_argerror(L, arg_i, "non-array table found"); + return 0; + } + + size = 0; + + for (i = 1; i <= max; i++) { + lua_rawgeti(L, index, i); /* stack: table value */ + type = lua_type(L, -1); + + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + + lua_tolstring(L, -1, &len); + size += len; + break; + + case LUA_TNIL: + + if (strict) { + goto bad_type; + } + + size += sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + + if (strict) { + goto bad_type; + } + + if (lua_toboolean(L, -1)) { + size += sizeof("true") - 1; + + } else { + size += sizeof("false") - 1; + } + + break; + + case LUA_TTABLE: + + size += ngx_stream_lua_calc_strlen_in_table(L, -1, arg_i, + strict); + break; + + case LUA_TLIGHTUSERDATA: + + if (strict) { + goto bad_type; + } + + if (lua_touserdata(L, -1) == NULL) { + size += sizeof("null") - 1; + break; + } + + continue; + + default: + +bad_type: + + msg = lua_pushfstring(L, "bad data type %s found", + lua_typename(L, type)); + return luaL_argerror(L, arg_i, msg); + } + + lua_pop(L, 1); /* stack: table */ + } + + return size; +} + + +u_char * +ngx_stream_lua_copy_str_in_table(lua_State *L, int index, u_char *dst) +{ + double key; + int max; + int i; + int type; + size_t len; + u_char *p; + + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + + max = 0; + + lua_pushnil(L); /* stack: table key */ + while (lua_next(L, index) != 0) { /* stack: table key value */ + key = lua_tonumber(L, -2); + if (key > max) { + max = (int) key; + } + + lua_pop(L, 1); /* stack: table key */ + } + + for (i = 1; i <= max; i++) { + lua_rawgeti(L, index, i); /* stack: table value */ + type = lua_type(L, -1); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) lua_tolstring(L, -1, &len); + dst = ngx_copy(dst, p, len); + break; + + case LUA_TNIL: + *dst++ = 'n'; + *dst++ = 'i'; + *dst++ = 'l'; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + *dst++ = 't'; + *dst++ = 'r'; + *dst++ = 'u'; + *dst++ = 'e'; + + } else { + *dst++ = 'f'; + *dst++ = 'a'; + *dst++ = 'l'; + *dst++ = 's'; + *dst++ = 'e'; + } + + break; + + case LUA_TTABLE: + dst = ngx_stream_lua_copy_str_in_table(L, -1, dst); + break; + + case LUA_TLIGHTUSERDATA: + + *dst++ = 'n'; + *dst++ = 'u'; + *dst++ = 'l'; + *dst++ = 'l'; + break; + + default: + luaL_error(L, "impossible to reach here"); + return NULL; + } + + lua_pop(L, 1); /* stack: table */ + } + + return dst; +} + + +/** + * Force flush out response content + * */ +static int +ngx_stream_lua_ngx_flush(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_chain_t *cl; + ngx_int_t rc; + int n; + unsigned wait = 0; + ngx_event_t *wev; + + ngx_stream_lua_srv_conf_t *cllscf; + + ngx_stream_lua_co_ctx_t *coctx; + + n = lua_gettop(L); + if (n > 1) { + return luaL_error(L, "attempt to pass %d arguments, but accepted 0 " + "or 1", n); + } + + r = ngx_stream_lua_get_req(L); + + if (n == 1) { + luaL_checktype(L, 1, LUA_TBOOLEAN); + wait = lua_toboolean(L, 1); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + if (r->connection->type == SOCK_DGRAM) { + return luaL_error(L, "API disabled in the current context"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_PREREAD); + + + coctx = ctx->cur_co_ctx; + if (coctx == NULL) { + return luaL_error(L, "no co ctx found"); + } + + + if (ctx->eof) { + lua_pushnil(L); + lua_pushliteral(L, "seen eof"); + return 2; + } + + + cl = ngx_stream_lua_get_flush_chain(r, ctx); + if (cl == NULL) { + return luaL_error(L, "no memory"); + } + + rc = ngx_stream_lua_send_chain_link(r, ctx, cl); + + dd("send chain: %d", (int) rc); + + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "nginx output filter error"); + return 2; + } + + dd("wait:%d, rc:%d, buffered:0x%x", wait, (int) rc, + r->connection->buffered); + + wev = r->connection->write; + + if (wait && (r->connection->buffered || wev->delayed)) + { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua flush requires waiting: buffered 0x%uxd, " + "delayed:%d", (unsigned) r->connection->buffered, + wev->delayed); + + coctx->flushing = 1; + ctx->flushing_coros++; + + if (ctx->entered_content_phase) { + /* mimic ngx_http_set_write_handler */ + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + cllscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (!wev->delayed) { + ngx_add_timer(wev, cllscf->send_timeout); + } + + + if (ngx_handle_write_event(wev, cllscf->send_lowat) != NGX_OK) { + if (wev->timer_set) { + wev->delayed = 0; + ngx_del_timer(wev); + } + + lua_pushnil(L); + lua_pushliteral(L, "connection broken"); + return 2; + } + + ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); + coctx->cleanup = ngx_stream_lua_flush_cleanup; + coctx->data = r; + + return lua_yield(L, 0); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua flush asynchronously"); + + lua_pushinteger(L, 1); + return 1; +} + + +/** + * Send last_buf, terminate output stream + * */ +static int +ngx_stream_lua_ngx_eof(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_int_t rc; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (lua_gettop(L) != 0) { + return luaL_error(L, "no argument is expected"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + + if (ctx->eof) { + lua_pushnil(L); + lua_pushliteral(L, "seen eof"); + return 2; + } + + if (r->connection->type == SOCK_DGRAM) { + return luaL_error(L, "API disabled in the current context"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_PREREAD); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua send eof"); + + rc = ngx_stream_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); + + dd("send chain: %d", (int) rc); + + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "nginx output filter error"); + return 2; + } + + lua_pushinteger(L, 1); + return 1; +} + + +void +ngx_stream_lua_inject_output_api(lua_State *L) +{ + + lua_pushcfunction(L, ngx_stream_lua_ngx_print); + lua_setfield(L, -2, "print"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_say); + lua_setfield(L, -2, "say"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_flush); + lua_setfield(L, -2, "flush"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_eof); + lua_setfield(L, -2, "eof"); +} + + + + +ngx_int_t +ngx_stream_lua_flush_resume_helper(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + int n; + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + + c = r->connection; + + ctx->cur_co_ctx->cleanup = NULL; + + /* push the return values */ + + if (c->timedout) { + lua_pushnil(ctx->cur_co_ctx->co); + lua_pushliteral(ctx->cur_co_ctx->co, "timeout"); + n = 2; + + } else if (c->error) { + lua_pushnil(ctx->cur_co_ctx->co); + lua_pushliteral(ctx->cur_co_ctx->co, "client aborted"); + n = 2; + + } else { + lua_pushinteger(ctx->cur_co_ctx->co, 1); + n = 1; + } + + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_stream_lua_run_thread(vm, r, ctx, n); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + /* rc == NGX_ERROR || rc >= NGX_OK */ + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +static void +ngx_stream_lua_flush_cleanup(void *data) +{ + ngx_stream_lua_request_t *r; + ngx_event_t *wev; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx = data; + + coctx->flushing = 0; + + r = coctx->data; + if (r == NULL) { + return; + } + + wev = r->connection->write; + + if (wev && wev->timer_set) { + ngx_del_timer(wev); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->flushing_coros--; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_output.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_output.h new file mode 100644 index 000000000..6cf941d96 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_output.h @@ -0,0 +1,36 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_output.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_OUTPUT_H_INCLUDED_ +#define _NGX_STREAM_LUA_OUTPUT_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_output_api(lua_State *L); + +size_t ngx_stream_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i, + unsigned strict); + +u_char *ngx_stream_lua_copy_str_in_table(lua_State *L, int index, u_char *dst); + +ngx_int_t ngx_stream_lua_flush_resume_helper(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx); + + +#endif /* _NGX_STREAM_LUA_OUTPUT_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_pcrefix.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_pcrefix.c new file mode 100644 index 000000000..37118a998 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_pcrefix.c @@ -0,0 +1,114 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_pcrefix.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_pcrefix.h" +#include "stdio.h" + +#if (NGX_PCRE) + +static ngx_pool_t *ngx_stream_lua_pcre_pool = NULL; + +static void *(*old_pcre_malloc)(size_t); +static void (*old_pcre_free)(void *ptr); + + +/* XXX: work-around to nginx regex subsystem, must init a memory pool + * to use PCRE functions. As PCRE still has memory-leaking problems, + * and nginx overwrote pcre_malloc/free hooks with its own static + * functions, so nobody else can reuse nginx regex subsystem... */ +static void * +ngx_stream_lua_pcre_malloc(size_t size) +{ + dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool); + + if (ngx_stream_lua_pcre_pool) { + return ngx_palloc(ngx_stream_lua_pcre_pool, size); + } + + fprintf(stderr, "error: lua pcre malloc failed due to empty pcre pool"); + + return NULL; +} + + +static void +ngx_stream_lua_pcre_free(void *ptr) +{ + dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool); + + if (ngx_stream_lua_pcre_pool) { + ngx_pfree(ngx_stream_lua_pcre_pool, ptr); + return; + } + + fprintf(stderr, "error: lua pcre free failed due to empty pcre pool"); +} + + +ngx_pool_t * +ngx_stream_lua_pcre_malloc_init(ngx_pool_t *pool) +{ + ngx_pool_t *old_pool; + + if (pcre_malloc != ngx_stream_lua_pcre_malloc) { + + dd("overriding nginx pcre malloc and free"); + + ngx_stream_lua_pcre_pool = pool; + + old_pcre_malloc = pcre_malloc; + old_pcre_free = pcre_free; + + pcre_malloc = ngx_stream_lua_pcre_malloc; + pcre_free = ngx_stream_lua_pcre_free; + + return NULL; + } + + dd("lua pcre pool was %p", ngx_stream_lua_pcre_pool); + + old_pool = ngx_stream_lua_pcre_pool; + ngx_stream_lua_pcre_pool = pool; + + dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool); + + return old_pool; +} + + +void +ngx_stream_lua_pcre_malloc_done(ngx_pool_t *old_pool) +{ + dd("lua pcre pool was %p", ngx_stream_lua_pcre_pool); + + ngx_stream_lua_pcre_pool = old_pool; + + dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool); + + if (old_pool == NULL) { + pcre_malloc = old_pcre_malloc; + pcre_free = old_pcre_free; + } +} + +#endif /* NGX_PCRE */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_pcrefix.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_pcrefix.h new file mode 100644 index 000000000..daff4c9fd --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_pcrefix.h @@ -0,0 +1,31 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_pcrefix.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_PCREFIX_H_INCLUDED_ +#define _NGX_STREAM_LUA_PCREFIX_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +#if (NGX_PCRE) +ngx_pool_t *ngx_stream_lua_pcre_malloc_init(ngx_pool_t *pool); +void ngx_stream_lua_pcre_malloc_done(ngx_pool_t *old_pool); +#endif + + +#endif /* _NGX_STREAM_LUA_PCREFIX_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_phase.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_phase.c new file mode 100644 index 000000000..db1f7658c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_phase.c @@ -0,0 +1,99 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_phase.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_phase.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_ctx.h" + + + +static int +ngx_stream_lua_ngx_get_phase(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + r = ngx_stream_lua_get_req(L); + + /* If we have no request object, assume we are called from the "init" + * phase. */ + + if (r == NULL) { + lua_pushliteral(L, "init"); + return 1; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + switch (ctx->context) { + case NGX_STREAM_LUA_CONTEXT_INIT_WORKER: + lua_pushliteral(L, "init_worker"); + break; + + case NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO: + lua_pushliteral(L, "ssl_client_hello"); + break; + + case NGX_STREAM_LUA_CONTEXT_SSL_CERT: + lua_pushliteral(L, "ssl_cert"); + break; + + case NGX_STREAM_LUA_CONTEXT_PREREAD: + lua_pushliteral(L, "preread"); + break; + + case NGX_STREAM_LUA_CONTEXT_CONTENT: + lua_pushliteral(L, "content"); + break; + + case NGX_STREAM_LUA_CONTEXT_LOG: + lua_pushliteral(L, "log"); + break; + + case NGX_STREAM_LUA_CONTEXT_TIMER: + lua_pushliteral(L, "timer"); + break; + + case NGX_STREAM_LUA_CONTEXT_BALANCER: + lua_pushliteral(L, "balancer"); + break; + + default: + return luaL_error(L, "unknown phase: %#x", (int) ctx->context); + } + + return 1; +} + + +void +ngx_stream_lua_inject_phase_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_stream_lua_ngx_get_phase); + lua_setfield(L, -2, "get_phase"); +} + + + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_phase.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_phase.h new file mode 100644 index 000000000..f31cb89b4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_phase.h @@ -0,0 +1,21 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_phase.h.tt2 + */ + +#ifndef _NGX_STREAM_LUA_PHASE_H_INCLUDED_ +#define _NGX_STREAM_LUA_PHASE_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_phase_api(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_PHASE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_prereadby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_prereadby.c new file mode 100644 index 000000000..e42787610 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_prereadby.c @@ -0,0 +1,333 @@ + +/* + * Copyright (C) OpenResty Inc. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include +#include "ngx_stream_lua_prereadby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_exception.h" +#include "ngx_stream_lua_cache.h" + + +static ngx_int_t ngx_stream_lua_preread_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); + + +ngx_int_t +ngx_stream_lua_preread_handler(ngx_stream_session_t *s) +{ + ngx_int_t rc; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_request_t *r; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "lua preread handler"); + + lmcf = ngx_stream_get_module_main_conf(s, ngx_stream_lua_module); + + if (!lmcf->postponed_to_preread_phase_end) { + ngx_stream_core_main_conf_t *cmcf; + ngx_stream_phase_handler_t tmp; + ngx_stream_phase_handler_t *ph; + ngx_stream_phase_handler_t *cur_ph; + ngx_stream_phase_handler_t *last_ph; + + lmcf->postponed_to_preread_phase_end = 1; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + ph = cmcf->phase_engine.handlers; + cur_ph = &ph[s->phase_handler]; + last_ph = &ph[cur_ph->next - 1]; + + if (cur_ph < last_ph) { + tmp = *cur_ph; + + ngx_memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) + * sizeof (ngx_stream_phase_handler_t)); + + *last_ph = tmp; + + s->phase_handler--; /* redo the current ph */ + + return NGX_DECLINED; + } + } + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); + + if (lscf->preread_handler == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "no preread handler found"); + return NGX_DECLINED; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(s); + if (ctx == NULL) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + } + + r = ctx->request; + + dd("entered? %d", (int) ctx->entered_preread_phase); + + if (ctx->entered_preread_phase) { + dd("calling wev handler"); + rc = ctx->resume_handler(r); + dd("wev handler returns %d", (int) rc); + + if (rc == NGX_ERROR || rc > NGX_OK) { + ngx_stream_lua_finalize_request(ctx->request, rc); + return NGX_DONE; + } + + if (rc == NGX_DONE && ctx->peek_needs_more_data) { + return NGX_AGAIN; + } + + if (rc == NGX_OK || rc == NGX_DONE) { + return rc; + } + + return NGX_DECLINED; + } + + r->connection->read->handler = ngx_stream_lua_request_handler; + r->connection->write->handler = ngx_stream_lua_request_handler; + + dd("calling preread handler"); + rc = lscf->preread_handler(r); + + if (rc == NGX_ERROR || rc > NGX_OK) { + ngx_stream_lua_finalize_request(ctx->request, rc); + return NGX_DONE; + } + + return rc; +} + + +ngx_int_t +ngx_stream_lua_preread_handler_inline(ngx_stream_lua_request_t *r) +{ + ngx_int_t rc; + lua_State *L; + ngx_stream_lua_srv_conf_t *lscf; + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua inline script (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + lscf->preread_src.value.data, + lscf->preread_src.value.len, + lscf->preread_src_key, + (const char *) + lscf->preread_chunkname); + + if (rc != NGX_OK) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + return ngx_stream_lua_preread_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_preread_handler_file(ngx_stream_lua_request_t *r) +{ + u_char *script_path; + ngx_int_t rc; + ngx_str_t eval_src; + lua_State *L; + ngx_stream_lua_srv_conf_t *lscf; + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + /* Eval nginx variables in code path string first */ + if (ngx_stream_complex_value(r->session, &lscf->preread_src, &eval_src) + != NGX_OK) + { + return NGX_ERROR; + } + + script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path, + lscf->preread_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_preread_by_chunk(L, r); +} + + +static ngx_int_t +ngx_stream_lua_preread_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_event_t *rev; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + + ngx_stream_lua_srv_conf_t *lscf; + + /* {{{ new coroutine to handle request */ + co = ngx_stream_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine " + "to handle request"); + + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_stream_lua_set_req(co, r); + + /* {{{ initialize request context */ + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_stream_lua_reset_ctx(r, L, ctx); + + ctx->entered_preread_phase = 1; + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + /* }}} */ + + /* {{{ register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + /* }}} */ + + ctx->context = NGX_STREAM_LUA_CONTEXT_PREREAD; + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (lscf->check_client_abort) { + r->read_event_handler = ngx_stream_lua_rd_check_broken_connection; + + rev = r->connection->read; + + if (!rev->active) { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + } else { + r->read_event_handler = ngx_stream_lua_block_reading; + } + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "preread run thread returned %d", (int) rc); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + c = r->connection; + + if (rc == NGX_AGAIN) { + rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0); + + if (rc == NGX_DONE && ctx->peek_needs_more_data) { + return NGX_AGAIN; + } + + if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) { + return rc; + } + + if (rc != NGX_OK) { + return NGX_DECLINED; + } + + } else if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + + rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0); + + if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) { + return rc; + } + + if (rc != NGX_OK) { + return NGX_DECLINED; + } + } + +#if 1 + if (rc == NGX_OK) { + return NGX_OK; + } +#endif + + return NGX_DECLINED; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_prereadby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_prereadby.h new file mode 100644 index 000000000..9248a3a07 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_prereadby.h @@ -0,0 +1,21 @@ + +/* + * Copyright (C) OpenResty Inc. + */ + + +#ifndef _NGX_STREAM_LUA_PREREAD_H_INCLUDED_ +#define _NGX_STREAM_LUA_PREREAD_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_preread_handler(ngx_stream_session_t *s); +ngx_int_t ngx_stream_lua_preread_handler_inline(ngx_stream_lua_request_t *r); +ngx_int_t ngx_stream_lua_preread_handler_file(ngx_stream_lua_request_t *r); + + +#endif /* _NGX_STREAM_LUA_PREREAD_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_probe.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_probe.h new file mode 100644 index 000000000..47eabd4c6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_probe.h @@ -0,0 +1,96 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_probe.h.tt2 + */ + +/* + * automatically generated from the file dtrace/ngx_lua_provider.d by the + * gen-dtrace-probe-header tool in the nginx-devel-utils project: + * https://github.com/agentzh/nginx-devel-utils + */ + +#ifndef _NGX_STREAM_LUA_PROBE_H_INCLUDED_ +#define _NGX_STREAM_LUA_PROBE_H_INCLUDED_ + + +#include +#include + + + + +#if defined(NGX_DTRACE) && NGX_DTRACE + +#include + +#define ngx_stream_lua_probe_info(s) \ + NGINX_LUA_HTTP_LUA_INFO(s) + +#define ngx_stream_lua_probe_register_preload_package(L, pkg) \ + NGINX_LUA_HTTP_LUA_REGISTER_PRELOAD_PACKAGE(L, pkg) + +#define ngx_stream_lua_probe_req_socket_consume_preread(r, data, len) \ + NGINX_LUA_HTTP_LUA_REQ_SOCKET_CONSUME_PREREAD(r, data, len) + +#define ngx_stream_lua_probe_user_coroutine_create(r, parent, child) \ + NGINX_LUA_HTTP_LUA_USER_COROUTINE_CREATE(r, parent, child) + +#define ngx_stream_lua_probe_user_coroutine_resume(r, parent, child) \ + NGINX_LUA_HTTP_LUA_USER_COROUTINE_RESUME(r, parent, child) + +#define ngx_stream_lua_probe_user_coroutine_yield(r, parent, child) \ + NGINX_LUA_HTTP_LUA_USER_COROUTINE_YIELD(r, parent, child) + +#define ngx_stream_lua_probe_thread_yield(r, L) \ + NGINX_LUA_HTTP_LUA_THREAD_YIELD(r, L) + +#define ngx_stream_lua_probe_socket_tcp_send_start(r, u, data, len) \ + NGINX_LUA_HTTP_LUA_SOCKET_TCP_SEND_START(r, u, data, len) + +#define ngx_stream_lua_probe_socket_tcp_receive_done(r, u, data, len) \ + NGINX_LUA_HTTP_LUA_SOCKET_TCP_RECEIVE_DONE(r, u, data, len) + +#define ngx_stream_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, \ + data, \ + len) \ + NGINX_LUA_HTTP_LUA_SOCKET_TCP_SETKEEPALIVE_BUF_UNREAD(r, u, data, len) + +#define ngx_stream_lua_probe_user_thread_spawn(r, creator, newthread) \ + NGINX_LUA_HTTP_LUA_USER_THREAD_SPAWN(r, creator, newthread) + +#define ngx_stream_lua_probe_thread_delete(r, thread, ctx) \ + NGINX_LUA_HTTP_LUA_THREAD_DELETE(r, thread, ctx) + +#define ngx_stream_lua_probe_run_posted_thread(r, thread, status) \ + NGINX_LUA_HTTP_LUA_RUN_POSTED_THREAD(r, thread, status) + +#define ngx_stream_lua_probe_coroutine_done(r, co, success) \ + NGINX_LUA_HTTP_LUA_COROUTINE_DONE(r, co, success) + +#define ngx_stream_lua_probe_user_thread_wait(parent, child) \ + NGINX_LUA_HTTP_LUA_USER_THREAD_WAIT(parent, child) + +#else /* !(NGX_DTRACE) */ + +#define ngx_stream_lua_probe_info(s) +#define ngx_stream_lua_probe_register_preload_package(L, pkg) +#define ngx_stream_lua_probe_req_socket_consume_preread(r, data, len) +#define ngx_stream_lua_probe_user_coroutine_create(r, parent, child) +#define ngx_stream_lua_probe_user_coroutine_resume(r, parent, child) +#define ngx_stream_lua_probe_user_coroutine_yield(r, parent, child) +#define ngx_stream_lua_probe_thread_yield(r, L) +#define ngx_stream_lua_probe_socket_tcp_send_start(r, u, data, len) +#define ngx_stream_lua_probe_socket_tcp_receive_done(r, u, data, len) +#define ngx_stream_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, len) +#define ngx_stream_lua_probe_user_thread_spawn(r, creator, newthread) +#define ngx_stream_lua_probe_thread_delete(r, thread, ctx) +#define ngx_stream_lua_probe_run_posted_thread(r, thread, status) +#define ngx_stream_lua_probe_coroutine_done(r, co, success) +#define ngx_stream_lua_probe_user_thread_wait(parent, child) + +#endif + +#endif /* _NGX_STREAM_LUA_PROBE_H_INCLUDED_ */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_regex.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_regex.c new file mode 100644 index 000000000..9fa8f4f4c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_regex.c @@ -0,0 +1,608 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_regex.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_PCRE) + +#include "ngx_stream_lua_pcrefix.h" +#include "ngx_stream_lua_script.h" +#include "ngx_stream_lua_util.h" + + +#if (PCRE_MAJOR >= 6) +# define LUA_HAVE_PCRE_DFA 1 +#else +# define LUA_HAVE_PCRE_DFA 0 +#endif + + +#define NGX_LUA_RE_MODE_DFA (1<<1) +#define NGX_LUA_RE_MODE_JIT (1<<2) +#define NGX_LUA_RE_NO_UTF8_CHECK (1<<4) + +#define NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT (100) + +#define NGX_LUA_RE_MIN_JIT_STACK_SIZE 32 * 1024 + + +typedef struct { + ngx_pool_t *pool; + u_char *name_table; + int name_count; + int name_entry_size; + + int ncaptures; + int *captures; + + pcre *regex; + pcre_extra *regex_sd; + + ngx_stream_lua_complex_value_t *replace; + + /* only for (stap) debugging, and may be an invalid pointer */ + const u_char *pattern; +} ngx_stream_lua_regex_t; + + +typedef struct { + ngx_str_t pattern; + ngx_pool_t *pool; + ngx_int_t options; + + pcre *regex; + int captures; + ngx_str_t err; +} ngx_stream_lua_regex_compile_t; + + +typedef struct { + ngx_stream_lua_request_t *request; + + pcre *regex; + pcre_extra *regex_sd; + int ncaptures; + int *captures; + int captures_len; + uint8_t flags; +} ngx_stream_lua_regex_ctx_t; + + +static void ngx_stream_lua_regex_free_study_data(ngx_pool_t *pool, + pcre_extra *sd); +static ngx_int_t ngx_stream_lua_regex_compile( + ngx_stream_lua_regex_compile_t *rc); + + +#define ngx_stream_lua_regex_exec(re, e, s, start, captures, size, \ + opts) \ + pcre_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \ + captures, size) + + +#define ngx_stream_lua_regex_dfa_exec(re, e, s, start, captures, size, \ + ws, wscount, opts) \ + pcre_dfa_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \ + captures, size, ws, wscount) + + +static void +ngx_stream_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd) +{ + ngx_pool_t *old_pool; + + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + +#if LUA_HAVE_PCRE_JIT + pcre_free_study(sd); +#else + pcre_free(sd); +#endif + + ngx_stream_lua_pcre_malloc_done(old_pool); +} + + +static ngx_int_t +ngx_stream_lua_regex_compile(ngx_stream_lua_regex_compile_t *rc) +{ + int n, erroff; + char *p; + const char *errstr; + pcre *re; + ngx_pool_t *old_pool; + + old_pool = ngx_stream_lua_pcre_malloc_init(rc->pool); + + re = pcre_compile((const char *) rc->pattern.data, (int) rc->options, + &errstr, &erroff, NULL); + + ngx_stream_lua_pcre_malloc_done(old_pool); + + if (re == NULL) { + if ((size_t) erroff == rc->pattern.len) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre_compile() failed: %s in \"%V\"", + errstr, &rc->pattern) + - rc->err.data; + + } else { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre_compile() failed: %s in \"%V\" " + "at \"%s\"", errstr, &rc->pattern, + rc->pattern.data + erroff) + - rc->err.data; + } + + return NGX_ERROR; + } + + rc->regex = re; + +#if 1 + n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d"; + goto failed; + } +#endif + + return NGX_OK; + +failed: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) + - rc->err.data; + return NGX_OK; +} + + +ngx_int_t +ngx_stream_lua_ffi_set_jit_stack_size(int size, u_char *errstr, + size_t *errstr_size) +{ +#if LUA_HAVE_PCRE_JIT + + ngx_stream_lua_main_conf_t *lmcf; + ngx_pool_t *pool, *old_pool; + + lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, + ngx_stream_lua_module); + + if (size < NGX_LUA_RE_MIN_JIT_STACK_SIZE) { + size = NGX_LUA_RE_MIN_JIT_STACK_SIZE; + } + + pool = lmcf->pool; + + dd("server pool %p", lmcf->pool); + + if (lmcf->jit_stack) { + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + + pcre_jit_stack_free(lmcf->jit_stack); + + ngx_stream_lua_pcre_malloc_done(old_pool); + } + + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + + lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE, + size); + + ngx_stream_lua_pcre_malloc_done(old_pool); + + if (lmcf->jit_stack == NULL) { + *errstr_size = ngx_snprintf(errstr, *errstr_size, + "pcre jit stack allocation failed") + - errstr; + return NGX_ERROR; + } + + return NGX_OK; + +#else /* LUA_HAVE_PCRE_JIT */ + + *errstr_size = ngx_snprintf(errstr, *errstr_size, + "no pcre jit support found") - errstr; + return NGX_ERROR; + +#endif /* LUA_HAVE_PCRE_JIT */ +} + + +ngx_stream_lua_regex_t * +ngx_stream_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, + int flags, int pcre_opts, u_char *errstr, + size_t errstr_size) +{ + int *cap = NULL, ovecsize; + u_char *p; + ngx_int_t rc; + const char *msg; + ngx_pool_t *pool, *old_pool; + pcre_extra *sd = NULL; + + ngx_stream_lua_regex_t *re; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_regex_compile_t re_comp; + + pool = ngx_create_pool(512, ngx_cycle->log); + if (pool == NULL) { + msg = "no memory"; + goto error; + } + + pool->log = (ngx_log_t *) &ngx_cycle->new_log; + + re = ngx_palloc(pool, sizeof(ngx_stream_lua_regex_t)); + if (re == NULL) { + ngx_destroy_pool(pool); + pool = NULL; + msg = "no memory"; + goto error; + } + + re->pool = pool; + + re_comp.options = pcre_opts; + re_comp.pattern.data = (u_char *) pat; + re_comp.pattern.len = pat_len; + re_comp.err.len = errstr_size - 1; + re_comp.err.data = errstr; + re_comp.pool = pool; + + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + rc = ngx_stream_lua_regex_compile(&re_comp); + ngx_stream_lua_pcre_malloc_done(old_pool); + + if (rc != NGX_OK) { + re_comp.err.data[re_comp.err.len] = '\0'; + msg = (char *) re_comp.err.data; + goto error; + } + + lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, + ngx_stream_lua_module); + +#if (LUA_HAVE_PCRE_JIT) + + if (flags & NGX_LUA_RE_MODE_JIT) { + + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); + ngx_stream_lua_pcre_malloc_done(old_pool); + +# if (NGX_DEBUG) + if (msg != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "pcre study failed with PCRE_STUDY_JIT_COMPILE: " + "%s (%p)", msg, sd); + } + + if (sd != NULL) { + int jitted; + + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + + pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); + + ngx_stream_lua_pcre_malloc_done(old_pool); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "pcre JIT compiling result: %d", jitted); + } +# endif /* !(NGX_DEBUG) */ + + } else { + old_pool = ngx_stream_lua_pcre_malloc_init(pool); + sd = pcre_study(re_comp.regex, 0, &msg); + ngx_stream_lua_pcre_malloc_done(old_pool); + } + + if (sd && lmcf->jit_stack) { + pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack); + } + +#endif /* LUA_HAVE_PCRE_JIT */ + + if (sd + && lmcf && lmcf->regex_match_limit > 0 + && !(flags & NGX_LUA_RE_MODE_DFA)) + { + sd->flags |= PCRE_EXTRA_MATCH_LIMIT; + sd->match_limit = lmcf->regex_match_limit; + } + + if (flags & NGX_LUA_RE_MODE_DFA) { + ovecsize = 2; + re_comp.captures = 0; + + } else { + ovecsize = (re_comp.captures + 1) * 3; + } + + dd("allocating cap with size: %d", (int) ovecsize); + + cap = ngx_palloc(pool, ovecsize * sizeof(int)); + if (cap == NULL) { + msg = "no memory"; + goto error; + } + + if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, + &re->name_count) != 0) + { + msg = "cannot acquire named subpattern count"; + goto error; + } + + if (re->name_count > 0) { + if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE, + &re->name_entry_size) != 0) + { + msg = "cannot acquire named subpattern entry size"; + goto error; + } + + if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE, + &re->name_table) != 0) + { + msg = "cannot acquire named subpattern table"; + goto error; + } + } + + re->regex = re_comp.regex; + re->regex_sd = sd; + re->ncaptures = re_comp.captures; + re->captures = cap; + re->replace = NULL; + + /* only for (stap) debugging, the pointer might be invalid when the + * string is collected later on.... */ + re->pattern = pat; + + return re; + +error: + + p = ngx_snprintf(errstr, errstr_size - 1, "%s", msg); + *p = '\0'; + + if (sd) { + ngx_stream_lua_regex_free_study_data(pool, sd); + } + + if (pool) { + ngx_destroy_pool(pool); + } + + return NULL; +} + + +int +ngx_stream_lua_ffi_exec_regex(ngx_stream_lua_regex_t *re, int flags, + const u_char *s, size_t len, int pos) +{ + int rc, ovecsize, exec_opts, *cap; + ngx_str_t subj; + pcre_extra *sd; + + cap = re->captures; + sd = re->regex_sd; + + if (flags & NGX_LUA_RE_MODE_DFA) { + ovecsize = 2; + re->ncaptures = 0; + + } else { + ovecsize = (re->ncaptures + 1) * 3; + } + + if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { + exec_opts = PCRE_NO_UTF8_CHECK; + + } else { + exec_opts = 0; + } + + subj.data = (u_char *) s; + subj.len = len; + + if (flags & NGX_LUA_RE_MODE_DFA) { + +#if LUA_HAVE_PCRE_DFA + + int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; + rc = ngx_stream_lua_regex_dfa_exec(re->regex, sd, &subj, + (int) pos, cap, ovecsize, ws, + sizeof(ws)/sizeof(ws[0]), exec_opts); + +#else + + return PCRE_ERROR_BADOPTION; + +#endif /* LUA_HAVE_PCRE_DFA */ + + } else { + rc = ngx_stream_lua_regex_exec(re->regex, sd, &subj, (int) pos, cap, + ovecsize, exec_opts); + } + + return rc; +} + + +void +ngx_stream_lua_ffi_destroy_regex(ngx_stream_lua_regex_t *re) +{ + ngx_pool_t *old_pool; + + dd("destroy regex called"); + + if (re == NULL || re->pool == NULL) { + return; + } + + if (re->regex_sd) { + old_pool = ngx_stream_lua_pcre_malloc_init(re->pool); +#if LUA_HAVE_PCRE_JIT + pcre_free_study(re->regex_sd); +#else + pcre_free(re->regex_sd); +#endif + ngx_stream_lua_pcre_malloc_done(old_pool); + re->regex_sd = NULL; + } + + ngx_destroy_pool(re->pool); +} + + +int +ngx_stream_lua_ffi_compile_replace_template(ngx_stream_lua_regex_t *re, + const u_char *replace_data, size_t replace_len) +{ + ngx_int_t rc; + ngx_str_t tpl; + + ngx_stream_lua_complex_value_t *ctpl; + ngx_stream_lua_compile_complex_value_t ccv; + + ctpl = ngx_palloc(re->pool, sizeof(ngx_stream_lua_complex_value_t)); + if (ctpl == NULL) { + return NGX_ERROR; + } + + if (replace_len != 0) { + /* copy the string buffer pointed to by tpl.data from Lua VM */ + tpl.data = ngx_palloc(re->pool, replace_len + 1); + if (tpl.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(tpl.data, replace_data, replace_len); + tpl.data[replace_len] = '\0'; + + } else { + tpl.data = (u_char *) replace_data; + } + + tpl.len = replace_len; + + ngx_memzero(&ccv, sizeof(ngx_stream_lua_compile_complex_value_t)); + ccv.pool = re->pool; + ccv.log = ngx_cycle->log; + ccv.value = &tpl; + ccv.complex_value = ctpl; + + rc = ngx_stream_lua_compile_complex_value(&ccv); + + re->replace = ctpl; + + return rc; +} + + +ngx_stream_lua_script_engine_t * +ngx_stream_lua_ffi_create_script_engine(void) +{ + return ngx_calloc(sizeof(ngx_stream_lua_script_engine_t), ngx_cycle->log); +} + + +void +ngx_stream_lua_ffi_init_script_engine(ngx_stream_lua_script_engine_t *e, + const unsigned char *subj, ngx_stream_lua_regex_t *compiled, int count) +{ + e->log = ngx_cycle->log; + e->ncaptures = count * 2; + e->captures = compiled->captures; + e->captures_data = (u_char *) subj; +} + + +void +ngx_stream_lua_ffi_destroy_script_engine(ngx_stream_lua_script_engine_t *e) +{ + ngx_free(e); +} + + +size_t +ngx_stream_lua_ffi_script_eval_len(ngx_stream_lua_script_engine_t *e, + ngx_stream_lua_complex_value_t *val) +{ + size_t len; + + ngx_stream_lua_script_len_code_pt lcode; + + e->ip = val->lengths; + len = 0; + + while (*(uintptr_t *) e->ip) { + lcode = *(ngx_stream_lua_script_len_code_pt *) e->ip; + len += lcode(e); + } + + return len; +} + + +void +ngx_stream_lua_ffi_script_eval_data(ngx_stream_lua_script_engine_t *e, + ngx_stream_lua_complex_value_t *val, u_char *dst) +{ + ngx_stream_lua_script_code_pt code; + + e->ip = val->values; + e->pos = dst; + + while (*(uintptr_t *) e->ip) { + code = *(ngx_stream_lua_script_code_pt *) e->ip; + code(e); + } +} + + +uint32_t +ngx_stream_lua_ffi_max_regex_cache_size(void) +{ + ngx_stream_lua_main_conf_t *lmcf; + lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, + ngx_stream_lua_module); + if (lmcf == NULL) { + return 0; + } + return (uint32_t) lmcf->regex_cache_max_entries; +} + + +const char * +ngx_stream_lua_ffi_pcre_version(void) +{ + return pcre_version(); +} + + +#endif /* NGX_PCRE */ + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_request.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_request.c new file mode 100644 index 000000000..39fda63f5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_request.c @@ -0,0 +1,350 @@ + +/* + * Copyright (C) OpenResty Inc. + */ + + +#include +#include +#include +#include "ddebug.h" +#include "ngx_stream_lua_common.h" +#include "ngx_stream_lua_request.h" +#include "ngx_stream_lua_contentby.h" + + +static ngx_int_t ngx_stream_lua_set_write_handler(ngx_stream_lua_request_t *r); +static void ngx_stream_lua_writer(ngx_stream_lua_request_t *r); +static void ngx_stream_lua_request_cleanup(void *data); + + +ngx_stream_lua_cleanup_t * +ngx_stream_lua_cleanup_add(ngx_stream_lua_request_t *r, size_t size) +{ + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_ctx_t *ctx; + + if (size == 0) { + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx != NULL && ctx->free_cleanup) { + cln = ctx->free_cleanup; + ctx->free_cleanup = cln->next; + + dd("reuse cleanup: %p", cln); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua stream cleanup reuse: %p", cln); + + cln->handler = NULL; + cln->next = r->cleanup; + + r->cleanup = cln; + + return cln; + } + } + + cln = ngx_palloc(r->pool, sizeof(ngx_stream_lua_cleanup_t)); + if (cln == NULL) { + return NULL; + } + + if (size) { + cln->data = ngx_palloc(r->pool, size); + if (cln->data == NULL) { + return NULL; + } + + } else { + cln->data = NULL; + } + + cln->handler = NULL; + cln->next = r->cleanup; + + r->cleanup = cln; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream cleanup add: %p", cln); + + return cln; +} + + +static void +ngx_stream_lua_request_cleanup(void *data) +{ + ngx_stream_lua_request_t *r = data; + ngx_stream_lua_cleanup_t *cln; + + cln = r->cleanup; + r->cleanup = NULL; + + while (cln) { + if (cln->handler) { + cln->handler(cln->data); + } + + cln = cln->next; + } +} + + +ngx_stream_lua_request_t * +ngx_stream_lua_create_request(ngx_stream_session_t *s) +{ + ngx_pool_t *pool; + ngx_stream_lua_request_t *r; + ngx_pool_cleanup_t *cln; + +#if 0 + pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, s->connection->log); + if (pool == NULL) { + return NULL; + } +#endif + + pool = s->connection->pool; + + r = ngx_pcalloc(pool, sizeof(ngx_stream_lua_request_t)); + if (r == NULL) { + return NULL; + } + + r->connection = s->connection; + r->session = s; + r->pool = pool; + + cln = ngx_pool_cleanup_add(pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_stream_lua_request_cleanup; + cln->data = r; + + return r; +} + + +void +ngx_stream_lua_request_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + c = ev->data; + s = c->data; + + if (ev->delayed && ev->timedout) { + ev->delayed = 0; + ev->timedout = 0; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + r = ctx->request; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "session run request: \"%p\"", r); + + if (ev->write) { + r->write_event_handler(r); + + } else { + r->read_event_handler(r); + } +} + + +void +ngx_stream_lua_empty_handler(ngx_event_t *wev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, wev->log, 0, + "stream lua empty handler"); + return; +} + + +void +ngx_stream_lua_block_reading(ngx_stream_lua_request_t *r) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream reading blocked"); + + /* aio does not call this handler */ + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) + && r->connection->read->active) + { + if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) { + ngx_stream_lua_finalize_real_request(r, + NGX_STREAM_INTERNAL_SERVER_ERROR); + } + } +} + + +void +ngx_stream_lua_finalize_real_request(ngx_stream_lua_request_t *r, ngx_int_t rc) +{ +#if 0 + ngx_pool_t *pool; +#endif + ngx_stream_session_t *s; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "finalize stream request: %i", rc); + + s = r->session; + + if (rc == NGX_ERROR) { + rc = NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + if (rc == NGX_DECLINED || rc == NGX_STREAM_INTERNAL_SERVER_ERROR) { + goto done; + } + + if (rc == NGX_DONE) { + return; + } + + if (rc == NGX_OK) { + rc = NGX_STREAM_OK; + } + + if (r->connection->buffered) { + if (ngx_stream_lua_set_write_handler(r) != NGX_OK) { + goto done; + } + + return; + } + +done: + +#if 0 + pool = r->pool; + r->pool = NULL; + + ngx_destroy_pool(pool); +#endif + + ngx_stream_finalize_session(s, rc); + return; +} + + +void +ngx_stream_lua_request_empty_handler(ngx_stream_lua_request_t *r) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream request empty handler"); + + return; +} + + +static void +ngx_stream_lua_writer(ngx_stream_lua_request_t *r) +{ + ngx_int_t rc; + ngx_event_t *wev; + ngx_connection_t *c; + ngx_stream_lua_srv_conf_t *lscf; + + c = r->connection; + wev = c->write; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, wev->log, 0, + "stream writer handler"); + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (wev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + c->timedout = 1; + + ngx_stream_lua_finalize_real_request(r, NGX_ERROR); + return; + } + + rc = ngx_stream_top_filter(r->session, NULL, 1); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream writer output filter: %i", rc); + + if (rc == NGX_ERROR) { + ngx_stream_lua_finalize_real_request(r, rc); + return; + } + + if (c->buffered) { + if (!wev->delayed) { + ngx_add_timer(wev, lscf->send_timeout); + } + + if (ngx_handle_write_event(wev, lscf->send_lowat) != NGX_OK) { + ngx_stream_lua_finalize_real_request(r, NGX_ERROR); + } + + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, wev->log, 0, + "stream writer done"); + + r->write_event_handler = ngx_stream_lua_request_empty_handler; + + ngx_stream_lua_finalize_real_request(r, rc); +} + + +static ngx_int_t +ngx_stream_lua_set_write_handler(ngx_stream_lua_request_t *r) +{ + ngx_event_t *wev; + ngx_stream_lua_srv_conf_t *lscf; + + r->read_event_handler = ngx_stream_lua_request_empty_handler; + r->write_event_handler = ngx_stream_lua_writer; + + wev = r->connection->write; + + if (wev->ready && wev->delayed) { + return NGX_OK; + } + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + if (!wev->delayed) { + ngx_add_timer(wev, lscf->send_timeout); + } + + if (ngx_handle_write_event(wev, lscf->send_lowat) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_stream_lua_core_run_phases(ngx_stream_lua_request_t *r) +{ + ngx_stream_session_t *s; + + s = r->session; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua session run phases: \"%p\"", r); + + ngx_stream_core_run_phases(s); +} diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_request.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_request.h new file mode 100644 index 000000000..ea4e05fb2 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_request.h @@ -0,0 +1,67 @@ + +/* + * Copyright (C) OpenResty Inc. + */ + + +#ifndef _NGX_STREAM_LUA_REQUEST_H_INCLUDED_ +#define _NGX_STREAM_LUA_REQUEST_H_INCLUDED_ + + +typedef void (*ngx_stream_lua_cleanup_pt)(void *data); + +typedef struct ngx_stream_lua_cleanup_s ngx_stream_lua_cleanup_t; + +struct ngx_stream_lua_cleanup_s { + ngx_stream_lua_cleanup_pt handler; + void *data; + ngx_stream_lua_cleanup_t *next; +}; + + +typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t; + +typedef void (*ngx_stream_lua_event_handler_pt)(ngx_stream_lua_request_t *r); + + +struct ngx_stream_lua_request_s { + ngx_connection_t *connection; + ngx_stream_session_t *session; + ngx_pool_t *pool; + ngx_stream_lua_cleanup_t *cleanup; + + ngx_stream_lua_event_handler_pt read_event_handler; + ngx_stream_lua_event_handler_pt write_event_handler; +}; + + +void ngx_stream_lua_empty_handler(ngx_event_t *wev); +void ngx_stream_lua_request_handler(ngx_event_t *ev); +void ngx_stream_lua_block_reading(ngx_stream_lua_request_t *r); + +ngx_stream_lua_cleanup_t * +ngx_stream_lua_cleanup_add(ngx_stream_lua_request_t *r, size_t size); + +ngx_stream_lua_request_t * +ngx_stream_lua_create_request(ngx_stream_session_t *s); +void ngx_stream_lua_finalize_real_request(ngx_stream_lua_request_t *r, + ngx_int_t rc); +void ngx_stream_lua_core_run_phases(ngx_stream_lua_request_t *r); + + +typedef ngx_int_t (*ngx_stream_lua_handler_pt)(ngx_stream_lua_request_t *r); + + +#define ngx_stream_lua_get_module_ctx(r, module) \ + ngx_stream_get_module_ctx((r)->session, module) +#define ngx_stream_lua_set_ctx(r, c, module) \ + ngx_stream_set_ctx((r)->session, c, module) +#define ngx_stream_lua_get_module_main_conf(r, module) \ + ngx_stream_get_module_main_conf((r)->session, module) +#define ngx_stream_lua_get_module_srv_conf(r, module) \ + ngx_stream_get_module_srv_conf((r)->session, module) +#define ngx_stream_lua_get_module_loc_conf \ + ngx_stream_lua_get_module_srv_conf + + +#endif /* _NGX_STREAM_LUA_REQUEST_H_INCLUDED_ */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_script.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_script.c new file mode 100644 index 000000000..2c82585f5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_script.c @@ -0,0 +1,555 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_script.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_script.h" + + +static void *ngx_stream_lua_script_add_code(ngx_array_t *codes, size_t size); +static size_t ngx_stream_lua_script_copy_len_code( + ngx_stream_lua_script_engine_t *e); +static void ngx_stream_lua_script_copy_code(ngx_stream_lua_script_engine_t *e); +static ngx_int_t ngx_stream_lua_script_add_copy_code( + ngx_stream_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last); +static ngx_int_t ngx_stream_lua_script_compile( + ngx_stream_lua_script_compile_t *sc); +static ngx_int_t ngx_stream_lua_script_add_capture_code( + ngx_stream_lua_script_compile_t *sc, ngx_uint_t n); +static size_t ngx_stream_lua_script_copy_capture_len_code( + ngx_stream_lua_script_engine_t *e); +static void ngx_stream_lua_script_copy_capture_code( + ngx_stream_lua_script_engine_t *e); +static ngx_int_t ngx_stream_lua_script_done( + ngx_stream_lua_script_compile_t *sc); +static ngx_int_t ngx_stream_lua_script_init_arrays( + ngx_stream_lua_script_compile_t *sc); + + +ngx_int_t +ngx_stream_lua_compile_complex_value( + ngx_stream_lua_compile_complex_value_t *ccv) +{ + ngx_str_t *v; + ngx_uint_t i, n, nv; + ngx_array_t lengths, values, *pl, *pv; + + ngx_stream_lua_script_compile_t sc; + + v = ccv->value; + + nv = 0; + + for (i = 0; i < v->len; i++) { + if (v->data[i] == '$') { + nv++; + } + } + + ccv->complex_value->value = *v; + ccv->complex_value->lengths = NULL; + ccv->complex_value->values = NULL; + + if (nv == 0) { + return NGX_OK; + } + + n = nv * (2 * sizeof(ngx_stream_lua_script_copy_code_t) + + sizeof(ngx_stream_lua_script_capture_code_t)) + + sizeof(uintptr_t); + + if (ngx_array_init(&lengths, ccv->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + n = (nv * (2 * sizeof(ngx_stream_lua_script_copy_code_t) + + sizeof(ngx_stream_lua_script_capture_code_t)) + + sizeof(uintptr_t) + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + if (ngx_array_init(&values, ccv->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + pl = &lengths; + pv = &values; + + ngx_memzero(&sc, sizeof(ngx_stream_lua_script_compile_t)); + + sc.pool = ccv->pool; + sc.log = ccv->log; + sc.source = v; + sc.lengths = &pl; + sc.values = &pv; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_stream_lua_script_compile(&sc) != NGX_OK) { + ngx_array_destroy(&lengths); + ngx_array_destroy(&values); + return NGX_ERROR; + } + + ccv->complex_value->lengths = lengths.elts; + ccv->complex_value->values = values.elts; + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_lua_complex_value(ngx_stream_lua_request_t *r, ngx_str_t *subj, + size_t offset, ngx_int_t count, int *cap, + ngx_stream_lua_complex_value_t *val, luaL_Buffer *luabuf) +{ + size_t len; + u_char *p; + + ngx_stream_lua_script_code_pt code; + ngx_stream_lua_script_len_code_pt lcode; + ngx_stream_lua_script_engine_t e; + + if (val->lengths == NULL) { + luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset); + luaL_addlstring(luabuf, (char *) val->value.data, val->value.len); + + return NGX_OK; + } + + ngx_memzero(&e, sizeof(ngx_stream_lua_script_engine_t)); + + e.log = r->connection->log; + e.ncaptures = count * 2; + e.captures = cap; + e.captures_data = subj->data; + + e.ip = val->lengths; + + len = 0; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_stream_lua_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + e.ip = val->values; + e.pos = p; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_stream_lua_script_code_pt *) e.ip; + code((ngx_stream_lua_script_engine_t *) &e); + } + + luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset); + luaL_addlstring(luabuf, (char *) p, len); + + ngx_pfree(r->pool, p); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_script_compile(ngx_stream_lua_script_compile_t *sc) +{ + u_char ch; + ngx_str_t name; + ngx_uint_t i, bracket; + unsigned num_var; + ngx_uint_t n = 0; + + if (ngx_stream_lua_script_init_arrays(sc) != NGX_OK) { + return NGX_ERROR; + } + + for (i = 0; i < sc->source->len; /* void */ ) { + + name.len = 0; + + if (sc->source->data[i] == '$') { + + if (++i == sc->source->len) { + goto invalid_variable; + } + + if (sc->source->data[i] == '$') { + name.data = &sc->source->data[i]; + i++; + name.len++; + + if (ngx_stream_lua_script_add_copy_code(sc, &name, + i == sc->source->len) + != NGX_OK) + { + return NGX_ERROR; + } + + continue; + } + + if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') { + num_var = 1; + n = 0; + + } else { + num_var = 0; + } + + if (sc->source->data[i] == '{') { + bracket = 1; + + if (++i == sc->source->len) { + goto invalid_variable; + } + + if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') { + num_var = 1; + n = 0; + } + + name.data = &sc->source->data[i]; + + } else { + bracket = 0; + name.data = &sc->source->data[i]; + } + + for ( /* void */ ; i < sc->source->len; i++, name.len++) { + ch = sc->source->data[i]; + + if (ch == '}' && bracket) { + i++; + bracket = 0; + break; + } + + if (num_var) { + if (ch >= '0' && ch <= '9') { + n = n * 10 + (ch - '0'); + continue; + } + + break; + } + + /* not a number variable like $1, $2, etc */ + + if ((ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') + || ch == '_') + { + continue; + } + + break; + } + + if (bracket) { + ngx_log_error(NGX_LOG_ERR, sc->log, 0, + "the closing bracket in \"%V\" " + "variable is missing", &name); + return NGX_ERROR; + } + + if (name.len == 0) { + goto invalid_variable; + } + + if (!num_var) { + ngx_log_error(NGX_LOG_ERR, sc->log, 0, + "attempt to use named capturing variable " + "\"%V\" (named captures not supported yet)", + &name); + + return NGX_ERROR; + } + + sc->variables++; + + if (ngx_stream_lua_script_add_capture_code(sc, n) != NGX_OK) { + return NGX_ERROR; + } + + continue; + } + + name.data = &sc->source->data[i]; + + while (i < sc->source->len) { + + if (sc->source->data[i] == '$') { + break; + } + + i++; + name.len++; + } + + if (ngx_stream_lua_script_add_copy_code(sc, &name, + i == sc->source->len) + != NGX_OK) + { + return NGX_ERROR; + } + } + + return ngx_stream_lua_script_done(sc); + +invalid_variable: + + ngx_log_error(NGX_LOG_ERR, sc->log, 0, + "lua script: invalid capturing variable name found in \"%V\"", + sc->source); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_stream_lua_script_add_copy_code(ngx_stream_lua_script_compile_t *sc, + ngx_str_t *value, ngx_uint_t last) +{ + size_t size, len; + + ngx_stream_lua_script_copy_code_t *code; + + len = value->len; + + code = ngx_stream_lua_script_add_code(*sc->lengths, + sizeof(ngx_stream_lua_script_copy_code_t)); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_lua_script_code_pt) (void *) + ngx_stream_lua_script_copy_len_code; + code->len = len; + + size = (sizeof(ngx_stream_lua_script_copy_code_t) + len + + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); + + code = ngx_stream_lua_script_add_code(*sc->values, size); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_lua_script_copy_code; + code->len = len; + + ngx_memcpy((u_char *) code + sizeof(ngx_stream_lua_script_copy_code_t), + value->data, value->len); + + return NGX_OK; +} + + +static size_t +ngx_stream_lua_script_copy_len_code(ngx_stream_lua_script_engine_t *e) +{ + ngx_stream_lua_script_copy_code_t *code; + + code = (ngx_stream_lua_script_copy_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_lua_script_copy_code_t); + + return code->len; +} + + +static void +ngx_stream_lua_script_copy_code(ngx_stream_lua_script_engine_t *e) +{ + u_char *p; + + ngx_stream_lua_script_copy_code_t *code; + + code = (ngx_stream_lua_script_copy_code_t *) e->ip; + + p = e->pos; + + if (!e->skip) { + e->pos = ngx_copy(p, e->ip + sizeof(ngx_stream_lua_script_copy_code_t), + code->len); + } + + e->ip += sizeof(ngx_stream_lua_script_copy_code_t) + + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->log, 0, + "lua script copy: \"%*s\"", e->pos - p, p); +} + + +static ngx_int_t +ngx_stream_lua_script_add_capture_code(ngx_stream_lua_script_compile_t *sc, + ngx_uint_t n) +{ + ngx_stream_lua_script_capture_code_t *code; + + code = ngx_stream_lua_script_add_code(*sc->lengths, + sizeof(ngx_stream_lua_script_capture_code_t)); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_lua_script_code_pt) (void *) + ngx_stream_lua_script_copy_capture_len_code; + code->n = 2 * n; + + code = ngx_stream_lua_script_add_code(*sc->values, + sizeof(ngx_stream_lua_script_capture_code_t)); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_lua_script_copy_capture_code; + code->n = 2 * n; + + return NGX_OK; +} + + +static size_t +ngx_stream_lua_script_copy_capture_len_code(ngx_stream_lua_script_engine_t *e) +{ + int *cap; + ngx_uint_t n; + + ngx_stream_lua_script_capture_code_t *code; + + code = (ngx_stream_lua_script_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_lua_script_capture_code_t); + + n = code->n; + + if (n < e->ncaptures) { + cap = e->captures; + return cap[n + 1] - cap[n]; + } + + return 0; +} + + +static void +ngx_stream_lua_script_copy_capture_code(ngx_stream_lua_script_engine_t *e) +{ + int *cap; + u_char *p, *pos; + ngx_uint_t n; + + ngx_stream_lua_script_capture_code_t *code; + + code = (ngx_stream_lua_script_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_lua_script_capture_code_t); + + n = code->n; + + pos = e->pos; + + if (n < e->ncaptures) { + + cap = e->captures; + p = e->captures_data; + + e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->log, 0, + "lua script capture: \"%*s\"", e->pos - pos, pos); +} + + +static ngx_int_t +ngx_stream_lua_script_init_arrays(ngx_stream_lua_script_compile_t *sc) +{ + ngx_uint_t n; + + if (*sc->lengths == NULL) { + n = sc->variables * (2 * sizeof(ngx_stream_lua_script_copy_code_t) + + sizeof(ngx_stream_lua_script_capture_code_t)) + + sizeof(uintptr_t); + + *sc->lengths = ngx_array_create(sc->pool, n, 1); + if (*sc->lengths == NULL) { + return NGX_ERROR; + } + } + + if (*sc->values == NULL) { + n = (sc->variables * (2 * sizeof(ngx_stream_lua_script_copy_code_t) + + sizeof(ngx_stream_lua_script_capture_code_t)) + + sizeof(uintptr_t) + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + *sc->values = ngx_array_create(sc->pool, n, 1); + if (*sc->values == NULL) { + return NGX_ERROR; + } + } + + sc->variables = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_script_done(ngx_stream_lua_script_compile_t *sc) +{ + uintptr_t *code; + + if (sc->complete_lengths) { + code = ngx_stream_lua_script_add_code(*sc->lengths, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + if (sc->complete_values) { + code = ngx_stream_lua_script_add_code(*sc->values, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + return NGX_OK; +} + + +static void * +ngx_stream_lua_script_add_code(ngx_array_t *codes, size_t size) +{ + return ngx_array_push_n(codes, size); +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_script.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_script.h new file mode 100644 index 000000000..a43da06fe --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_script.h @@ -0,0 +1,96 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_script.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SCRIPT_H_INCLUDED_ +#define _NGX_STREAM_LUA_SCRIPT_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +typedef struct { + ngx_log_t *log; + ngx_pool_t *pool; + ngx_str_t *source; + + ngx_array_t **lengths; + ngx_array_t **values; + + ngx_uint_t variables; + + unsigned complete_lengths:1; + unsigned complete_values:1; +} ngx_stream_lua_script_compile_t; + + +typedef struct { + ngx_str_t value; + void *lengths; + void *values; +} ngx_stream_lua_complex_value_t; + + +typedef struct { + ngx_log_t *log; + ngx_pool_t *pool; + ngx_str_t *value; + + ngx_stream_lua_complex_value_t *complex_value; +} ngx_stream_lua_compile_complex_value_t; + + +typedef struct { + u_char *ip; + u_char *pos; + + ngx_str_t buf; + + int *captures; + ngx_uint_t ncaptures; + u_char *captures_data; + + unsigned skip:1; + + ngx_log_t *log; +} ngx_stream_lua_script_engine_t; + + +typedef void (*ngx_stream_lua_script_code_pt) ( + ngx_stream_lua_script_engine_t *e); +typedef size_t (*ngx_stream_lua_script_len_code_pt) + (ngx_stream_lua_script_engine_t *e); + + +typedef struct { + ngx_stream_lua_script_code_pt code; + uintptr_t len; +} ngx_stream_lua_script_copy_code_t; + + +typedef struct { + ngx_stream_lua_script_code_pt code; + uintptr_t n; +} ngx_stream_lua_script_capture_code_t; + + +ngx_int_t ngx_stream_lua_compile_complex_value( + ngx_stream_lua_compile_complex_value_t *ccv); +ngx_int_t ngx_stream_lua_complex_value(ngx_stream_lua_request_t *r, + ngx_str_t *subj, size_t offset, ngx_int_t count, int *cap, + ngx_stream_lua_complex_value_t *val, luaL_Buffer *luabuf); + + +#endif /* _NGX_STREAM_LUA_SCRIPT_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_semaphore.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_semaphore.c new file mode 100644 index 000000000..ab2271883 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_semaphore.c @@ -0,0 +1,583 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_semaphore.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + * Copyright (C) cuiweixie + * I hereby assign copyright in this code to the lua-nginx-module project, + * to be licensed under the same terms as the rest of the code. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_semaphore.h" +#include "ngx_stream_lua_contentby.h" + + +ngx_int_t ngx_stream_lua_sema_mm_init(ngx_conf_t *cf, + ngx_stream_lua_main_conf_t *lmcf); +void ngx_stream_lua_sema_mm_cleanup(void *data); +static ngx_stream_lua_sema_t *ngx_stream_lua_alloc_sema(void); +static void ngx_stream_lua_free_sema(ngx_stream_lua_sema_t *sem); +static ngx_int_t ngx_stream_lua_sema_resume(ngx_stream_lua_request_t *r); +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_wait(ngx_stream_lua_request_t *r, + ngx_stream_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen); +static void ngx_stream_lua_sema_cleanup(void *data); +static void ngx_stream_lua_sema_handler(ngx_event_t *ev); +static void ngx_stream_lua_sema_timeout_handler(ngx_event_t *ev); +void ngx_stream_lua_ffi_sema_gc(ngx_stream_lua_sema_t *sem); + + +enum { + SEMAPHORE_WAIT_SUCC = 0, + SEMAPHORE_WAIT_TIMEOUT = 1 +}; + + +ngx_int_t +ngx_stream_lua_sema_mm_init(ngx_conf_t *cf, ngx_stream_lua_main_conf_t *lmcf) +{ + ngx_stream_lua_sema_mm_t *mm; + + mm = ngx_palloc(cf->pool, sizeof(ngx_stream_lua_sema_mm_t)); + if (mm == NULL) { + return NGX_ERROR; + } + + lmcf->sema_mm = mm; + mm->lmcf = lmcf; + + ngx_queue_init(&mm->free_queue); + mm->cur_epoch = 0; + mm->total = 0; + mm->used = 0; + + /* it's better to be 4096, but it needs some space for + * ngx_stream_lua_sema_mm_block_t, one is enough, so it is 4095 + */ + mm->num_per_block = 4095; + + return NGX_OK; +} + + +static ngx_stream_lua_sema_t * +ngx_stream_lua_alloc_sema(void) +{ + ngx_uint_t i, n; + ngx_queue_t *q; + ngx_stream_lua_sema_t *sem, *iter; + ngx_stream_lua_sema_mm_t *mm; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_sema_mm_block_t *block; + + ngx_stream_lua_assert(ngx_cycle && ngx_cycle->conf_ctx); + + lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, + ngx_stream_lua_module); + + ngx_stream_lua_assert(lmcf != NULL); + + mm = lmcf->sema_mm; + + if (!ngx_queue_empty(&mm->free_queue)) { + q = ngx_queue_head(&mm->free_queue); + ngx_queue_remove(q); + + sem = ngx_queue_data(q, ngx_stream_lua_sema_t, chain); + + sem->block->used++; + + ngx_memzero(&sem->sem_event, sizeof(ngx_event_t)); + + sem->sem_event.handler = ngx_stream_lua_sema_handler; + sem->sem_event.data = sem; + sem->sem_event.log = ngx_cycle->log; + + mm->used++; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "from head of free queue, alloc semaphore: %p", sem); + + return sem; + } + + /* free_queue is empty */ + + n = sizeof(ngx_stream_lua_sema_mm_block_t) + + mm->num_per_block * sizeof(ngx_stream_lua_sema_t); + + dd("block size: %d, item size: %d", + (int) sizeof(ngx_stream_lua_sema_mm_block_t), + (int) sizeof(ngx_stream_lua_sema_t)); + + block = ngx_alloc(n, ngx_cycle->log); + if (block == NULL) { + return NULL; + } + + mm->cur_epoch++; + mm->total += mm->num_per_block; + mm->used++; + + block->mm = mm; + block->epoch = mm->cur_epoch; + + sem = (ngx_stream_lua_sema_t *) (block + 1); + sem->block = block; + sem->block->used = 1; + + ngx_memzero(&sem->sem_event, sizeof(ngx_event_t)); + + sem->sem_event.handler = ngx_stream_lua_sema_handler; + sem->sem_event.data = sem; + sem->sem_event.log = ngx_cycle->log; + + for (iter = sem + 1, i = 1; i < mm->num_per_block; i++, iter++) { + iter->block = block; + ngx_queue_insert_tail(&mm->free_queue, &iter->chain); + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "new block, alloc semaphore: %p block: %p", sem, block); + + return sem; +} + + +void +ngx_stream_lua_sema_mm_cleanup(void *data) +{ + ngx_uint_t i; + ngx_queue_t *q; + ngx_stream_lua_sema_t *sem, *iter; + ngx_stream_lua_sema_mm_t *mm; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_sema_mm_block_t *block; + + lmcf = (ngx_stream_lua_main_conf_t *) data; + mm = lmcf->sema_mm; + + while (!ngx_queue_empty(&mm->free_queue)) { + q = ngx_queue_head(&mm->free_queue); + + sem = ngx_queue_data(q, ngx_stream_lua_sema_t, chain); + block = sem->block; + + ngx_stream_lua_assert(block != NULL); + + if (block->used == 0) { + iter = (ngx_stream_lua_sema_t *) (block + 1); + + for (i = 0; i < block->mm->num_per_block; i++, iter++) { + ngx_queue_remove(&iter->chain); + } + + dd("free sema block: %p at final", block); + + ngx_free(block); + + } else { + /* just return directly when some thing goes wrong */ + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "lua sema mm: freeing a block %p that is still " + " used by someone", block); + + return; + } + } + + dd("lua sema mm cleanup done"); +} + + +static void +ngx_stream_lua_free_sema(ngx_stream_lua_sema_t *sem) +{ + ngx_stream_lua_sema_t *iter; + ngx_uint_t i, mid_epoch; + ngx_stream_lua_sema_mm_block_t *block; + ngx_stream_lua_sema_mm_t *mm; + + block = sem->block; + block->used--; + + mm = block->mm; + mm->used--; + + mid_epoch = mm->cur_epoch - ((mm->total / mm->num_per_block) >> 1); + + if (block->epoch < mid_epoch) { + ngx_queue_insert_tail(&mm->free_queue, &sem->chain); + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "add to free queue tail semaphore: %p epoch: %d" + "mid_epoch: %d cur_epoch: %d", sem, (int) block->epoch, + (int) mid_epoch, (int) mm->cur_epoch); + + } else { + ngx_queue_insert_head(&mm->free_queue, &sem->chain); + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "add to free queue head semaphore: %p epoch: %d" + "mid_epoch: %d cur_epoch: %d", sem, (int) block->epoch, + (int) mid_epoch, (int) mm->cur_epoch); + } + + dd("used: %d", (int) block->used); + + if (block->used == 0 + && mm->used <= (mm->total >> 1) + && block->epoch < mid_epoch) + { + /* load <= 50% and it's on the older side */ + iter = (ngx_stream_lua_sema_t *) (block + 1); + + for (i = 0; i < mm->num_per_block; i++, iter++) { + ngx_queue_remove(&iter->chain); + } + + mm->total -= mm->num_per_block; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "free semaphore block: %p", block); + + ngx_free(block); + } +} + + +static ngx_int_t +ngx_stream_lua_sema_resume(ngx_stream_lua_request_t *r) +{ + lua_State *vm; + ngx_connection_t *c; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_stream_lua_wev_handler; + + c = r->connection; + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + if (ctx->cur_co_ctx->sem_resume_status == SEMAPHORE_WAIT_SUCC) { + lua_pushboolean(ctx->cur_co_ctx->co, 1); + lua_pushnil(ctx->cur_co_ctx->co); + + } else { + lua_pushboolean(ctx->cur_co_ctx->co, 0); + lua_pushliteral(ctx->cur_co_ctx->co, "timeout"); + } + + rc = ngx_stream_lua_run_thread(vm, r, ctx, 2); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + /* rc == NGX_ERROR || rc >= NGX_OK */ + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +int +ngx_stream_lua_ffi_sema_new(ngx_stream_lua_sema_t **psem, + int n, char **errmsg) +{ + ngx_stream_lua_sema_t *sem; + + sem = ngx_stream_lua_alloc_sema(); + if (sem == NULL) { + *errmsg = "no memory"; + return NGX_ERROR; + } + + ngx_queue_init(&sem->wait_queue); + + sem->resource_count = n; + sem->wait_count = 0; + *psem = sem; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua semaphore new: %p, resources: %d", + sem, sem->resource_count); + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_sema_post(ngx_stream_lua_sema_t *sem, int n) +{ + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua semaphore post: %p, n: %d, resources: %d", + sem, n, sem->resource_count); + + sem->resource_count += n; + + if (!ngx_queue_empty(&sem->wait_queue)) { + /* we need the extra paranthese around the first argument of + * ngx_post_event() just to work around macro issues in nginx + * cores older than nginx 1.7.12 (exclusive). + */ + ngx_post_event((&sem->sem_event), &ngx_posted_events); + } + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_sema_wait(ngx_stream_lua_request_t *r, + ngx_stream_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *wait_co_ctx; + ngx_int_t rc; + + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua semaphore wait: %p, timeout: %d, " + "resources: %d, event posted: %d", + sem, wait_ms, sem->resource_count, +#if defined(nginx_version) && nginx_version >= 1007005 + (int) sem->sem_event.posted +#else + sem->sem_event.prev ? 1 : 0 +#endif + ); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err; + return NGX_ERROR; + } + + rc = ngx_stream_lua_ffi_check_context(ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE, + err, errlen); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + /* we keep the order, will first resume the thread waiting for the + * longest time in ngx_stream_lua_sema_handler + */ + + if (ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) { + sem->resource_count--; + return NGX_OK; + } + + if (wait_ms == 0) { + return NGX_DECLINED; + } + + sem->wait_count++; + wait_co_ctx = ctx->cur_co_ctx; + + wait_co_ctx->sleep.handler = ngx_stream_lua_sema_timeout_handler; + wait_co_ctx->sleep.data = ctx->cur_co_ctx; + wait_co_ctx->sleep.log = r->connection->log; + + ngx_add_timer(&wait_co_ctx->sleep, (ngx_msec_t) wait_ms); + + dd("ngx_stream_lua_ffi_sema_wait add timer coctx:%p wait: %d(ms)", + wait_co_ctx, wait_ms); + + ngx_queue_insert_tail(&sem->wait_queue, &wait_co_ctx->sem_wait_queue); + + wait_co_ctx->data = sem; + wait_co_ctx->cleanup = ngx_stream_lua_sema_cleanup; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua semaphore wait yielding"); + + return NGX_AGAIN; +} + + +int +ngx_stream_lua_ffi_sema_count(ngx_stream_lua_sema_t *sem) +{ + return sem->resource_count - sem->wait_count; +} + + +static void +ngx_stream_lua_sema_cleanup(void *data) +{ + ngx_stream_lua_co_ctx_t *coctx = data; + ngx_queue_t *q; + ngx_stream_lua_sema_t *sem; + + sem = coctx->data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua semaphore cleanup"); + + if (coctx->sleep.timer_set) { + ngx_del_timer(&coctx->sleep); + } + + q = &coctx->sem_wait_queue; + + ngx_queue_remove(q); + sem->wait_count--; + coctx->cleanup = NULL; +} + + +static void +ngx_stream_lua_sema_handler(ngx_event_t *ev) +{ + ngx_stream_lua_sema_t *sem; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *wait_co_ctx; + ngx_queue_t *q; + + sem = ev->data; + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "semaphore handler: wait queue: %sempty, resource count: %d", + ngx_queue_empty(&sem->wait_queue) ? "" : "not ", + sem->resource_count); + + while (!ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) { + + q = ngx_queue_head(&sem->wait_queue); + ngx_queue_remove(q); + + sem->wait_count--; + + wait_co_ctx = ngx_queue_data(q, ngx_stream_lua_co_ctx_t, sem_wait_queue); + wait_co_ctx->cleanup = NULL; + + if (wait_co_ctx->sleep.timer_set) { + ngx_del_timer(&wait_co_ctx->sleep); + } + + r = ngx_stream_lua_get_req(wait_co_ctx->co); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + ngx_stream_lua_assert(ctx != NULL); + + sem->resource_count--; + + ctx->cur_co_ctx = wait_co_ctx; + + wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_SUCC; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_sema_resume(r); + + } else { + ctx->resume_handler = ngx_stream_lua_sema_resume; + ngx_stream_lua_core_run_phases(r); + } + + } +} + + +static void +ngx_stream_lua_sema_timeout_handler(ngx_event_t *ev) +{ + ngx_stream_lua_co_ctx_t *wait_co_ctx; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_sema_t *sem; + + wait_co_ctx = ev->data; + wait_co_ctx->cleanup = NULL; + + dd("ngx_stream_lua_sema_timeout_handler timeout coctx:%p", wait_co_ctx); + + sem = wait_co_ctx->data; + + ngx_queue_remove(&wait_co_ctx->sem_wait_queue); + sem->wait_count--; + + r = ngx_stream_lua_get_req(wait_co_ctx->co); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + ngx_stream_lua_assert(ctx != NULL); + + ctx->cur_co_ctx = wait_co_ctx; + + wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_TIMEOUT; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_sema_resume(r); + + } else { + ctx->resume_handler = ngx_stream_lua_sema_resume; + ngx_stream_lua_core_run_phases(r); + } + +} + + +void +ngx_stream_lua_ffi_sema_gc(ngx_stream_lua_sema_t *sem) +{ + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "in lua gc, semaphore %p", sem); + + if (sem == NULL) { + return; + } + + if (!ngx_terminate + && !ngx_quit + && !ngx_queue_empty(&sem->wait_queue)) + { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "in lua semaphore gc wait queue is" + " not empty while the semaphore %p is being " + "destroyed", sem); + } + + if (sem->sem_event.posted) { + ngx_delete_posted_event(&sem->sem_event); + } + + ngx_stream_lua_free_sema(sem); +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_semaphore.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_semaphore.h new file mode 100644 index 000000000..a4a2e5476 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_semaphore.h @@ -0,0 +1,59 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_semaphore.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + * Copyright (C) cuiweixie + * I hereby assign copyright in this code to the lua-nginx-module project, + * to be licensed under the same terms as the rest of the code. + */ + + +#ifndef _NGX_STREAM_LUA_SEMAPHORE_H_INCLUDED_ +#define _NGX_STREAM_LUA_SEMAPHORE_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +typedef struct ngx_stream_lua_sema_mm_block_s { + ngx_uint_t used; + ngx_stream_lua_sema_mm_t *mm; + ngx_uint_t epoch; +} ngx_stream_lua_sema_mm_block_t; + + +struct ngx_stream_lua_sema_mm_s { + ngx_queue_t free_queue; + ngx_uint_t total; + ngx_uint_t used; + ngx_uint_t num_per_block; + ngx_uint_t cur_epoch; + ngx_stream_lua_main_conf_t *lmcf; +}; + + +typedef struct ngx_stream_lua_sema_s { + ngx_queue_t wait_queue; + ngx_queue_t chain; + ngx_event_t sem_event; + ngx_stream_lua_sema_mm_block_t *block; + int resource_count; + unsigned wait_count; +} ngx_stream_lua_sema_t; + + +void ngx_stream_lua_sema_mm_cleanup(void *data); +ngx_int_t ngx_stream_lua_sema_mm_init(ngx_conf_t *cf, + ngx_stream_lua_main_conf_t *lmcf); + + +#endif /* _NGX_STREAM_LUA_SEMAPHORE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_shdict.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_shdict.c new file mode 100644 index 000000000..ba10a3b20 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_shdict.c @@ -0,0 +1,2064 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_shdict.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_shdict.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_api.h" + + +static int ngx_stream_lua_shdict_expire(ngx_stream_lua_shdict_ctx_t *ctx, + ngx_uint_t n); +static ngx_int_t ngx_stream_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, + ngx_uint_t hash, u_char *kdata, size_t klen, + ngx_stream_lua_shdict_node_t **sdp); +static int ngx_stream_lua_shdict_flush_expired(lua_State *L); +static int ngx_stream_lua_shdict_get_keys(lua_State *L); +static int ngx_stream_lua_shdict_lpush(lua_State *L); +static int ngx_stream_lua_shdict_rpush(lua_State *L); +static int ngx_stream_lua_shdict_push_helper(lua_State *L, int flags); +static int ngx_stream_lua_shdict_lpop(lua_State *L); +static int ngx_stream_lua_shdict_rpop(lua_State *L); +static int ngx_stream_lua_shdict_pop_helper(lua_State *L, int flags); +static int ngx_stream_lua_shdict_llen(lua_State *L); + + +static ngx_inline ngx_shm_zone_t *ngx_stream_lua_shdict_get_zone(lua_State *L, + int index); + + +#define NGX_STREAM_LUA_SHDICT_ADD 0x0001 +#define NGX_STREAM_LUA_SHDICT_REPLACE 0x0002 +#define NGX_STREAM_LUA_SHDICT_SAFE_STORE 0x0004 + + +#define NGX_STREAM_LUA_SHDICT_LEFT 0x0001 +#define NGX_STREAM_LUA_SHDICT_RIGHT 0x0002 + + +enum { + SHDICT_USERDATA_INDEX = 1, +}; + + +enum { + SHDICT_TNIL = 0, /* same as LUA_TNIL */ + SHDICT_TBOOLEAN = 1, /* same as LUA_TBOOLEAN */ + SHDICT_TNUMBER = 3, /* same as LUA_TNUMBER */ + SHDICT_TSTRING = 4, /* same as LUA_TSTRING */ + SHDICT_TLIST = 5, +}; + + +static ngx_inline ngx_queue_t * +ngx_stream_lua_shdict_get_list_head(ngx_stream_lua_shdict_node_t *sd, + size_t len) +{ + return (ngx_queue_t *) ngx_align_ptr(((u_char *) &sd->data + len), + NGX_ALIGNMENT); +} + + +ngx_int_t +ngx_stream_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_stream_lua_shdict_ctx_t *octx = data; + ngx_stream_lua_shdict_ctx_t *ctx; + + size_t len; + + dd("init zone"); + + ctx = shm_zone->data; + + if (octx) { + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; + + return NGX_OK; + } + + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + ctx->sh = ctx->shpool->data; + + return NGX_OK; + } + + ctx->sh = ngx_slab_alloc(ctx->shpool, + sizeof(ngx_stream_lua_shdict_shctx_t)); + if (ctx->sh == NULL) { + return NGX_ERROR; + } + + ctx->shpool->data = ctx->sh; + + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, + ngx_stream_lua_shdict_rbtree_insert_value); + + ngx_queue_init(&ctx->sh->lru_queue); + + len = sizeof(" in lua_shared_dict zone \"\"") + shm_zone->shm.name.len; + + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(ctx->shpool->log_ctx, " in lua_shared_dict zone \"%V\"%Z", + &shm_zone->shm.name); + + ctx->shpool->log_nomem = 0; + + return NGX_OK; +} + + +void +ngx_stream_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + + ngx_stream_lua_shdict_node_t *sdn, *sdnt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + sdn = (ngx_stream_lua_shdict_node_t *) &node->color; + sdnt = (ngx_stream_lua_shdict_node_t *) &temp->color; + + p = ngx_memn2cmp(sdn->data, sdnt->data, sdn->key_len, + sdnt->key_len) < 0 ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_int_t +ngx_stream_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, + u_char *kdata, size_t klen, ngx_stream_lua_shdict_node_t **sdp) +{ + ngx_int_t rc; + ngx_time_t *tp; + uint64_t now; + int64_t ms; + ngx_rbtree_node_t *node, *sentinel; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + ctx = shm_zone->data; + + node = ctx->sh->rbtree.root; + sentinel = ctx->sh->rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + sd = (ngx_stream_lua_shdict_node_t *) &node->color; + + rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len); + + if (rc == 0) { + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + *sdp = sd; + + dd("node expires: %lld", (long long) sd->expires); + + if (sd->expires != 0) { + tp = ngx_timeofday(); + + now = (uint64_t) tp->sec * 1000 + tp->msec; + ms = sd->expires - now; + + dd("time to live: %lld", (long long) ms); + + if (ms < 0) { + dd("node already expired"); + return NGX_DONE; + } + } + + return NGX_OK; + } + + node = (rc < 0) ? node->left : node->right; + } + + *sdp = NULL; + + return NGX_DECLINED; +} + + +static int +ngx_stream_lua_shdict_expire(ngx_stream_lua_shdict_ctx_t *ctx, ngx_uint_t n) +{ + ngx_time_t *tp; + uint64_t now; + ngx_queue_t *q, *list_queue, *lq; + int64_t ms; + ngx_rbtree_node_t *node; + int freed = 0; + + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_list_node_t *lnode; + + tp = ngx_timeofday(); + + now = (uint64_t) tp->sec * 1000 + tp->msec; + + /* + * n == 1 deletes one or two expired entries + * n == 0 deletes oldest entry by force + * and one or two zero rate entries + */ + + while (n < 3) { + + if (ngx_queue_empty(&ctx->sh->lru_queue)) { + return freed; + } + + q = ngx_queue_last(&ctx->sh->lru_queue); + + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); + + if (n++ != 0) { + + if (sd->expires == 0) { + return freed; + } + + ms = sd->expires - now; + if (ms > 0) { + return freed; + } + } + + if (sd->value_type == SHDICT_TLIST) { + list_queue = ngx_stream_lua_shdict_get_list_head(sd, + sd->key_len); + + for (lq = ngx_queue_head(list_queue); + lq != ngx_queue_sentinel(list_queue); + lq = ngx_queue_next(lq)) + { + lnode = ngx_queue_data(lq, ngx_stream_lua_shdict_list_node_t, + queue); + + ngx_slab_free_locked(ctx->shpool, lnode); + } + } + + ngx_queue_remove(q); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + + freed++; + } + + return freed; +} + + +void +ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, lua_State *L) +{ + ngx_stream_lua_shdict_ctx_t *ctx; + + ngx_uint_t i; + ngx_shm_zone_t **zone; + ngx_shm_zone_t **zone_udata; + + if (lmcf->shdict_zones != NULL) { + lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */); + /* ngx.shared */ + + lua_createtable(L, 0 /* narr */, 22 /* nrec */); /* shared mt */ + + lua_pushcfunction(L, ngx_stream_lua_shdict_lpush); + lua_setfield(L, -2, "lpush"); + + lua_pushcfunction(L, ngx_stream_lua_shdict_rpush); + lua_setfield(L, -2, "rpush"); + + lua_pushcfunction(L, ngx_stream_lua_shdict_lpop); + lua_setfield(L, -2, "lpop"); + + lua_pushcfunction(L, ngx_stream_lua_shdict_rpop); + lua_setfield(L, -2, "rpop"); + + lua_pushcfunction(L, ngx_stream_lua_shdict_llen); + lua_setfield(L, -2, "llen"); + + lua_pushcfunction(L, ngx_stream_lua_shdict_flush_expired); + lua_setfield(L, -2, "flush_expired"); + + lua_pushcfunction(L, ngx_stream_lua_shdict_get_keys); + lua_setfield(L, -2, "get_keys"); + + lua_pushvalue(L, -1); /* shared mt mt */ + lua_setfield(L, -2, "__index"); /* shared mt */ + + zone = lmcf->shdict_zones->elts; + + for (i = 0; i < lmcf->shdict_zones->nelts; i++) { + ctx = zone[i]->data; + + lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); + /* shared mt key */ + + lua_createtable(L, 1 /* narr */, 0 /* nrec */); + /* table of zone[i] */ + zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *)); + /* shared mt key ud */ + *zone_udata = zone[i]; + lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */ + lua_pushvalue(L, -3); /* shared mt key ud mt */ + lua_setmetatable(L, -2); /* shared mt key ud */ + lua_rawset(L, -4); /* shared mt */ + } + + lua_pop(L, 1); /* shared */ + + } else { + lua_newtable(L); /* ngx.shared */ + } + + lua_setfield(L, -2, "shared"); +} + + +static ngx_inline ngx_shm_zone_t * +ngx_stream_lua_shdict_get_zone(lua_State *L, int index) +{ + ngx_shm_zone_t *zone; + ngx_shm_zone_t **zone_udata; + + lua_rawgeti(L, index, SHDICT_USERDATA_INDEX); + zone_udata = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (zone_udata == NULL) { + return NULL; + } + + zone = *zone_udata; + return zone; +} + + +static int +ngx_stream_lua_shdict_flush_expired(lua_State *L) +{ + ngx_queue_t *q, *prev, *list_queue, *lq; + ngx_shm_zone_t *zone; + ngx_time_t *tp; + int freed = 0; + int attempts = 0; + ngx_rbtree_node_t *node; + uint64_t now; + int n; + + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_list_node_t *lnode; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + zone = ngx_stream_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + if (n == 2) { + attempts = luaL_checkint(L, 2); + } + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + if (ngx_queue_empty(&ctx->sh->lru_queue)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnumber(L, 0); + return 1; + } + + tp = ngx_timeofday(); + + now = (uint64_t) tp->sec * 1000 + tp->msec; + + q = ngx_queue_last(&ctx->sh->lru_queue); + + while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { + prev = ngx_queue_prev(q); + + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); + + if (sd->expires != 0 && sd->expires <= now) { + + if (sd->value_type == SHDICT_TLIST) { + list_queue = ngx_stream_lua_shdict_get_list_head(sd, + sd->key_len); + + for (lq = ngx_queue_head(list_queue); + lq != ngx_queue_sentinel(list_queue); + lq = ngx_queue_next(lq)) + { + lnode = ngx_queue_data(lq, + ngx_stream_lua_shdict_list_node_t, + queue); + + ngx_slab_free_locked(ctx->shpool, lnode); + } + } + + ngx_queue_remove(q); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); + freed++; + + if (attempts && freed == attempts) { + break; + } + } + + q = prev; + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnumber(L, freed); + return 1; +} + + +/* + * This trades CPU for memory. This is potentially slow. O(2n) + */ + +static int +ngx_stream_lua_shdict_get_keys(lua_State *L) +{ + ngx_queue_t *q, *prev; + ngx_shm_zone_t *zone; + ngx_time_t *tp; + int total = 0; + int attempts = 1024; + uint64_t now; + int n; + + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_ctx_t *ctx; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 argument(s), " + "but saw %d", n); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + zone = ngx_stream_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + if (n == 2) { + attempts = luaL_checkint(L, 2); + } + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + if (ngx_queue_empty(&ctx->sh->lru_queue)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_createtable(L, 0, 0); + return 1; + } + + tp = ngx_timeofday(); + + now = (uint64_t) tp->sec * 1000 + tp->msec; + + /* first run through: get total number of elements we need to allocate */ + + q = ngx_queue_last(&ctx->sh->lru_queue); + + while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { + prev = ngx_queue_prev(q); + + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); + + if (sd->expires == 0 || sd->expires > now) { + total++; + if (attempts && total == attempts) { + break; + } + } + + q = prev; + } + + lua_createtable(L, total, 0); + + /* second run through: add keys to table */ + + total = 0; + q = ngx_queue_last(&ctx->sh->lru_queue); + + while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { + prev = ngx_queue_prev(q); + + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); + + if (sd->expires == 0 || sd->expires > now) { + lua_pushlstring(L, (char *) sd->data, sd->key_len); + lua_rawseti(L, -2, ++total); + if (attempts && total == attempts) { + break; + } + } + + q = prev; + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + /* table is at top of stack */ + return 1; +} + + + + +static int +ngx_stream_lua_shdict_lpush(lua_State *L) +{ + return ngx_stream_lua_shdict_push_helper(L, NGX_STREAM_LUA_SHDICT_LEFT); +} + + +static int +ngx_stream_lua_shdict_rpush(lua_State *L) +{ + return ngx_stream_lua_shdict_push_helper(L, NGX_STREAM_LUA_SHDICT_RIGHT); +} + + +static int +ngx_stream_lua_shdict_push_helper(lua_State *L, int flags) +{ + int n; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_str_t value; + int value_type; + double num; + ngx_rbtree_node_t *node; + ngx_shm_zone_t *zone; + ngx_queue_t *queue, *q; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_list_node_t *lnode; + + n = lua_gettop(L); + + if (n != 3) { + return luaL_error(L, "expecting 3 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_stream_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + value_type = lua_type(L, 3); + + switch (value_type) { + + case SHDICT_TSTRING: + value.data = (u_char *) lua_tolstring(L, 3, &value.len); + break; + + case SHDICT_TNUMBER: + value.len = sizeof(double); + num = lua_tonumber(L, 3); + value.data = (u_char *) # + break; + + default: + lua_pushnil(L); + lua_pushliteral(L, "bad value type"); + return 2; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + /* exists but expired */ + + if (rc == NGX_DONE) { + + if (sd->value_type != SHDICT_TLIST) { + /* TODO: reuse when length matched */ + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict push: found old entry and value " + "type not matched, remove it first"); + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + + dd("go to init_list"); + goto init_list; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict push: found old entry and value " + "type matched, reusing it"); + + sd->expires = 0; + sd->value_len = 0; + /* free list nodes */ + + queue = ngx_stream_lua_shdict_get_list_head(sd, key.len); + + for (q = ngx_queue_head(queue); + q != ngx_queue_sentinel(queue); + q = ngx_queue_next(q)) + { + /* TODO: reuse matched size list node */ + lnode = ngx_queue_data(q, ngx_stream_lua_shdict_list_node_t, queue); + ngx_slab_free_locked(ctx->shpool, lnode); + } + + ngx_queue_init(queue); + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + dd("go to push_node"); + goto push_node; + } + + /* exists and not expired */ + + if (rc == NGX_OK) { + + if (sd->value_type != SHDICT_TLIST) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a list"); + return 2; + } + + queue = ngx_stream_lua_shdict_get_list_head(sd, key.len); + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + dd("go to push_node"); + goto push_node; + } + + /* rc == NGX_DECLINED, not found */ + +init_list: + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict list: creating a new entry"); + + /* NOTICE: we assume the begin point aligned in slab, be careful */ + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_stream_lua_shdict_node_t, data) + + key.len + + sizeof(ngx_queue_t); + + dd("length before aligned: %d", n); + + n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT); + + dd("length after aligned: %d", n); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + return 2; + } + + sd = (ngx_stream_lua_shdict_node_t *) &node->color; + + queue = ngx_stream_lua_shdict_get_list_head(sd, key.len); + + node->key = hash; + sd->key_len = (u_short) key.len; + + sd->expires = 0; + + sd->value_len = 0; + + dd("setting value type to %d", (int) SHDICT_TLIST); + + sd->value_type = (uint8_t) SHDICT_TLIST; + + ngx_memcpy(sd->data, key.data, key.len); + + ngx_queue_init(queue); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + +push_node: + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict list: creating a new list node"); + + n = offsetof(ngx_stream_lua_shdict_list_node_t, data) + + value.len; + + dd("list node length: %d", n); + + lnode = ngx_slab_alloc_locked(ctx->shpool, n); + + if (lnode == NULL) { + + if (sd->value_len == 0) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict list: no memory for create" + " list node and list empty, remove it"); + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + return 2; + } + + dd("setting list length to %d", sd->value_len + 1); + + sd->value_len = sd->value_len + 1; + + dd("setting list node value length to %d", (int) value.len); + + lnode->value_len = (uint32_t) value.len; + + dd("setting list node value type to %d", value_type); + + lnode->value_type = (uint8_t) value_type; + + ngx_memcpy(lnode->data, value.data, value.len); + + if (flags == NGX_STREAM_LUA_SHDICT_LEFT) { + ngx_queue_insert_head(queue, &lnode->queue); + + } else { + ngx_queue_insert_tail(queue, &lnode->queue); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnumber(L, sd->value_len); + return 1; +} + + +static int +ngx_stream_lua_shdict_lpop(lua_State *L) +{ + return ngx_stream_lua_shdict_pop_helper(L, NGX_STREAM_LUA_SHDICT_LEFT); +} + + +static int +ngx_stream_lua_shdict_rpop(lua_State *L) +{ + return ngx_stream_lua_shdict_pop_helper(L, NGX_STREAM_LUA_SHDICT_RIGHT); +} + + +static int +ngx_stream_lua_shdict_pop_helper(lua_State *L, int flags) +{ + int n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_str_t value; + int value_type; + double num; + ngx_rbtree_node_t *node; + ngx_shm_zone_t *zone; + ngx_queue_t *queue; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_list_node_t *lnode; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_stream_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + name = ctx->name; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TLIST) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a list"); + return 2; + } + + if (sd->value_len <= 0) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua list length found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) sd->value_len); + } + + queue = ngx_stream_lua_shdict_get_list_head(sd, key.len); + + if (flags == NGX_STREAM_LUA_SHDICT_LEFT) { + queue = ngx_queue_head(queue); + + } else { + queue = ngx_queue_last(queue); + } + + lnode = ngx_queue_data(queue, ngx_stream_lua_shdict_list_node_t, queue); + + value_type = lnode->value_type; + + dd("data: %p", lnode->data); + dd("value len: %d", (int) sd->value_len); + + value.data = lnode->data; + value.len = (size_t) lnode->value_len; + + switch (value_type) { + + case SHDICT_TSTRING: + + lua_pushlstring(L, (char *) value.data, value.len); + break; + + case SHDICT_TNUMBER: + + if (value.len != sizeof(double)) { + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua list node number value size found " + "for key %s in shared_dict %s: %lu", key.data, + name.data, (unsigned long) value.len); + } + + ngx_memcpy(&num, value.data, sizeof(double)); + + lua_pushnumber(L, num); + break; + + default: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad list node value type found for key %s in " + "shared_dict %s: %d", key.data, name.data, + value_type); + } + + ngx_queue_remove(queue); + + ngx_slab_free_locked(ctx->shpool, lnode); + + if (sd->value_len == 1) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict list: empty node after pop, " + "remove it"); + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + + } else { + sd->value_len = sd->value_len - 1; + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 1; +} + + +static int +ngx_stream_lua_shdict_llen(lua_State *L) +{ + int n; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_shm_zone_t *zone; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_stream_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_OK) { + + if (sd->value_type != SHDICT_TLIST) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a list"); + return 2; + } + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnumber(L, (lua_Number) sd->value_len); + return 1; + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnumber(L, 0); + return 1; +} + + +ngx_shm_zone_t * +ngx_stream_lua_find_zone(u_char *name_data, size_t name_len) +{ + ngx_str_t *name; + ngx_uint_t i; + ngx_shm_zone_t *zone; + volatile ngx_list_part_t *part; + + ngx_stream_lua_shm_zone_ctx_t *ctx; + + part = &ngx_cycle->shared_memory.part; + zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + zone = part->elts; + i = 0; + } + + name = &zone[i].shm.name; + + dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len); + dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len); + + if (name->len == name_len + && ngx_strncmp(name->data, name_data, name_len) == 0) + { + ctx = (ngx_stream_lua_shm_zone_ctx_t *) zone[i].data; + return &ctx->zone; + } + } + + return NULL; +} + + +ngx_shm_zone_t * +ngx_stream_lua_ffi_shdict_udata_to_zone(void *zone_udata) +{ + if (zone_udata == NULL) { + return NULL; + } + + return *(ngx_shm_zone_t **) zone_udata; +} + + +int +ngx_stream_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, + size_t key_len, int value_type, u_char *str_value_buf, + size_t str_value_len, double num_value, long exptime, int user_flags, + char **errmsg, int *forcible) +{ + int i, n; + u_char c, *p; + uint32_t hash; + ngx_int_t rc; + ngx_time_t *tp; + ngx_queue_t *queue, *q; + ngx_rbtree_node_t *node; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + dd("exptime: %ld", exptime); + + ctx = zone->data; + + *forcible = 0; + + hash = ngx_crc32_short(key, key_len); + + switch (value_type) { + + case SHDICT_TSTRING: + /* do nothing */ + break; + + case SHDICT_TNUMBER: + dd("num value: %lf", num_value); + str_value_buf = (u_char *) &num_value; + str_value_len = sizeof(double); + break; + + case SHDICT_TBOOLEAN: + c = num_value ? 1 : 0; + str_value_buf = &c; + str_value_len = sizeof(u_char); + break; + + case LUA_TNIL: + if (op & (NGX_STREAM_LUA_SHDICT_ADD|NGX_STREAM_LUA_SHDICT_REPLACE)) { + *errmsg = "attempt to add or replace nil values"; + return NGX_ERROR; + } + + str_value_buf = NULL; + str_value_len = 0; + break; + + default: + *errmsg = "unsupported value type"; + return NGX_ERROR; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd); + + dd("lookup returns %d", (int) rc); + + if (op & NGX_STREAM_LUA_SHDICT_REPLACE) { + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *errmsg = "not found"; + return NGX_DECLINED; + } + + /* rc == NGX_OK */ + + goto replace; + } + + if (op & NGX_STREAM_LUA_SHDICT_ADD) { + + if (rc == NGX_OK) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *errmsg = "exists"; + return NGX_DECLINED; + } + + if (rc == NGX_DONE) { + /* exists but expired */ + + dd("go to replace"); + goto replace; + } + + /* rc == NGX_DECLINED */ + + dd("go to insert"); + goto insert; + } + + if (rc == NGX_OK || rc == NGX_DONE) { + + if (value_type == LUA_TNIL) { + goto remove; + } + +replace: + + if (str_value_buf + && str_value_len == (size_t) sd->value_len + && sd->value_type != SHDICT_TLIST) + { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict set: found old entry and value " + "size matched, reusing it"); + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + sd->key_len = (u_short) key_len; + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) exptime; + + } else { + sd->expires = 0; + } + + sd->user_flags = user_flags; + + sd->value_len = (uint32_t) str_value_len; + + dd("setting value type to %d", value_type); + + sd->value_type = (uint8_t) value_type; + + p = ngx_copy(sd->data, key, key_len); + ngx_memcpy(p, str_value_buf, str_value_len); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict set: found old entry but value size " + "NOT matched, removing it first"); + +remove: + + if (sd->value_type == SHDICT_TLIST) { + queue = ngx_stream_lua_shdict_get_list_head(sd, key_len); + + for (q = ngx_queue_head(queue); + q != ngx_queue_sentinel(queue); + q = ngx_queue_next(q)) + { + p = (u_char *) ngx_queue_data(q, + ngx_stream_lua_shdict_list_node_t, + queue); + + ngx_slab_free_locked(ctx->shpool, p); + } + } + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + + } + +insert: + + /* rc == NGX_DECLINED or value size unmatch */ + + if (str_value_buf == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict set: creating a new entry"); + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_stream_lua_shdict_node_t, data) + + key_len + + str_value_len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + + if (node == NULL) { + + if (op & NGX_STREAM_LUA_SHDICT_SAFE_STORE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *errmsg = "no memory"; + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict set: overriding non-expired items " + "due to memory shortage for entry \"%*s\"", key_len, + key); + + for (i = 0; i < 30; i++) { + if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) { + break; + } + + *forcible = 1; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node != NULL) { + goto allocated; + } + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *errmsg = "no memory"; + return NGX_ERROR; + } + +allocated: + + sd = (ngx_stream_lua_shdict_node_t *) &node->color; + + node->key = hash; + sd->key_len = (u_short) key_len; + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) exptime; + + } else { + sd->expires = 0; + } + + sd->user_flags = user_flags; + sd->value_len = (uint32_t) str_value_len; + dd("setting value type to %d", value_type); + sd->value_type = (uint8_t) value_type; + + p = ngx_copy(sd->data, key, key_len); + ngx_memcpy(p, str_value_buf, str_value_len); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, + size_t key_len, int *value_type, u_char **str_value_buf, + size_t *str_value_len, double *num_value, int *user_flags, + int get_stale, int *is_stale, char **err) +{ + ngx_str_t name; + uint32_t hash; + ngx_int_t rc; + ngx_str_t value; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + *err = NULL; + + ctx = zone->data; + name = ctx->name; + + hash = ngx_crc32_short(key, key_len); + +#if (NGX_DEBUG) + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "fetching key \"%*s\" in shared dict \"%V\"", key_len, + key, &name); +#endif /* NGX_DEBUG */ + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + if (!get_stale) { + ngx_stream_lua_shdict_expire(ctx, 1); + } +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd); + + dd("shdict lookup returns %d", (int) rc); + + if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *value_type = LUA_TNIL; + return NGX_OK; + } + + /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */ + + *value_type = sd->value_type; + + dd("data: %p", sd->data); + dd("key len: %d", (int) sd->key_len); + + value.data = sd->data + sd->key_len; + value.len = (size_t) sd->value_len; + + if (*str_value_len < (size_t) value.len) { + if (*value_type == SHDICT_TBOOLEAN) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + + if (*value_type == SHDICT_TSTRING) { + *str_value_buf = malloc(value.len); + if (*str_value_buf == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + } + } + + switch (*value_type) { + + case SHDICT_TSTRING: + *str_value_len = value.len; + ngx_memcpy(*str_value_buf, value.data, value.len); + break; + + case SHDICT_TNUMBER: + + if (value.len != sizeof(double)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "bad lua number value size found for key %*s " + "in shared_dict %V: %z", key_len, key, + &name, value.len); + return NGX_ERROR; + } + + *str_value_len = value.len; + ngx_memcpy(num_value, value.data, sizeof(double)); + break; + + case SHDICT_TBOOLEAN: + + if (value.len != sizeof(u_char)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "bad lua boolean value size found for key %*s " + "in shared_dict %V: %z", key_len, key, &name, + value.len); + return NGX_ERROR; + } + + ngx_memcpy(*str_value_buf, value.data, value.len); + break; + + case SHDICT_TLIST: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *err = "value is a list"; + return NGX_ERROR; + + default: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "bad value type found for key %*s in " + "shared_dict %V: %d", key_len, key, &name, + *value_type); + return NGX_ERROR; + } + + *user_flags = sd->user_flags; + dd("user flags: %d", *user_flags); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (get_stale) { + + /* always return value, flags, stale */ + + *is_stale = (rc == NGX_DONE); + return NGX_OK; + } + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, + size_t key_len, double *value, char **err, int has_init, double init, + long init_ttl, int *forcible) +{ + int i, n; + uint32_t hash; + ngx_int_t rc; + ngx_time_t *tp = NULL; + double num; + ngx_rbtree_node_t *node; + u_char *p; + ngx_queue_t *queue, *q; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + if (init_ttl > 0) { + tp = ngx_timeofday(); + } + + ctx = zone->data; + + *forcible = 0; + + hash = ngx_crc32_short(key, key_len); + + dd("looking up key %.*s in shared dict %.*s", (int) key_len, key, + (int) ctx->name.len, ctx->name.data); + + ngx_shmtx_lock(&ctx->shpool->mutex); +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + if (!has_init) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *err = "not found"; + return NGX_ERROR; + } + + /* add value */ + num = *value + init; + + if (rc == NGX_DONE) { + + /* found an expired item */ + + if ((size_t) sd->value_len == sizeof(double) + && sd->value_type != SHDICT_TLIST) + { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict incr: found old entry and " + "value size matched, reusing it"); + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + dd("go to setvalue"); + goto setvalue; + } + + dd("go to remove"); + goto remove; + } + + dd("go to insert"); + goto insert; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *err = "not a number"; + return NGX_ERROR; + } + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + dd("setting value type to %d", (int) sd->value_type); + + p = sd->data + key_len; + + ngx_memcpy(&num, p, sizeof(double)); + num += *value; + + ngx_memcpy(p, (double *) &num, sizeof(double)); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *value = num; + return NGX_OK; + +remove: + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict incr: found old entry but value size " + "NOT matched, removing it first"); + + if (sd->value_type == SHDICT_TLIST) { + queue = ngx_stream_lua_shdict_get_list_head(sd, key_len); + + for (q = ngx_queue_head(queue); + q != ngx_queue_sentinel(queue); + q = ngx_queue_next(q)) + { + p = (u_char *) ngx_queue_data(q, ngx_stream_lua_shdict_list_node_t, + queue); + + ngx_slab_free_locked(ctx->shpool, p); + } + } + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + +insert: + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict incr: creating a new entry"); + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_stream_lua_shdict_node_t, data) + + key_len + + sizeof(double); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + + if (node == NULL) { + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict incr: overriding non-expired items " + "due to memory shortage for entry \"%*s\"", key_len, + key); + + for (i = 0; i < 30; i++) { + if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) { + break; + } + + *forcible = 1; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node != NULL) { + goto allocated; + } + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *err = "no memory"; + return NGX_ERROR; + } + +allocated: + + sd = (ngx_stream_lua_shdict_node_t *) &node->color; + + node->key = hash; + + sd->key_len = (u_short) key_len; + + sd->value_len = (uint32_t) sizeof(double); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + +setvalue: + + sd->user_flags = 0; + + if (init_ttl > 0) { + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) init_ttl; + + } else { + sd->expires = 0; + } + + dd("setting value type to %d", LUA_TNUMBER); + + sd->value_type = (uint8_t) LUA_TNUMBER; + + p = ngx_copy(sd->data, key, key_len); + ngx_memcpy(p, (double *) &num, sizeof(double)); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *value = num; + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_shdict_flush_all(ngx_shm_zone_t *zone) +{ + ngx_queue_t *q; + + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_ctx_t *ctx; + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + for (q = ngx_queue_head(&ctx->sh->lru_queue); + q != ngx_queue_sentinel(&ctx->sh->lru_queue); + q = ngx_queue_next(q)) + { + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); + sd->expires = 1; + } + + ngx_stream_lua_shdict_expire(ctx, 0); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_shdict_peek(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, + u_char *kdata, size_t klen, ngx_stream_lua_shdict_node_t **sdp) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + ctx = shm_zone->data; + + node = ctx->sh->rbtree.root; + sentinel = ctx->sh->rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + sd = (ngx_stream_lua_shdict_node_t *) &node->color; + + rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len); + + if (rc == 0) { + *sdp = sd; + + return NGX_OK; + } + + node = (rc < 0) ? node->left : node->right; + } + + *sdp = NULL; + + return NGX_DECLINED; +} + + +long +ngx_stream_lua_ffi_shdict_get_ttl(ngx_shm_zone_t *zone, u_char *key, + size_t key_len) +{ + uint32_t hash; + uint64_t now; + uint64_t expires; + ngx_int_t rc; + ngx_time_t *tp; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + ctx = zone->data; + hash = ngx_crc32_short(key, key_len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + rc = ngx_stream_lua_shdict_peek(zone, hash, key, key_len, &sd); + + if (rc == NGX_DECLINED) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_DECLINED; + } + + /* rc == NGX_OK */ + + expires = sd->expires; + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (expires == 0) { + return 0; + } + + tp = ngx_timeofday(); + now = (uint64_t) tp->sec * 1000 + tp->msec; + + return expires - now; +} + + +int +ngx_stream_lua_ffi_shdict_set_expire(ngx_shm_zone_t *zone, u_char *key, + size_t key_len, long exptime) +{ + uint32_t hash; + ngx_int_t rc; + ngx_time_t *tp = NULL; + + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + if (exptime > 0) { + tp = ngx_timeofday(); + } + + ctx = zone->data; + hash = ngx_crc32_short(key, key_len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + rc = ngx_stream_lua_shdict_peek(zone, hash, key, key_len, &sd); + + if (rc == NGX_DECLINED) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_DECLINED; + } + + /* rc == NGX_OK */ + + if (exptime > 0) { + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) exptime; + + } else { + sd->expires = 0; + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; +} + + +size_t +ngx_stream_lua_ffi_shdict_capacity(ngx_shm_zone_t *zone) +{ + return zone->shm.size; +} + + +#if defined(nginx_version) && nginx_version >= 1011007 +size_t +ngx_stream_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone) +{ + size_t bytes; + + ngx_stream_lua_shdict_ctx_t *ctx; + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + bytes = ctx->shpool->pfree * ngx_pagesize; + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return bytes; +} +#endif + + +#if (NGX_DARWIN) +int +ngx_stream_lua_ffi_shdict_get_macos(ngx_stream_lua_shdict_get_params_t *p) +{ + return ngx_stream_lua_ffi_shdict_get(p->zone, p->key, p->key_len, + p->value_type, p->str_value_buf, + p->str_value_len, p->num_value, + p->user_flags, p->get_stale, + p->is_stale, p->errmsg); +} + + +int +ngx_stream_lua_ffi_shdict_store_macos(ngx_stream_lua_shdict_store_params_t *p) +{ + return ngx_stream_lua_ffi_shdict_store(p->zone, p->op, p->key, p->key_len, + p->value_type, p->str_value_buf, + p->str_value_len, p->num_value, + p->exptime, p->user_flags, + p->errmsg, p->forcible); +} + + +int +ngx_stream_lua_ffi_shdict_incr_macos(ngx_stream_lua_shdict_incr_params_t *p) +{ + return ngx_stream_lua_ffi_shdict_incr(p->zone, p->key, p->key_len, + p->num_value, p->errmsg, + p->has_init, p->init, p->init_ttl, + p->forcible); +} +#endif + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_shdict.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_shdict.h new file mode 100644 index 000000000..3554764db --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_shdict.h @@ -0,0 +1,121 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_shdict.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SHDICT_H_INCLUDED_ +#define _NGX_STREAM_LUA_SHDICT_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +typedef struct { + u_char color; + uint8_t value_type; + u_short key_len; + uint32_t value_len; + uint64_t expires; + ngx_queue_t queue; + uint32_t user_flags; + u_char data[1]; +} ngx_stream_lua_shdict_node_t; + + +typedef struct { + ngx_queue_t queue; + uint32_t value_len; + uint8_t value_type; + u_char data[1]; +} ngx_stream_lua_shdict_list_node_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t lru_queue; +} ngx_stream_lua_shdict_shctx_t; + + +typedef struct { + ngx_stream_lua_shdict_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_str_t name; + ngx_stream_lua_main_conf_t *main_conf; + ngx_log_t *log; +} ngx_stream_lua_shdict_ctx_t; + + +typedef struct { + ngx_log_t *log; + ngx_stream_lua_main_conf_t *lmcf; + ngx_cycle_t *cycle; + ngx_shm_zone_t zone; +} ngx_stream_lua_shm_zone_ctx_t; + + +#if (NGX_DARWIN) +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; +#endif + + +ngx_int_t ngx_stream_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data); +void ngx_stream_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +void ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, + lua_State *L); + + +#endif /* _NGX_STREAM_LUA_SHDICT_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_sleep.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_sleep.c new file mode 100644 index 000000000..fb590c5bd --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_sleep.c @@ -0,0 +1,223 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_sleep.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_sleep.h" +#include "ngx_stream_lua_contentby.h" + + +static int ngx_stream_lua_ngx_sleep(lua_State *L); +static void ngx_stream_lua_sleep_handler(ngx_event_t *ev); +static void ngx_stream_lua_sleep_cleanup(void *data); +static ngx_int_t ngx_stream_lua_sleep_resume(ngx_stream_lua_request_t *r); + + +static int +ngx_stream_lua_ngx_sleep(lua_State *L) +{ + int n; + ngx_int_t delay; /* in msec */ + ngx_stream_lua_request_t *r; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000); + + if (delay < 0) { + return luaL_error(L, "invalid sleep duration \"%d\"", delay); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + coctx = ctx->cur_co_ctx; + if (coctx == NULL) { + return luaL_error(L, "no co ctx found"); + } + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_sleep_cleanup; + coctx->data = r; + + coctx->sleep.handler = ngx_stream_lua_sleep_handler; + coctx->sleep.data = coctx; + coctx->sleep.log = r->connection->log; + + if (delay == 0) { +#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH + dd("posting 0 sec sleep event to head of delayed queue"); + + coctx->sleep.delayed = 1; + ngx_post_event(&coctx->sleep, &ngx_posted_delayed_events); +#else + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "ngx.sleep(0)" + " called without delayed events patch, this will" + " hurt performance"); + ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay); +#endif + + } else { + dd("adding timer with delay %lu ms, r:%p", (unsigned long) delay, r); + + ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay); + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua ready to sleep for %d ms", delay); + + return lua_yield(L, 0); +} + + +void +ngx_stream_lua_sleep_handler(ngx_event_t *ev) +{ +#if (NGX_DEBUG) + ngx_connection_t *c; +#endif + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + coctx = ev->data; + + r = coctx->data; + +#if (NGX_DEBUG) + + c = r->connection; + +#endif + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx == NULL) { + return; + } + + + coctx->cleanup = NULL; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua sleep timer expired"); + + ctx->cur_co_ctx = coctx; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_sleep_resume(r); + + } else { + ctx->resume_handler = ngx_stream_lua_sleep_resume; + ngx_stream_lua_core_run_phases(r); + } + +} + + +void +ngx_stream_lua_inject_sleep_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_stream_lua_ngx_sleep); + lua_setfield(L, -2, "sleep"); +} + + +static void +ngx_stream_lua_sleep_cleanup(void *data) +{ + ngx_stream_lua_co_ctx_t *coctx = data; + + if (coctx->sleep.timer_set) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "lua clean up the timer for pending ngx.sleep"); + + ngx_del_timer(&coctx->sleep); + } + +#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH + if (coctx->sleep.posted) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "lua clean up the posted event for pending ngx.sleep"); + + ngx_delete_posted_event(&coctx->sleep); + } +#endif +} + + +static ngx_int_t +ngx_stream_lua_sleep_resume(ngx_stream_lua_request_t *r) +{ + lua_State *vm; + ngx_connection_t *c; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_stream_lua_wev_handler; + + c = r->connection; + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_stream_lua_run_thread(vm, r, ctx, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_sleep.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_sleep.h new file mode 100644 index 000000000..cea10bd11 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_sleep.h @@ -0,0 +1,28 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_sleep.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SLEEP_H_INCLUDED_ +#define _NGX_STREAM_LUA_SLEEP_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_sleep_api(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_SLEEP_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_tcp.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_tcp.c new file mode 100644 index 000000000..9bc38c31e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_tcp.c @@ -0,0 +1,6234 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_socket_tcp.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_socket_tcp.h" +#include "ngx_stream_lua_input_filters.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_uthread.h" +#include "ngx_stream_lua_output.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_probe.h" + + +static int ngx_stream_lua_socket_tcp(lua_State *L); +static int ngx_stream_lua_socket_tcp_connect(lua_State *L); +#if (NGX_STREAM_SSL) +static int ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L); +#endif +static int ngx_stream_lua_socket_tcp_receive(lua_State *L); +static int ngx_stream_lua_socket_tcp_receiveany(lua_State *L); +static int ngx_stream_lua_socket_tcp_send(lua_State *L); +static int ngx_stream_lua_socket_tcp_close(lua_State *L); +static int ngx_stream_lua_socket_tcp_setoption(lua_State *L); +static int ngx_stream_lua_socket_tcp_settimeout(lua_State *L); +static int ngx_stream_lua_socket_tcp_settimeouts(lua_State *L); +static void ngx_stream_lua_socket_tcp_handler(ngx_event_t *ev); +static ngx_int_t ngx_stream_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, + void *data); +static void ngx_stream_lua_socket_init_peer_connection_addr_text( + ngx_peer_connection_t *pc); +static void ngx_stream_lua_socket_read_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_send_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_connected_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_tcp_cleanup(void *data); +static void ngx_stream_lua_socket_tcp_finalize(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_tcp_finalize_read_part( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_tcp_finalize_write_part( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + int do_shutdown); + +static ngx_int_t ngx_stream_lua_socket_send(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_stream_lua_socket_test_connect(ngx_stream_lua_request_t *r, + ngx_connection_t *c); +static void ngx_stream_lua_socket_handle_conn_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); +static void ngx_stream_lua_socket_handle_read_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); +static void ngx_stream_lua_socket_handle_write_error( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + ngx_uint_t ft_type); +static void ngx_stream_lua_socket_handle_conn_success( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_handle_read_success( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_handle_write_success( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +static int ngx_stream_lua_socket_tcp_send_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static int ngx_stream_lua_socket_tcp_conn_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static void ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static int ngx_stream_lua_socket_tcp_receive_helper(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L); +static ngx_int_t ngx_stream_lua_socket_tcp_read(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static int ngx_stream_lua_socket_tcp_receive_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static ngx_int_t ngx_stream_lua_socket_read_line(void *data, ssize_t bytes); +static void ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx); +static int ngx_stream_lua_socket_resolve_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static int ngx_stream_lua_socket_conn_error_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static int ngx_stream_lua_socket_read_error_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static int ngx_stream_lua_socket_write_error_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static ngx_int_t ngx_stream_lua_socket_read_all(void *data, ssize_t bytes); +static ngx_int_t ngx_stream_lua_socket_read_until(void *data, ssize_t bytes); +static ngx_int_t ngx_stream_lua_socket_read_chunk(void *data, ssize_t bytes); +static ngx_int_t ngx_stream_lua_socket_read_any(void *data, ssize_t bytes); +static int ngx_stream_lua_socket_tcp_receiveuntil(lua_State *L); +static int ngx_stream_lua_socket_receiveuntil_iterator(lua_State *L); +static ngx_int_t ngx_stream_lua_socket_compile_pattern(u_char *data, size_t len, + ngx_stream_lua_socket_compiled_pattern_t *cp, ngx_log_t *log); +static int ngx_stream_lua_socket_cleanup_compiled_pattern(lua_State *L); +static void ngx_stream_lua_req_socket_rev_handler(ngx_stream_lua_request_t *r); +static int ngx_stream_lua_socket_tcp_getreusedtimes(lua_State *L); +static int ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L); +static void ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L, + ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, + ngx_int_t backlog, ngx_stream_lua_socket_pool_t **spool); +static ngx_int_t ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u); +static void ngx_stream_lua_socket_keepalive_dummy_handler(ngx_event_t *ev); +static int ngx_stream_lua_socket_tcp_connect_helper(lua_State *L, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, + in_port_t port, unsigned resuming); +static void ngx_stream_lua_socket_tcp_conn_op_timeout_handler( + ngx_event_t *ev); +static int ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static void ngx_stream_lua_socket_tcp_resume_conn_op( + ngx_stream_lua_socket_pool_t *spool); +static void ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup(void *data); +static void ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev); +static ngx_int_t ngx_stream_lua_socket_keepalive_close_handler(ngx_event_t *ev); +static void ngx_stream_lua_socket_keepalive_rev_handler(ngx_event_t *ev); +static int ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static int ngx_stream_lua_socket_tcp_upstream_destroy(lua_State *L); +static int ngx_stream_lua_socket_downstream_destroy(lua_State *L); +static ngx_int_t ngx_stream_lua_socket_push_input_data( + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L); +static ngx_int_t ngx_stream_lua_socket_add_pending_data( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + u_char *pos, size_t len, u_char *pat, int prefix, int old_state); +static ngx_int_t ngx_stream_lua_socket_add_input_buffer( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_stream_lua_socket_insert_buffer( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + u_char *pat, size_t prefix); +static ngx_int_t ngx_stream_lua_socket_tcp_conn_op_resume( + ngx_stream_lua_request_t *r); +static ngx_int_t ngx_stream_lua_socket_tcp_conn_resume( + ngx_stream_lua_request_t *r); +static ngx_int_t ngx_stream_lua_socket_tcp_read_resume( + ngx_stream_lua_request_t *r); +static ngx_int_t ngx_stream_lua_socket_tcp_write_resume( + ngx_stream_lua_request_t *r); +static ngx_int_t ngx_stream_lua_socket_tcp_resume_helper( + ngx_stream_lua_request_t *r, int socket_op); +static void ngx_stream_lua_tcp_queue_conn_op_cleanup(void *data); +static void ngx_stream_lua_tcp_resolve_cleanup(void *data); +static void ngx_stream_lua_coctx_cleanup(void *data); +static void ngx_stream_lua_socket_free_pool(ngx_log_t *log, + ngx_stream_lua_socket_pool_t *spool); +static int ngx_stream_lua_socket_shutdown_pool(lua_State *L); +static void ngx_stream_lua_socket_shutdown_pool_helper( + ngx_stream_lua_socket_pool_t *spool); +static int ngx_stream_lua_socket_prepare_error_retvals( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L, ngx_uint_t ft_type); +#if (NGX_STREAM_SSL) +static int ngx_stream_lua_ssl_handshake_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static void ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c); +static int ngx_stream_lua_ssl_free_session(lua_State *L); +#endif +static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c); + +static int ngx_stream_lua_socket_tcp_peek(lua_State *L); +static ngx_int_t ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r); +static int ngx_stream_lua_socket_tcp_shutdown(lua_State *L); + + +enum { + SOCKET_CTX_INDEX = 1, + SOCKET_KEY_INDEX = 3, + SOCKET_CONNECT_TIMEOUT_INDEX = 2, + SOCKET_SEND_TIMEOUT_INDEX = 4, + SOCKET_READ_TIMEOUT_INDEX = 5, +}; + + +enum { + SOCKET_OP_CONNECT, + SOCKET_OP_READ, + SOCKET_OP_WRITE, + SOCKET_OP_RESUME_CONN +}; + + +#define ngx_stream_lua_socket_check_busy_connecting(r, u, L) \ + if ((u)->conn_waiting) { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy connecting"); \ + return 2; \ + } + + +#define ngx_stream_lua_socket_check_busy_reading(r, u, L) \ + if ((u)->read_waiting) { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy reading"); \ + return 2; \ + } + + +#define ngx_stream_lua_socket_check_busy_writing(r, u, L) \ + if ((u)->write_waiting) { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy writing"); \ + return 2; \ + } \ + if ((u)->raw_downstream \ + && ((r)->connection->buffered)) \ + { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy writing"); \ + return 2; \ + } + + + +static char ngx_stream_lua_raw_req_socket_metatable_key; +static char ngx_stream_lua_tcp_socket_metatable_key; +static char ngx_stream_lua_upstream_udata_metatable_key; +static char ngx_stream_lua_downstream_udata_metatable_key; +static char ngx_stream_lua_pool_udata_metatable_key; +static char ngx_stream_lua_pattern_udata_metatable_key; +#if (NGX_STREAM_SSL) +static char ngx_stream_lua_ssl_session_metatable_key; +#endif + + +void +ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) +{ + ngx_int_t rc; + + lua_createtable(L, 0, 4 /* nrec */); /* ngx.socket */ + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp); + lua_pushvalue(L, -1); + lua_setfield(L, -3, "tcp"); + lua_setfield(L, -2, "stream"); + + { + const char buf[] = "local sock = ngx.socket.tcp()" + " local ok, err = sock:connect(...)" + " if ok then return sock else return nil, err end"; + + rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=ngx.socket.connect"); + } + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_CRIT, log, 0, + "failed to load Lua code for ngx.socket.connect(): %i", + rc); + + } else { + lua_setfield(L, -2, "connect"); + } + + lua_setfield(L, -2, "socket"); + + + /* {{{raw req socket object metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + raw_req_socket_metatable_key)); + lua_createtable(L, 0 /* narr */, 9 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveany); + lua_setfield(L, -2, "receiveany"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveuntil); + lua_setfield(L, -2, "receiveuntil"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_send); + lua_setfield(L, -2, "send"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeouts); + lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_peek); + lua_setfield(L, -2, "peek"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown); + lua_setfield(L, -2, "shutdown"); + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{tcp object metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + tcp_socket_metatable_key)); + lua_createtable(L, 0 /* narr */, 14 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_connect); + lua_setfield(L, -2, "connect"); + +#if (NGX_STREAM_SSL) + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_sslhandshake); + lua_setfield(L, -2, "sslhandshake"); + +#endif + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveuntil); + lua_setfield(L, -2, "receiveuntil"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveany); + lua_setfield(L, -2, "receiveany"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_send); + lua_setfield(L, -2, "send"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_close); + lua_setfield(L, -2, "close"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setoption); + lua_setfield(L, -2, "setoption"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeouts); + lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_getreusedtimes); + lua_setfield(L, -2, "getreusedtimes"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setkeepalive); + lua_setfield(L, -2, "setkeepalive"); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown); + lua_setfield(L, -2, "shutdown"); + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{upstream userdata metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + upstream_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_upstream_destroy); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{downstream userdata metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + downstream_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_stream_lua_socket_downstream_destroy); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{socket pool userdata metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + pool_udata_metatable_key)); + lua_createtable(L, 0, 1); /* metatable */ + lua_pushcfunction(L, ngx_stream_lua_socket_shutdown_pool); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{socket compiled pattern userdata metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + pattern_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_stream_lua_socket_cleanup_compiled_pattern); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + +#if (NGX_STREAM_SSL) + + /* {{{ssl session userdata metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + ssl_session_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_stream_lua_ssl_free_session); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + +#endif +} + + +static int +ngx_stream_lua_socket_tcp(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + if (lua_gettop(L) != 0) { + return luaL_error(L, "expecting zero arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + lua_createtable(L, 5 /* narr */, 1 /* nrec */); + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + tcp_socket_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + dd("top: %d", lua_gettop(L)); + + return 1; +} + + +static void +ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L, + ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog, + ngx_stream_lua_socket_pool_t **spool) +{ + u_char *p; + size_t size, key_len; + ngx_int_t i; + + ngx_stream_lua_socket_pool_t *sp; + ngx_stream_lua_socket_pool_item_t *items; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connection pool size: " + "%i, backlog: %i", + pool_size, backlog); + + key_len = ngx_align(key.len + 1, sizeof(void *)); + + size = sizeof(ngx_stream_lua_socket_pool_t) - 1 + key_len + + sizeof(ngx_stream_lua_socket_pool_item_t) * pool_size; + + /* before calling this function, the Lua stack is: + * -1 key + * -2 pools + */ + sp = lua_newuserdata(L, size); + if (sp == NULL) { + luaL_error(L, "no memory"); + return; + } + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + pool_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket keepalive create " + "connection pool for key \"%V\"", &key); + + /* a new socket pool with metatable is push to the stack, so now we have: + * -1 sp + * -2 key + * -3 pools + * + * it is time to set pools[key] to sp. + */ + lua_rawset(L, -3); + + /* clean up the stack for consistency's sake */ + lua_pop(L, 1); + + sp->backlog = backlog; + sp->size = pool_size; + sp->connections = 0; + sp->lua_vm = ngx_stream_lua_get_lua_vm(r, NULL); + + ngx_queue_init(&sp->cache_connect_op); + ngx_queue_init(&sp->wait_connect_op); + ngx_queue_init(&sp->cache); + ngx_queue_init(&sp->free); + + p = ngx_copy(sp->key, key.data, key.len); + *p++ = '\0'; + + items = (ngx_stream_lua_socket_pool_item_t *) (sp->key + key_len); + + dd("items: %p", items); + + ngx_stream_lua_assert((void *) items + == ngx_align_ptr(items, sizeof(void *))); + + for (i = 0; i < pool_size; i++) { + ngx_queue_insert_head(&sp->free, &items[i].queue); + items[i].socket_pool = sp; + } + + *spool = sp; +} + + +static int +ngx_stream_lua_socket_tcp_connect_helper(lua_State *L, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, + in_port_t port, unsigned resuming) +{ + int n; + int host_size; + int saved_top; + ngx_int_t rc; + ngx_str_t host; + ngx_str_t *conn_op_host; + ngx_url_t url; + ngx_queue_t *q; + ngx_resolver_ctx_t *rctx, temp; + + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_pool_t *spool; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + ngx_stream_core_srv_conf_t *clcf; + + spool = u->socket_pool; + if (spool != NULL) { + rc = ngx_stream_lua_get_keepalive_peer(r, u); + + if (rc == NGX_OK) { + lua_pushinteger(L, 1); + return 1; + } + + /* rc == NGX_DECLINED */ + + spool->connections++; + + /* check if backlog is enabled and + * don't queue resuming connection operation */ + if (spool->backlog >= 0 && !resuming) { + + dd("lua tcp socket %s connections %ld", + spool->key, spool->connections); + + if (spool->connections > spool->size + spool->backlog) { + spool->connections--; + lua_pushnil(L); + lua_pushliteral(L, "too many waiting connect operations"); + return 2; + } + + if (spool->connections > spool->size) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->peer.log, 0, + "stream lua tcp socket queue connect " + "operation for connection pool \"%s\", " + "connections: %i", + spool->key, spool->connections); + + host_size = sizeof(u_char) * + (ngx_max(host_len, NGX_INET_ADDRSTRLEN) + 1); + + if (!ngx_queue_empty(&spool->cache_connect_op)) { + q = ngx_queue_last(&spool->cache_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data( + q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, queue); + + conn_op_host = &conn_op_ctx->host; + if (host_len > conn_op_host->len + && host_len > NGX_INET_ADDRSTRLEN) + { + ngx_free(conn_op_host->data); + conn_op_host->data = ngx_alloc(host_size, + ngx_cycle->log); + if (conn_op_host->data == NULL) { + ngx_free(conn_op_ctx); + goto no_memory_and_not_resuming; + } + } + + } else { + conn_op_ctx = ngx_alloc( + sizeof(ngx_stream_lua_socket_tcp_conn_op_ctx_t), + ngx_cycle->log); + if (conn_op_ctx == NULL) { + goto no_memory_and_not_resuming; + } + + conn_op_host = &conn_op_ctx->host; + conn_op_host->data = ngx_alloc(host_size, ngx_cycle->log); + if (conn_op_host->data == NULL) { + ngx_free(conn_op_ctx); + goto no_memory_and_not_resuming; + } + } + + conn_op_ctx->cleanup = NULL; + + ngx_memcpy(conn_op_host->data, host_ref, host_len); + conn_op_host->data[host_len] = '\0'; + conn_op_host->len = host_len; + + conn_op_ctx->port = port; + + u->write_co_ctx = ctx->cur_co_ctx; + + conn_op_ctx->u = u; + ctx->cur_co_ctx->cleanup = + ngx_stream_lua_tcp_queue_conn_op_cleanup; + ctx->cur_co_ctx->data = conn_op_ctx; + + ngx_memzero(&conn_op_ctx->event, sizeof(ngx_event_t)); + conn_op_ctx->event.handler = + ngx_stream_lua_socket_tcp_conn_op_timeout_handler; + conn_op_ctx->event.data = conn_op_ctx; + conn_op_ctx->event.log = ngx_cycle->log; + + ngx_add_timer(&conn_op_ctx->event, u->connect_timeout); + + ngx_queue_insert_tail(&spool->wait_connect_op, + &conn_op_ctx->queue); + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket queued connect " + "operation for %d(ms), u: %p, ctx: %p", + u->connect_timeout, conn_op_ctx->u, conn_op_ctx); + + return lua_yield(L, 0); + } + } + + } /* end spool != NULL */ + + host.data = ngx_palloc(r->pool, host_len + 1); + if (host.data == NULL) { + return luaL_error(L, "no memory"); + } + + host.len = host_len; + + ngx_memcpy(host.data, host_ref, host_len); + host.data[host_len] = '\0'; + + ngx_memzero(&url, sizeof(ngx_url_t)); + url.url = host; + url.default_port = port; + url.no_resolve = 1; + + coctx = ctx->cur_co_ctx; + + if (ngx_parse_url(r->pool, &url) != NGX_OK) { + lua_pushnil(L); + + if (url.err) { + lua_pushfstring(L, "failed to parse host name \"%s\": %s", + url.url.data, url.err); + + } else { + lua_pushfstring(L, "failed to parse host name \"%s\"", + url.url.data); + } + + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connect timeout: %M", + u->connect_timeout); + + u->resolved = ngx_pcalloc(r->pool, + sizeof(ngx_stream_upstream_resolved_t)); + if (u->resolved == NULL) { + if (resuming) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + goto failed; + } + + goto no_memory_and_not_resuming; + } + + if (url.addrs && url.addrs[0].sockaddr) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket network address given " + "directly"); + + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = host; + u->resolved->port = url.default_port; + } + + if (u->resolved->sockaddr) { + rc = ngx_stream_lua_socket_resolve_retval_handler(r, u, L); + if (rc == NGX_AGAIN && !resuming) { + return lua_yield(L, 0); + } + + if (rc > 1) { + goto failed; + } + + return rc; + } + + clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module); + + temp.name = host; + rctx = ngx_resolve_start(clcf->resolver, &temp); + if (rctx == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushliteral(L, "failed to start the resolver"); + goto failed; + } + + if (rctx == NGX_NO_RESOLVER) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); + goto failed; + } + + rctx->name = host; + rctx->handler = ngx_stream_lua_socket_resolve_handler; + rctx->data = u; + rctx->timeout = clcf->resolver_timeout; + + u->resolved->ctx = rctx; + u->write_co_ctx = ctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_tcp_resolve_cleanup; + coctx->data = u; + + saved_top = lua_gettop(L); + + if (ngx_resolve_name(rctx) != NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket fail to run resolver " + "immediately"); + + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + + coctx->cleanup = NULL; + coctx->data = NULL; + + u->resolved->ctx = NULL; + lua_pushnil(L); + lua_pushfstring(L, "%s could not be resolved", host.data); + goto failed; + } + + if (u->conn_waiting) { + dd("resolved and already connecting"); + + if (resuming) { + return NGX_AGAIN; + } + + return lua_yield(L, 0); + } + + n = lua_gettop(L) - saved_top; + if (n) { + dd("errors occurred during resolving or connecting" + "or already connected"); + + if (n > 1) { + goto failed; + } + + return n; + } + + /* still resolving */ + + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_stream_lua_socket_resolve_retval_handler; + + dd("setting data to %p", u); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + if (resuming) { + return NGX_AGAIN; + } + + return lua_yield(L, 0); + +failed: + + if (spool != NULL) { + spool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + } + + return 2; + +no_memory_and_not_resuming: + + if (spool != NULL) { + spool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + } + + return luaL_error(L, "no memory"); +} + + +static int +ngx_stream_lua_socket_tcp_connect(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + int port; + int n; + u_char *p; + size_t len; + ngx_peer_connection_t *pc; + int connect_timeout, send_timeout, read_timeout; + unsigned custom_pool; + int key_index; + ngx_int_t backlog; + ngx_int_t pool_size; + ngx_str_t key; + const char *msg; + + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_pool_t *spool; + + n = lua_gettop(L); + if (n != 2 && n != 3 && n != 4) { + return luaL_error(L, "ngx.socket connect: expecting 2, 3, or 4 " + "arguments (including the object), but seen %d", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + luaL_checktype(L, 1, LUA_TTABLE); + + p = (u_char *) luaL_checklstring(L, 2, &len); + + backlog = -1; + key_index = 2; + pool_size = 0; + custom_pool = 0; + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (lua_type(L, n) == LUA_TTABLE) { + + /* found the last optional option table */ + + lua_getfield(L, n, "pool_size"); + + if (lua_isnumber(L, -1)) { + pool_size = (ngx_int_t) lua_tointeger(L, -1); + + if (pool_size <= 0) { + msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", + pool_size); + return luaL_argerror(L, n, msg); + } + + } else if (!lua_isnil(L, -1)) { + msg = lua_pushfstring(L, "bad \"pool_size\" option type: %s", + lua_typename(L, lua_type(L, -1))); + return luaL_argerror(L, n, msg); + } + + lua_pop(L, 1); + + lua_getfield(L, n, "backlog"); + + if (lua_isnumber(L, -1)) { + backlog = (ngx_int_t) lua_tointeger(L, -1); + + if (backlog < 0) { + msg = lua_pushfstring(L, "bad \"backlog\" option value: %i", + backlog); + return luaL_argerror(L, n, msg); + } + + /* use default value for pool size if only backlog specified */ + if (pool_size == 0) { + pool_size = llcf->pool_size; + } + } + + lua_pop(L, 1); + + lua_getfield(L, n, "pool"); + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + lua_tostring(L, -1); + /* FALLTHROUGH */ + + case LUA_TSTRING: + custom_pool = 1; + + lua_pushvalue(L, -1); + lua_rawseti(L, 1, SOCKET_KEY_INDEX); + + key_index = n + 1; + + break; + + case LUA_TNIL: + lua_pop(L, 2); + break; + + default: + msg = lua_pushfstring(L, "bad \"pool\" option type: %s", + luaL_typename(L, -1)); + luaL_argerror(L, n, msg); + break; + } + + n--; + } + + /* the fourth argument is not a table */ + if (n == 4) { + lua_pop(L, 1); + n--; + } + + if (n == 3) { + port = luaL_checkinteger(L, 3); + + if (port < 0 || port > 65535) { + lua_pushnil(L); + lua_pushfstring(L, "bad port number: %d", port); + return 2; + } + + if (!custom_pool) { + lua_pushliteral(L, ":"); + lua_insert(L, 3); + lua_concat(L, 3); + } + + dd("socket key: %s", lua_tostring(L, -1)); + + } else { /* n == 2 */ + port = 0; + } + + if (!custom_pool) { + /* the key's index is 2 */ + + lua_pushvalue(L, 2); + lua_rawseti(L, 1, SOCKET_KEY_INDEX); + } + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u) { + if (u->request && u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + ngx_stream_lua_socket_check_busy_writing(r, u, L); + + if (u->body_downstream || u->raw_downstream) { + return luaL_error(L, "attempt to re-connect a request socket"); + } + + if (u->peer.connection) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket reconnect without " + "shutting down"); + + ngx_stream_lua_socket_tcp_finalize(r, u); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua reuse socket upstream ctx"); + + } else { + u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_tcp_upstream_t)); + if (u == NULL) { + return luaL_error(L, "no memory"); + } + +#if 1 + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + upstream_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +#endif + + lua_rawseti(L, 1, SOCKET_CTX_INDEX); + } + + ngx_memzero(u, sizeof(ngx_stream_lua_socket_tcp_upstream_t)); + + u->request = r; /* set the controlling request */ + + u->conf = llcf; + + pc = &u->peer; + + pc->log = r->connection->log; + pc->log_error = NGX_ERROR_ERR; + + dd("lua peer connection log: %p", pc->log); + + lua_rawgeti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); + lua_rawgeti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); + lua_rawgeti(L, 1, SOCKET_READ_TIMEOUT_INDEX); + + read_timeout = (ngx_int_t) lua_tointeger(L, -1); + send_timeout = (ngx_int_t) lua_tointeger(L, -2); + connect_timeout = (ngx_int_t) lua_tointeger(L, -3); + + lua_pop(L, 3); + + if (connect_timeout > 0) { + u->connect_timeout = (ngx_msec_t) connect_timeout; + + } else { + u->connect_timeout = u->conf->connect_timeout; + } + + if (send_timeout > 0) { + u->send_timeout = (ngx_msec_t) send_timeout; + + } else { + u->send_timeout = u->conf->send_timeout; + } + + if (read_timeout > 0) { + u->read_timeout = (ngx_msec_t) read_timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + } + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); /* table */ + lua_pushvalue(L, key_index); /* key */ + + lua_rawget(L, -2); + spool = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (spool != NULL) { + u->socket_pool = spool; + + } else if (pool_size > 0) { + lua_pushvalue(L, key_index); + key.data = (u_char *) lua_tolstring(L, -1, &key.len); + + ngx_stream_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, + backlog, &spool); + u->socket_pool = spool; + } + + return ngx_stream_lua_socket_tcp_connect_helper(L, u, r, ctx, p, + len, port, 0); +} + + +static void +ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_stream_lua_request_t *r; +#if (NGX_DEBUG) + ngx_connection_t *c; +#endif + lua_State *L; + u_char *p; + size_t len; + socklen_t socklen; + struct sockaddr *sockaddr; + ngx_uint_t i; + unsigned waiting; + + ngx_stream_upstream_resolved_t *ur; + ngx_stream_lua_ctx_t *lctx; + ngx_stream_lua_socket_tcp_upstream_t *u; + + u = ctx->data; + r = u->request; + +#if (NGX_DEBUG) + + c = r->connection; + +#endif + + ur = u->resolved; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua tcp socket resolve handler"); + + lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (lctx == NULL) { + return; + } + + lctx->cur_co_ctx = u->write_co_ctx; + + u->write_co_ctx->cleanup = NULL; + + L = lctx->cur_co_ctx->co; + + waiting = u->conn_waiting; + + if (ctx->state) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua tcp socket resolver error: %s " + "(connect waiting: %d)", + ngx_resolver_strerror(ctx->state), (int) waiting); + + lua_pushnil(L); + lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); + lua_pushfstring(L, " could not be resolved (%d: %s)", + (int) ctx->state, + ngx_resolver_strerror(ctx->state)); + lua_concat(L, 2); + + u->write_prepare_retvals = + ngx_stream_lua_socket_conn_error_retval_handler; + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_RESOLVER); + + + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + ngx_stream_lua_assert(ur->naddrs > 0); + + if (ur->naddrs == 1) { + i = 0; + + } else { + i = ngx_random() % ur->naddrs; + } + + dd("selected addr index: %d", (int) i); + + socklen = ur->addrs[i].socklen; + + sockaddr = ngx_palloc(r->pool, socklen); + if (sockaddr == NULL) { + goto nomem; + } + + ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); + + switch (sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); + break; +#endif + default: /* AF_INET */ + ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); + } + + p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + goto nomem; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + ur->sockaddr = sockaddr; + ur->socklen = socklen; + ur->host.data = p; + ur->host.len = len; + ur->naddrs = 1; + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + u->conn_waiting = 0; + u->write_co_ctx = NULL; + + if (waiting) { + lctx->resume_handler = ngx_stream_lua_socket_tcp_conn_resume; + r->write_event_handler(r); + + + } else { + (void) ngx_stream_lua_socket_resolve_retval_handler(r, u, L); + } + + return; + +nomem: + + if (ur->ctx) { + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + } + + u->write_prepare_retvals = ngx_stream_lua_socket_conn_error_retval_handler; + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_NOMEM); + + if (waiting) { + dd("run posted requests"); + + + } else { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + } +} + + +static void +ngx_stream_lua_socket_init_peer_connection_addr_text(ngx_peer_connection_t *pc) +{ + ngx_connection_t *c; + size_t addr_text_max_len; + + c = pc->connection; + + switch (pc->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr_text_max_len = NGX_INET6_ADDRSTRLEN; + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + addr_text_max_len = NGX_UNIX_ADDRSTRLEN; + break; +#endif + + case AF_INET: + addr_text_max_len = NGX_INET_ADDRSTRLEN; + break; + + default: + addr_text_max_len = NGX_SOCKADDR_STRLEN; + break; + } + + c->addr_text.data = ngx_pnalloc(c->pool, addr_text_max_len); + if (c->addr_text.data == NULL) { + ngx_log_error(NGX_LOG_ERR, pc->log, 0, + "init peer connection addr_text failed: no memory"); + return; + } + + c->addr_text.len = ngx_sock_ntop(pc->sockaddr, pc->socklen, + c->addr_text.data, + addr_text_max_len, 0); +} + + +static int +ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_peer_connection_t *pc; + ngx_connection_t *c; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_upstream_resolved_t *ur; + ngx_int_t rc; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket resolve retval handler"); + + if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_RESOLVER) { + return 2; + } + + pc = &u->peer; + + ur = u->resolved; + + if (ur->sockaddr) { + pc->sockaddr = ur->sockaddr; + pc->socklen = ur->socklen; + pc->name = &ur->host; + + } else { + lua_pushnil(L); + lua_pushliteral(L, "resolver not working"); + return 2; + } + + pc->get = ngx_stream_lua_socket_tcp_get_peer; + + rc = ngx_event_connect_peer(pc); + + if (rc == NGX_ERROR) { + u->socket_errno = ngx_socket_errno; + } + + if (u->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + cln->handler = ngx_stream_lua_socket_tcp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connect: %i", rc); + + if (rc == NGX_ERROR) { + return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L); + } + + if (rc == NGX_BUSY) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no live connection"); + return 2; + } + + if (rc == NGX_DECLINED) { + dd("socket errno: %d", (int) ngx_socket_errno); + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + u->socket_errno = ngx_socket_errno; + return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L); + } + + /* rc == NGX_OK || rc == NGX_AGAIN */ + + c = pc->connection; + + c->data = u; + + c->write->handler = ngx_stream_lua_socket_tcp_handler; + c->read->handler = ngx_stream_lua_socket_tcp_handler; + + u->write_event_handler = ngx_stream_lua_socket_connected_handler; + u->read_event_handler = ngx_stream_lua_socket_connected_handler; + + c->sendfile &= r->connection->sendfile; + + if (c->pool == NULL) { + + /* we need separate pool here to be able to cache SSL connections */ + + c->pool = ngx_create_pool(128, r->connection->log); + if (c->pool == NULL) { + return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, + NGX_STREAM_LUA_SOCKET_FT_NOMEM); + } + } + + c->log = r->connection->log; + c->pool->log = c->log; + c->read->log = c->log; + c->write->log = c->log; + + /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ + +#if 0 + u->writer.out = NULL; + u->writer.last = &u->writer.out; +#endif + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + coctx = ctx->cur_co_ctx; + + dd("setting data to %p", u); + + if (rc == NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connected: fd:%d", (int) c->fd); + + /* We should delete the current write/read event + * here because the socket object may not be used immediately + * on the Lua land, thus causing hot spin around level triggered + * event poll and wasting CPU cycles. */ + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + lua_pushnil(L); + lua_pushliteral(L, "failed to handle write event"); + return 2; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + lua_pushnil(L); + lua_pushliteral(L, "failed to handle read event"); + return 2; + } + + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; + + lua_pushinteger(L, 1); + return 1; + } + + /* rc == NGX_AGAIN */ + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + ngx_add_timer(c->write, u->connect_timeout); + + u->write_co_ctx = ctx->cur_co_ctx; + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_stream_lua_socket_tcp_conn_retval_handler; + + dd("setting data to %p", u); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + return NGX_AGAIN; +} + + +static int +ngx_stream_lua_socket_conn_error_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_uint_t ft_type; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket error retval handler"); + + if (u->write_co_ctx) { + u->write_co_ctx->cleanup = NULL; + } + + ngx_stream_lua_socket_tcp_finalize(r, u); + + ft_type = u->ft_type; + u->ft_type = 0; + return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, ft_type); +} + + +#if (NGX_STREAM_SSL) + +static int +ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L) +{ + int n, top; + ngx_int_t rc; + ngx_str_t name = ngx_null_string; + ngx_connection_t *c; + ngx_ssl_session_t **psession; + + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_tcp_upstream_t *u; + + /* Lua function arguments: self [,session] [,host] [,verify] + [,send_status_req] */ + + n = lua_gettop(L); + if (n < 1 || n > 5) { + return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 5 " + "arguments (including the object), but seen %d", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket ssl handshake"); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL + || u->peer.connection == NULL + || u->read_closed + || u->write_closed) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + ngx_stream_lua_socket_check_busy_writing(r, u, L); + + if (u->raw_downstream || u->body_downstream) { + lua_pushnil(L); + lua_pushliteral(L, "not supported for downstream"); + return 2; + } + + c = u->peer.connection; + + u->ssl_session_reuse = 1; + + if (c->ssl && c->ssl->handshaked) { + switch (lua_type(L, 2)) { + case LUA_TUSERDATA: + lua_pushvalue(L, 2); + break; + + case LUA_TBOOLEAN: + if (!lua_toboolean(L, 2)) { + /* avoid generating the ssl session */ + lua_pushboolean(L, 1); + break; + } + /* fall through */ + + default: + ngx_stream_lua_ssl_handshake_retval_handler(r, u, L); + break; + } + + return 1; + } + + if (ngx_ssl_create_connection(u->conf->ssl, c, + NGX_SSL_BUFFER|NGX_SSL_CLIENT) + != NGX_OK) + { + lua_pushnil(L); + lua_pushliteral(L, "failed to create ssl connection"); + return 2; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + coctx = ctx->cur_co_ctx; + + c->sendfile = 0; + + if (n >= 2) { + if (lua_type(L, 2) == LUA_TBOOLEAN) { + u->ssl_session_reuse = lua_toboolean(L, 2); + + } else { + psession = lua_touserdata(L, 2); + + if (psession != NULL && *psession != NULL) { + if (ngx_ssl_set_session(c, *psession) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "lua ssl set session failed"); + return 2; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua ssl set session: %p", *psession); + } + } + + if (n >= 3) { + name.data = (u_char *) lua_tolstring(L, 3, &name.len); + + if (name.data) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua ssl server name: \"%*s\"", name.len, + name.data); + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + + if (SSL_set_tlsext_host_name(c->ssl->connection, + (char *) name.data) + == 0) + { + lua_pushnil(L); + lua_pushliteral(L, "SSL_set_tlsext_host_name failed"); + return 2; + } + +#else + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua socket SNI disabled because the current " + "version of OpenSSL lacks the support"); + +#endif + } + + if (n >= 4) { + u->ssl_verify = lua_toboolean(L, 4); + + if (n >= 5) { + if (lua_toboolean(L, 5)) { +#ifdef NGX_STREAM_LUA_USE_OCSP + SSL_set_tlsext_status_type(c->ssl->connection, + TLSEXT_STATUSTYPE_ocsp); +#else + return luaL_error(L, "no OCSP support"); +#endif + } + } + } + } + } + + dd("found sni name: %.*s %p", (int) name.len, name.data, name.data); + + if (name.len == 0) { + u->ssl_name.len = 0; + + } else { + if (u->ssl_name.data) { + /* buffer already allocated */ + + if (u->ssl_name.len >= name.len) { + /* reuse it */ + ngx_memcpy(u->ssl_name.data, name.data, name.len); + u->ssl_name.len = name.len; + + } else { + ngx_free(u->ssl_name.data); + goto new_ssl_name; + } + + } else { + +new_ssl_name: + + u->ssl_name.data = ngx_alloc(name.len, ngx_cycle->log); + if (u->ssl_name.data == NULL) { + u->ssl_name.len = 0; + + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + ngx_memcpy(u->ssl_name.data, name.data, name.len); + u->ssl_name.len = name.len; + } + } + + u->write_co_ctx = coctx; + +#if 0 +#ifdef NGX_STREAM_LUA_USE_OCSP + SSL_set_tlsext_status_type(c->ssl->connection, TLSEXT_STATUSTYPE_ocsp); +#endif +#endif + + rc = ngx_ssl_handshake(c); + + dd("ngx_ssl_handshake returned %d", (int) rc); + + if (rc == NGX_AGAIN) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + ngx_add_timer(c->read, u->connect_timeout); + + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_stream_lua_ssl_handshake_retval_handler; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + c->ssl->handler = ngx_stream_lua_ssl_handshake_handler; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + return lua_yield(L, 0); + } + + top = lua_gettop(L); + ngx_stream_lua_ssl_handshake_handler(c); + return lua_gettop(L) - top; +} + + +static void +ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c) +{ + const char *err; + int waiting; + lua_State *L; + ngx_int_t rc; + ngx_connection_t *dc; /* downstream connection */ + ngx_stream_lua_request_t *r; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_socket_tcp_upstream_t *u; + + u = c->data; + r = u->request; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + c->write->handler = ngx_stream_lua_socket_tcp_handler; + c->read->handler = ngx_stream_lua_socket_tcp_handler; + + waiting = u->conn_waiting; + + dc = r->connection; + L = u->write_co_ctx->co; + + if (c->read->timedout) { + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + goto failed; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->ssl->handshaked) { + + if (u->ssl_verify) { + rc = SSL_get_verify_result(c->ssl->connection); + + if (rc != X509_V_OK) { + lua_pushnil(L); + err = lua_pushfstring(L, "%d: %s", (int) rc, + X509_verify_cert_error_string(rc)); + + llcf = ngx_stream_lua_get_module_loc_conf(r, + ngx_stream_lua_module); + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, dc->log, 0, "stream lua ssl " + "certificate verify error: (%s)", err); + } + + goto failed; + } + +#if defined(nginx_version) && nginx_version >= 1007000 + + if (u->ssl_name.len + && ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) + { + lua_pushnil(L); + lua_pushliteral(L, "certificate host mismatch"); + + llcf = ngx_stream_lua_get_module_loc_conf(r, + ngx_stream_lua_module); + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, dc->log, 0, "stream lua ssl " + "certificate does not match host \"%V\"", + &u->ssl_name); + } + + goto failed; + } + +#endif + } + + if (waiting) { + ngx_stream_lua_socket_handle_conn_success(r, u); + + } else { + (void) ngx_stream_lua_ssl_handshake_retval_handler(r, u, L); + } + + + return; + } + + lua_pushnil(L); + lua_pushliteral(L, "handshake failed"); + +failed: + + if (waiting) { + u->write_prepare_retvals = + ngx_stream_lua_socket_conn_error_retval_handler; + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_SSL); + + + } else { + (void) ngx_stream_lua_socket_conn_error_retval_handler(r, u, L); + } +} + + +static int +ngx_stream_lua_ssl_handshake_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_connection_t *c; + ngx_ssl_session_t *ssl_session, **ud; + + if (!u->ssl_session_reuse) { + lua_pushboolean(L, 1); + return 1; + } + + ud = lua_newuserdata(L, sizeof(ngx_ssl_session_t *)); + + c = u->peer.connection; + + ssl_session = ngx_ssl_get_session(c); + if (ssl_session == NULL) { + *ud = NULL; + + } else { + *ud = ssl_session; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua ssl save session: %p", ssl_session); + + /* set up the __gc metamethod */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + ssl_session_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + } + + return 1; +} + +#endif /* NGX_STREAM_SSL */ + + +static int +ngx_stream_lua_socket_read_error_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_uint_t ft_type; + + if (u->read_co_ctx) { + u->read_co_ctx->cleanup = NULL; + } + + ft_type = u->ft_type; + u->ft_type = 0; + + if (u->no_close) { + u->no_close = 0; + + } else { + ngx_stream_lua_socket_tcp_finalize_read_part(r, u); + } + + return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, ft_type); +} + + +static int +ngx_stream_lua_socket_write_error_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_uint_t ft_type; + + if (u->write_co_ctx) { + u->write_co_ctx->cleanup = NULL; + } + + ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 0); + + ft_type = u->ft_type; + u->ft_type = 0; + return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, ft_type); +} + + +static int +ngx_stream_lua_socket_prepare_error_retvals(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type) +{ + u_char errstr[NGX_MAX_ERROR_STR]; + u_char *p; + + if (ft_type & (NGX_STREAM_LUA_SOCKET_FT_RESOLVER + | NGX_STREAM_LUA_SOCKET_FT_SSL)) + { + return 2; + } + + lua_pushnil(L); + + if (ft_type & NGX_STREAM_LUA_SOCKET_FT_TIMEOUT) { + lua_pushliteral(L, "timeout"); + + } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_CLOSED) { + lua_pushliteral(L, "closed"); + + } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_BUFTOOSMALL) { + lua_pushliteral(L, "buffer too small"); + + } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_NOMEM) { + lua_pushliteral(L, "no memory"); + + } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT) { + lua_pushliteral(L, "client aborted"); + + } else { + + if (u->socket_errno) { + p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); + /* for compatibility with LuaSocket */ + ngx_strlow(errstr, errstr, p - errstr); + lua_pushlstring(L, (char *) errstr, p - errstr); + + } else { + lua_pushliteral(L, "error"); + } + } + + return 2; +} + + +static int +ngx_stream_lua_socket_tcp_conn_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + if (u->ft_type) { + return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L); + } + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_stream_lua_socket_tcp_peek(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_co_ctx_t *coctx; + int n; + lua_Integer bytes; + size_t size; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_PREREAD); + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments " + "(including the object), but got %d", n); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket calling peek() method"); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to peek data on a closed socket: u:%p", u); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->read_consumed) { + return luaL_error(L, "attempt to peek on a consumed socket"); + } + + c = u->peer.connection; + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_reading(r, u, L); + + if (!lua_isnumber(L, 2)) { + return luaL_error(L, "argument must be a number"); + } + + bytes = lua_tointeger(L, 2); + if (bytes < 0) { + return luaL_argerror(L, 2, "bytes can not be negative"); + } + + if (bytes == 0) { + lua_pushliteral(L, ""); + return 1; + } + + u->length = (size_t) bytes; + + if (c->buffer != NULL) { + size = c->buffer->last - c->buffer->pos; + + if (size >= u->length) { + lua_pushlstring(L, (char *) c->buffer->pos, u->length); + return 1; + } + } + + /* not enough data in the preread buffer */ + + coctx = ctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + dd("setting data to %p, coctx:%p", u, coctx); + + ctx->downstream = u; + ctx->resume_handler = ngx_stream_lua_socket_tcp_peek_resume; + ctx->peek_needs_more_data = 1; + u->read_co_ctx = coctx; + u->read_waiting = 1; + + return lua_yield(L, 0); +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r) +{ + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + size_t size; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket resuming peek"); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + u = ctx->downstream; + c = r->connection; + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + size = c->buffer->last - c->buffer->pos; + + if (size < u->length) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua peek does not have enough data, returning NGX_AGAIN"); + + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + ctx->resume_handler = ngx_stream_lua_wev_handler; + /* read handler might have been changed by ngx_stream_core_preread_phase */ + r->connection->read->handler = ngx_stream_lua_request_handler; + + lua_pushlstring(u->read_co_ctx->co, (char *) c->buffer->pos, u->length); + + u->read_co_ctx->cleanup = NULL; + ctx->cur_co_ctx = u->read_co_ctx; + u->read_co_ctx = NULL; + ctx->peek_needs_more_data = 0; + u->read_waiting = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua tcp operation done, resuming lua thread"); + + rc = ngx_stream_lua_run_thread(vm, r, ctx, 1); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + return rc; +} + + +static int +ngx_stream_lua_socket_tcp_receive_helper(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_int_t rc; + ngx_stream_lua_ctx_t *lctx; + ngx_stream_lua_co_ctx_t *coctx; + + u->input_filter_ctx = u; + + lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (u->bufs_in == NULL) { + u->bufs_in = + ngx_stream_lua_chain_get_free_buf(r->connection->log, + r->pool, + &lctx->free_recv_bufs, + u->conf->buffer_size); + + if (u->bufs_in == NULL) { + return luaL_error(L, "no memory"); + } + + u->buf_in = u->bufs_in; + u->buffer = *u->buf_in->buf; + } + + dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket read timeout: %M", u->read_timeout); + + if (u->raw_downstream || u->body_downstream) { + r->read_event_handler = ngx_stream_lua_req_socket_rev_handler; + } + + u->read_waiting = 0; + u->read_co_ctx = NULL; + + rc = ngx_stream_lua_socket_tcp_read(r, u); + + if (rc == NGX_ERROR) { + dd("read failed: %d", (int) u->ft_type); + rc = ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); + dd("tcp receive retval returned: %d", (int) rc); + return rc; + } + + if (rc == NGX_OK) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket receive done in a single run"); + + return ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); + } + + /* rc == NGX_AGAIN */ + + u->read_event_handler = ngx_stream_lua_socket_read_handler; + + coctx = lctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + if (lctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + u->read_co_ctx = coctx; + u->read_waiting = 1; + u->read_prepare_retvals = ngx_stream_lua_socket_tcp_receive_retval_handler; + + dd("setting data to %p, coctx:%p", u, coctx); + + if (u->raw_downstream || u->body_downstream) { + lctx->downstream = u; + } + + return lua_yield(L, 0); +} + + +static int +ngx_stream_lua_socket_tcp_receiveany(lua_State *L) +{ + int n; + lua_Integer bytes; + ngx_stream_lua_request_t *r; + ngx_stream_lua_loc_conf_t *llcf; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments " + "(including the object), but got %d", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to receive data on a closed " + "socket: u:%p, c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + + if (!lua_isnumber(L, 2)) { + return luaL_argerror(L, 2, "bad max argument"); + } + + bytes = lua_tointeger(L, 2); + if (bytes <= 0) { + return luaL_argerror(L, 2, "bad max argument"); + } + + u->input_filter = ngx_stream_lua_socket_read_any; + u->rest = (size_t) bytes; + u->length = u->rest; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket calling receiveany() " + "method to read at most %uz bytes", u->rest); + + return ngx_stream_lua_socket_tcp_receive_helper(r, u, L); +} + + +static int +ngx_stream_lua_socket_tcp_receive(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_loc_conf_t *llcf; + int n; + ngx_str_t pat; + lua_Integer bytes; + char *p; + int typ; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 arguments " + "(including the object), but got %d", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket calling receive() method"); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream attempt to receive data on a closed " + "socket: u:%p, c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket read timeout: %M", u->read_timeout); + + if (n > 1) { + if (lua_isnumber(L, 2)) { + typ = LUA_TNUMBER; + + } else { + typ = lua_type(L, 2); + } + + switch (typ) { + case LUA_TSTRING: + pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); + if (pat.len != 2 || pat.data[0] != '*') { + p = (char *) lua_pushfstring(L, "bad pattern argument: %s", + (char *) pat.data); + + return luaL_argerror(L, 2, p); + } + + switch (pat.data[1]) { + case 'l': + u->input_filter = ngx_stream_lua_socket_read_line; + break; + + case 'a': + u->input_filter = ngx_stream_lua_socket_read_all; + break; + + default: + return luaL_argerror(L, 2, "bad pattern argument"); + break; + } + + u->length = 0; + u->rest = 0; + + break; + + case LUA_TNUMBER: + bytes = lua_tointeger(L, 2); + if (bytes < 0) { + return luaL_argerror(L, 2, "bad pattern argument"); + } + +#if 1 + if (bytes == 0) { + lua_pushliteral(L, ""); + return 1; + } +#endif + + u->input_filter = ngx_stream_lua_socket_read_chunk; + u->length = (size_t) bytes; + u->rest = u->length; + + break; + + default: + return luaL_argerror(L, 2, "bad pattern argument"); + break; + } + + } else { + u->input_filter = ngx_stream_lua_socket_read_line; + u->length = 0; + u->rest = 0; + } + + return ngx_stream_lua_socket_tcp_receive_helper(r, u, L); +} + + +static ngx_int_t +ngx_stream_lua_socket_read_chunk(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_stream_lua_socket_tcp_upstream_t *u = data; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream lua tcp socket read chunk %z", bytes); + + rc = ngx_stream_lua_read_bytes(&u->buffer, u->buf_in, &u->rest, + bytes, u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_stream_lua_socket_read_all(void *data, ssize_t bytes) +{ + ngx_stream_lua_socket_tcp_upstream_t *u = data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream lua tcp socket read all"); + return ngx_stream_lua_read_all(&u->buffer, u->buf_in, bytes, + u->request->connection->log); +} + + +static ngx_int_t +ngx_stream_lua_socket_read_line(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_stream_lua_socket_tcp_upstream_t *u = data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream lua tcp socket read line"); + + rc = ngx_stream_lua_read_line(&u->buffer, u->buf_in, bytes, + u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_stream_lua_socket_read_any(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_stream_lua_socket_tcp_upstream_t *u = data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream lua tcp socket read any"); + + rc = ngx_stream_lua_read_any(&u->buffer, u->buf_in, &u->rest, bytes, + u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_read(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_buf_t *b; + ngx_event_t *rev; + size_t size; + ssize_t n; + unsigned read; + off_t preread = 0; + + ngx_stream_lua_loc_conf_t *llcf; + + c = u->peer.connection; + rev = c->read; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua tcp socket read data: wait:%d", + (int) u->read_waiting); + + b = &u->buffer; + read = 0; + + for ( ;; ) { + + size = b->last - b->pos; + + if (size || u->eof) { + + rc = u->input_filter(u->input_filter_ctx, size); + + if (rc == NGX_OK) { + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket receive done: " + "wait:%d, eof:%d, ", (int) u->read_waiting, + (int) u->eof); + + +#if 1 + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } +#endif + + + u->read_consumed = 1; + + ngx_stream_lua_socket_handle_read_success(r, u); + return NGX_OK; + } + + if (rc == NGX_ERROR) { + dd("input filter error: ft_type:%d wait:%d", + (int) u->ft_type, (int) u->read_waiting); + + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + /* rc == NGX_AGAIN */ + + + continue; + } + + if (read && !rev->ready) { + rc = NGX_AGAIN; + break; + } + + size = b->end - b->last; + + if (size == 0) { + rc = ngx_stream_lua_socket_add_input_buffer(r, u); + if (rc == NGX_ERROR) { + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_NOMEM); + + return NGX_ERROR; + } + + b = &u->buffer; + size = (size_t) (b->end - b->last); + } + + if (u->raw_downstream) { + if (r->connection->buffer != NULL) { + preread = ngx_buf_size(r->connection->buffer); + } + + if (preread) { + + if ((off_t) size > preread) { + size = (size_t) preread; + } + + ngx_stream_lua_probe_req_socket_consume_preread(r, + r->connection->buffer->pos, + size); + + b->last = ngx_copy(b->last, r->connection->buffer->pos, size); + r->connection->buffer->pos += size; + continue; + } + + } + +#if 1 + if (rev->active && !rev->ready) { + rc = NGX_AGAIN; + break; + } +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket try to recv data %uz", size); + + n = c->recv(c, b->last, size); + + dd("read event ready: %d", (int) c->read->ready); + + read = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket recv returned %d", (int) n); + + if (n == NGX_AGAIN) { + rc = NGX_AGAIN; + dd("socket recv busy"); + break; + } + + if (n == 0) { + + if (u->raw_downstream || u->body_downstream) { + + llcf = ngx_stream_lua_get_module_loc_conf(r, + ngx_stream_lua_module); + + if (llcf->check_client_abort) { + + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT); + return NGX_ERROR; + } + + /* llcf->check_client_abort == 0 */ + + } + + u->eof = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket closed"); + + continue; + } + + if (n == NGX_ERROR) { + u->socket_errno = ngx_socket_errno; + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + b->last += n; + + } + +#if 1 + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } +#endif + + if (rev->active) { + ngx_add_timer(rev, u->read_timeout); + + } else if (rev->timer_set) { + ngx_del_timer(rev); + } + + return rc; +} + + +static int +ngx_stream_lua_socket_tcp_send(lua_State *L) +{ + ngx_int_t rc; + ngx_stream_lua_request_t *r; + u_char *p; + size_t len; + ngx_chain_t *cl; + int type; + int tcp_nodelay; + const char *msg; + ngx_buf_t *b; + ngx_connection_t *c; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_loc_conf_t *llcf; + + ngx_stream_core_srv_conf_t *clcf; + + ngx_stream_lua_co_ctx_t *coctx; + + /* TODO: add support for the optional "i" and "j" arguments */ + + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments (including the object), " + "but got %d", lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + dd("tcp send: u=%p, u->write_closed=%d", u, (unsigned) u->write_closed); + + if (u == NULL || u->peer.connection == NULL || u->write_closed) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to send data on a closed socket: u:%p, " + "c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_writing(r, u, L); + + if (u->body_downstream) { + return luaL_error(L, "attempt to write to request sockets"); + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket send timeout: %M", u->send_timeout); + + type = lua_type(L, 2); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_tolstring(L, 2, &len); + break; + + case LUA_TTABLE: + len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); + break; + + case LUA_TNIL: + len = sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + len = sizeof("true") - 1; + + } else { + len = sizeof("false") - 1; + } + + break; + + default: + msg = lua_pushfstring(L, "string, number, boolean, nil, " + "or array table expected, got %s", + lua_typename(L, type)); + + return luaL_argerror(L, 2, msg); + } + + if (len == 0) { + lua_pushinteger(L, 0); + return 1; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_bufs, len); + + if (cl == NULL) { + return luaL_error(L, "no memory"); + } + + b = cl->buf; + + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) lua_tolstring(L, -1, &len); + b->last = ngx_copy(b->last, (u_char *) p, len); + break; + + case LUA_TTABLE: + b->last = ngx_stream_lua_copy_str_in_table(L, -1, b->last); + break; + + case LUA_TNIL: + *b->last++ = 'n'; + *b->last++ = 'i'; + *b->last++ = 'l'; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + *b->last++ = 't'; + *b->last++ = 'r'; + *b->last++ = 'u'; + *b->last++ = 'e'; + + } else { + *b->last++ = 'f'; + *b->last++ = 'a'; + *b->last++ = 'l'; + *b->last++ = 's'; + *b->last++ = 'e'; + } + + break; + + default: + return luaL_error(L, "impossible to reach here"); + } + + u->request_bufs = cl; + + u->request_len = len; + + /* mimic ngx_http_upstream_init_request here */ + + clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module); + c = u->peer.connection; + + if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua socket tcp_nodelay"); + + tcp_nodelay = 1; + + if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, + (const void *) &tcp_nodelay, sizeof(int)) + == -1) + { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + if (llcf->log_socket_errors) { + ngx_connection_error(c, ngx_socket_errno, + "setsockopt(TCP_NODELAY) " + "failed"); + } + + lua_pushnil(L); + lua_pushliteral(L, "setsocketopt tcp_nodelay failed"); + return 2; + } + + c->tcp_nodelay = NGX_TCP_NODELAY_SET; + } + +#if 1 + u->write_waiting = 0; + u->write_co_ctx = NULL; +#endif + + ngx_stream_lua_probe_socket_tcp_send_start(r, u, b->pos, len); + + rc = ngx_stream_lua_socket_send(r, u); + + dd("socket send returned %d", (int) rc); + + if (rc == NGX_ERROR) { + return ngx_stream_lua_socket_write_error_retval_handler(r, u, L); + } + + if (rc == NGX_OK) { + lua_pushinteger(L, len); + return 1; + } + + /* rc == NGX_AGAIN */ + + coctx = ctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + if (u->raw_downstream) { + ctx->writing_raw_req_socket = 1; + } + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + u->write_co_ctx = coctx; + u->write_waiting = 1; + u->write_prepare_retvals = ngx_stream_lua_socket_tcp_send_retval_handler; + + dd("setting data to %p", u); + + return lua_yield(L, 0); +} + + +static int +ngx_stream_lua_socket_tcp_send_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket send return value handler"); + + if (u->ft_type) { + return ngx_stream_lua_socket_write_error_retval_handler(r, u, L); + } + + lua_pushinteger(L, u->request_len); + return 1; +} + + +static int +ngx_stream_lua_socket_tcp_receive_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + int n; + ngx_int_t rc; + ngx_stream_lua_ctx_t *ctx; + ngx_event_t *ev; + + ngx_stream_lua_loc_conf_t *llcf; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket receive return value handler"); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + +#if 1 + if (u->raw_downstream || u->body_downstream) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->check_client_abort) { + + r->read_event_handler = ngx_stream_lua_rd_check_broken_connection; + + ev = r->connection->read; + + dd("rev active: %d", ev->active); + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && !ev->active) { + if (ngx_add_event(ev, NGX_READ_EVENT, 0) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "failed to add event"); + return 2; + } + } + + } else { + /* llcf->check_client_abort == 0 */ + r->read_event_handler = ngx_stream_lua_block_reading; + } + } +#endif + + if (u->ft_type) { + + if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_TIMEOUT) { + u->no_close = 1; + } + + dd("u->bufs_in: %p", u->bufs_in); + + if (u->bufs_in) { + rc = ngx_stream_lua_socket_push_input_data(r, ctx, u, L); + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + (void) ngx_stream_lua_socket_read_error_retval_handler(r, u, L); + + lua_pushvalue(L, -3); + lua_remove(L, -4); + return 3; + } + + n = ngx_stream_lua_socket_read_error_retval_handler(r, u, L); + lua_pushliteral(L, ""); + return n + 1; + } + + rc = ngx_stream_lua_socket_push_input_data(r, ctx, u, L); + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + return 1; +} + + +static int +ngx_stream_lua_socket_tcp_close(lua_State *L) +{ + ngx_stream_lua_request_t *r; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument " + "(including the object) but seen %d", lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL + || u->peer.connection == NULL + || (u->read_closed && u->write_closed)) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + ngx_stream_lua_socket_check_busy_writing(r, u, L); + + if (u->raw_downstream || u->body_downstream) { + lua_pushnil(L); + lua_pushliteral(L, "attempt to close a request socket"); + return 2; + } + + ngx_stream_lua_socket_tcp_finalize(r, u); + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_stream_lua_socket_tcp_shutdown(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_str_t direction; + char *p; + ngx_stream_lua_ctx_t *ctx; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments " + "(including the object) but seen %d", lua_gettop(L)); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + ngx_stream_lua_socket_handle_write_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + /* + * only allow shutdown on raw request in stream module in content phase. + * in http module, lingering close will take care of the shutdown. + * in stream module, it is unsafe to shutdown prior on reaching content + * phase as later phases may still need to write to the socket. + */ + + if (u->raw_downstream) { + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT); + + if (ctx->eof) { + lua_pushnil(L); + lua_pushliteral(L, "seen eof"); + return 2; + } + + /* prevent all further output attempt */ + ctx->eof = 1; + } + + if (u == NULL + || u->peer.connection == NULL + || (u->read_closed && u->write_closed)) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->write_closed) { + lua_pushnil(L); + lua_pushliteral(L, "already shutdown"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_writing(r, u, L); + + /* shutdown */ + direction.data = (u_char *) luaL_checklstring(L, 2, &direction.len); + if (direction.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "pattern is empty"); + return 2; + } + + if (direction.len != 4 || ngx_strcmp(direction.data, "send") != 0) { + p = (char *) lua_pushfstring(L, "bad shutdown argument: %s", + (char *) direction.data); + + return luaL_argerror(L, 2, p); + } + + ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 1); + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_stream_lua_socket_tcp_setoption(lua_State *L) +{ + /* TODO */ + return 0; +} + + +static int +ngx_stream_lua_socket_tcp_settimeout(lua_State *L) +{ + int n; + ngx_int_t timeout; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "ngx.socket settimout: expecting 2 arguments " + "(including the object) but seen %d", lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + if (timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + lua_pushinteger(L, timeout); + lua_pushinteger(L, timeout); + + lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (timeout > 0) { + u->read_timeout = (ngx_msec_t) timeout; + u->send_timeout = (ngx_msec_t) timeout; + u->connect_timeout = (ngx_msec_t) timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + u->send_timeout = u->conf->send_timeout; + u->connect_timeout = u->conf->connect_timeout; + } + } + + return 0; +} + + +static int +ngx_stream_lua_socket_tcp_settimeouts(lua_State *L) +{ + int n; + ngx_int_t connect_timeout, send_timeout, read_timeout; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 4) { + return luaL_error(L, "ngx.socket settimout: expecting 4 arguments " + "(including the object) but seen %d", lua_gettop(L)); + } + + connect_timeout = (ngx_int_t) lua_tonumber(L, 2); + if (connect_timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + send_timeout = (ngx_int_t) lua_tonumber(L, 3); + if (send_timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + read_timeout = (ngx_int_t) lua_tonumber(L, 4); + if (read_timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (connect_timeout > 0) { + u->connect_timeout = (ngx_msec_t) connect_timeout; + + } else { + u->connect_timeout = u->conf->connect_timeout; + } + + if (send_timeout > 0) { + u->send_timeout = (ngx_msec_t) send_timeout; + + } else { + u->send_timeout = u->conf->send_timeout; + } + + if (read_timeout > 0) { + u->read_timeout = (ngx_msec_t) read_timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + } + } + + return 0; +} + + +static void +ngx_stream_lua_socket_tcp_handler(ngx_event_t *ev) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_connection_t *c; + + c = ev->data; + u = c->data; + r = u->request; + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua tcp socket handler: wev %d", (int) ev->write); + + if (ev->write) { + u->write_event_handler(r, u); + + } else { + u->read_event_handler(r, u); + } + +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data) +{ + /* empty */ + return NGX_OK; +} + + +static void +ngx_stream_lua_socket_read_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_stream_lua_loc_conf_t *llcf; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket read handler"); + + if (c->read->timedout) { + c->read->timedout = 0; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream lua tcp socket read timed out"); + } + + ngx_stream_lua_socket_handle_read_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_TIMEOUT); + return; + } + +#if 1 + if (c->read->timer_set) { + ngx_del_timer(c->read); + } +#endif + + if (u->buffer.start != NULL) { + (void) ngx_stream_lua_socket_tcp_read(r, u); + } +} + + +static void +ngx_stream_lua_socket_send_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_stream_lua_loc_conf_t *llcf; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket send handler"); + + if (c->write->timedout) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream lua tcp socket write timed out"); + } + + ngx_stream_lua_socket_handle_write_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_TIMEOUT); + return; + } + + if (u->request_bufs) { + (void) ngx_stream_lua_socket_send(r, u); + } +} + + +static ngx_int_t +ngx_stream_lua_socket_send(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_int_t n; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + ngx_buf_t *b; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket send data"); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + ngx_stream_lua_socket_handle_write_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + b = u->request_bufs->buf; + + for (;;) { + n = c->send(c, b->pos, b->last - b->pos); + + if (n >= 0) { + b->pos += n; + + if (b->pos == b->last) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua tcp socket sent all the data"); + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + + ngx_chain_update_chains(r->pool, + &ctx->free_bufs, &ctx->busy_bufs, + &u->request_bufs, + (ngx_buf_tag_t) &ngx_stream_lua_module); + + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_write_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + ngx_stream_lua_socket_handle_write_success(r, u); + return NGX_OK; + } + + /* keep sending more data */ + continue; + } + + /* NGX_ERROR || NGX_AGAIN */ + break; + } + + if (n == NGX_ERROR) { + c->error = 1; + u->socket_errno = ngx_socket_errno; + ngx_stream_lua_socket_handle_write_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + /* n == NGX_AGAIN */ + + if (u->raw_downstream) { + ctx->writing_raw_req_socket = 1; + } + + u->write_event_handler = ngx_stream_lua_socket_send_handler; + + ngx_add_timer(c->write, u->send_timeout); + + if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { + ngx_stream_lua_socket_handle_write_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +static void +ngx_stream_lua_socket_handle_conn_success(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + +#if 1 + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; +#endif + + if (u->conn_waiting) { + u->conn_waiting = 0; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_resume; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request (conn)"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_handle_read_success(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + +#if 1 + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; +#endif + + if (u->read_waiting) { + u->read_waiting = 0; + + coctx = u->read_co_ctx; + coctx->cleanup = NULL; + u->read_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_stream_lua_socket_tcp_read_resume; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request (read)"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_handle_write_success(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + +#if 1 + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; +#endif + + if (u->write_waiting) { + u->write_waiting = 0; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_stream_lua_socket_tcp_write_resume; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request (read)"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_handle_conn_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket handle connect error"); + + u->ft_type |= ft_type; + +#if 1 + ngx_stream_lua_socket_tcp_finalize(r, u); +#endif + + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; + + dd("connection waiting: %d", (int) u->conn_waiting); + + coctx = u->write_co_ctx; + + if (u->conn_waiting) { + u->conn_waiting = 0; + + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_resume; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_handle_read_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket handle read error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_stream_lua_socket_tcp_finalize(r, u); +#endif + + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + + if (u->read_waiting) { + u->read_waiting = 0; + + coctx = u->read_co_ctx; + coctx->cleanup = NULL; + u->read_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + ctx->resume_handler = ngx_stream_lua_socket_tcp_read_resume; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_handle_write_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket handle write error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_stream_lua_socket_tcp_finalize(r, u); +#endif + + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; + + if (u->write_waiting) { + u->write_waiting = 0; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + ctx->resume_handler = ngx_stream_lua_socket_tcp_write_resume; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_connected_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_int_t rc; + ngx_connection_t *c; + + ngx_stream_lua_loc_conf_t *llcf; + + c = u->peer.connection; + + if (c->write->timedout) { + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_stream_lua_socket_init_peer_connection_addr_text(&u->peer); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream lua tcp socket connect timed out," + " when connecting to %V:%ud", + &c->addr_text, ngx_inet_get_port(u->peer.sockaddr)); + } + + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_TIMEOUT); + return; + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + rc = ngx_stream_lua_socket_test_connect(r, c); + if (rc != NGX_OK) { + if (rc > 0) { + u->socket_errno = (ngx_err_t) rc; + } + + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connected"); + + /* We should delete the current write/read event + * here because the socket object may not be used immediately + * on the Lua land, thus causing hot spin around level triggered + * event poll and wasting CPU cycles. */ + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_stream_lua_socket_handle_conn_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return; + } + + ngx_stream_lua_socket_handle_conn_success(r, u); +} + + +static void +ngx_stream_lua_socket_tcp_cleanup(void *data) +{ + ngx_stream_lua_socket_tcp_upstream_t *u = data; + + ngx_stream_lua_request_t *r; + + r = u->request; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "cleanup lua tcp socket request"); + + ngx_stream_lua_socket_tcp_finalize(r, u); +} + + +static void +ngx_stream_lua_socket_tcp_finalize_read_part(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_chain_t *cl; + ngx_chain_t **ll; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + + if (u->read_closed) { + return; + } + + u->read_closed = 1; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx && u->bufs_in) { + + ll = &u->bufs_in; + for (cl = u->bufs_in; cl; cl = cl->next) { + dd("bufs_in chain: %p, next %p", cl, cl->next); + cl->buf->pos = cl->buf->last; + ll = &cl->next; + } + + dd("ctx: %p", ctx); + dd("free recv bufs: %p", ctx->free_recv_bufs); + *ll = ctx->free_recv_bufs; + ctx->free_recv_bufs = u->bufs_in; + u->bufs_in = NULL; + u->buf_in = NULL; + ngx_memzero(&u->buffer, sizeof(ngx_buf_t)); + } + + if (u->raw_downstream || u->body_downstream) { + if (r->connection->read->timer_set) { + ngx_del_timer(r->connection->read); + } + return; + } + + c = u->peer.connection; + + if (c) { + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->read->active || c->read->disabled) { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + +#if defined(nginx_version) && nginx_version >= 1007005 + if (c->read->posted) { +#else + if (c->read->prev) { +#endif + ngx_delete_posted_event(c->read); + } + + c->read->closed = 1; + + /* TODO: shutdown the reading part of the connection */ + } +} + + +static void +ngx_stream_lua_socket_tcp_finalize_write_part(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, int do_shutdown) + +{ + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + + c = u->peer.connection; + + if (u->write_closed) { + return; + } + + u->write_closed = 1; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (c && do_shutdown) { + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua shutdown socket write direction"); + } + + if (u->raw_downstream || u->body_downstream) { + if (ctx && ctx->writing_raw_req_socket) { + ctx->writing_raw_req_socket = 0; + if (r->connection->write->timer_set) { + ngx_del_timer(r->connection->write); + } + + r->connection->write->error = 1; + } + return; + } + + if (c) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (c->write->active || c->write->disabled) { + ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); + } + +#if defined(nginx_version) && nginx_version >= 1007005 + if (c->write->posted) { +#else + if (c->write->prev) { +#endif + ngx_delete_posted_event(c->write); + } + + c->write->closed = 1; + } +} + + +static void +ngx_stream_lua_socket_tcp_conn_op_timeout_handler(ngx_event_t *ev) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_request_t *r; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = ev->data; + ngx_queue_remove(&conn_op_ctx->queue); + + u = conn_op_ctx->u; + r = u->request; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + /* note that we store conn_op_ctx in coctx->data instead of u */ + coctx->data = conn_op_ctx; + u->write_co_ctx = NULL; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream lua tcp socket queued connect " + "timed out, when trying to connect to %V:%ud", + &conn_op_ctx->host, conn_op_ctx->port); + } + + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + u->socket_pool->connections--; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request"); + + u->write_prepare_retvals = + ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_socket_tcp_conn_op_resume(r); + + } else { + ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_op_resume; + ngx_stream_lua_core_run_phases(r); + } + +} + + +static int +ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L) +{ + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + return 2; +} + + +static void +ngx_stream_lua_socket_tcp_resume_conn_op( + ngx_stream_lua_socket_pool_t *spool) +{ + ngx_queue_t *q; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + +#if (NGX_DEBUG) + ngx_stream_lua_assert(spool->connections >= 0); + +#else + if (spool->connections < 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "stream lua tcp socket connections count " + "mismatched for connection pool \"%s\", connections: " + "%i, size: %i", + spool->key, spool->connections, spool->size); + spool->connections = 0; + } +#endif + + /* we manually destroy wait_connect_op before triggering connect + * operation resumption, so that there is no resumption happens when Nginx + * is exiting. + */ + if (ngx_queue_empty(&spool->wait_connect_op)) { + return; + } + + q = ngx_queue_head(&spool->wait_connect_op); + conn_op_ctx = ngx_queue_data(q, + ngx_stream_lua_socket_tcp_conn_op_ctx_t, + queue); + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket post connect operation " + "resumption u: %p, ctx: %p for connection pool \"%s\", " + "connections: %i", + conn_op_ctx->u, conn_op_ctx, spool->key, spool->connections); + + if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + conn_op_ctx->event.handler = + ngx_stream_lua_socket_tcp_conn_op_resume_handler; + + ngx_post_event((&conn_op_ctx->event), &ngx_posted_events); +} + + +static void +ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup(void *data) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx = data; + + u = conn_op_ctx->u; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream cleanup lua tcp socket " + "conn_op_ctx: %p, u: %p", + conn_op_ctx, u); + + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); +} + + +static void +ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev) +{ + ngx_queue_t *q; + ngx_stream_lua_request_t *r; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_pool_t *spool; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = ev->data; + u = conn_op_ctx->u; + r = u->request; + spool = u->socket_pool; + + if (ngx_queue_empty(&spool->wait_connect_op)) { +#if (NGX_DEBUG) + ngx_stream_lua_assert(!(spool->backlog >= 0 + && spool->connections > spool->size)); + +#else + if (spool->backlog >= 0 && spool->connections > spool->size) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "stream lua tcp socket connections count " + "mismatched for connection pool \"%s\", connections: " + "%i, size: %i", + spool->key, spool->connections, spool->size); + spool->connections = spool->size; + } +#endif + + return; + } + + q = ngx_queue_head(&spool->wait_connect_op); + ngx_queue_remove(q); + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + /* note that we store conn_op_ctx in coctx->data instead of u */ + coctx->data = conn_op_ctx; + /* clear ngx_stream_lua_tcp_queue_conn_op_cleanup */ + u->write_co_ctx = NULL; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + ngx_queue_insert_head(&spool->cache_connect_op, + &conn_op_ctx->queue); + return; + } + + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request"); + + u->write_prepare_retvals = + ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_socket_tcp_conn_op_resume(r); + + } else { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln != NULL) { + cln->handler = ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup; + cln->data = conn_op_ctx; + conn_op_ctx->cleanup = &cln->handler; + } + + ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_op_resume; + ngx_stream_lua_core_run_phases(r); + } + +} + + +static int +ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + int nret; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + coctx = ctx->cur_co_ctx; + dd("coctx: %p", coctx); + conn_op_ctx = coctx->data; + if (conn_op_ctx->cleanup != NULL) { + *conn_op_ctx->cleanup = NULL; + ngx_stream_lua_cleanup_free(r, conn_op_ctx->cleanup); + conn_op_ctx->cleanup = NULL; + } + + /* decrease pending connect operation counter */ + u->socket_pool->connections--; + + nret = ngx_stream_lua_socket_tcp_connect_helper(L, u, r, ctx, + conn_op_ctx->host.data, + conn_op_ctx->host.len, + conn_op_ctx->port, 1); + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + + return nret; +} + + +static void +ngx_stream_lua_socket_tcp_finalize(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + + ngx_stream_lua_socket_pool_t *spool; + + dd("request: %p, u: %p, u->cleanup: %p", r, u, u->cleanup); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua finalize socket"); + + if (u->cleanup) { + *u->cleanup = NULL; + ngx_stream_lua_cleanup_free(r, u->cleanup); + u->cleanup = NULL; + } + + ngx_stream_lua_socket_tcp_finalize_read_part(r, u); + + ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 0); + + if (u->raw_downstream || u->body_downstream) { + u->peer.connection = NULL; + return; + } + + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + if (u->peer.free) { + u->peer.free(&u->peer, u->peer.data, 0); + } + +#if (NGX_STREAM_SSL) + if (u->ssl_name.data) { + ngx_free(u->ssl_name.data); + u->ssl_name.data = NULL; + u->ssl_name.len = 0; + } +#endif + + c = u->peer.connection; + if (c) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua close socket connection"); + + ngx_stream_lua_socket_tcp_close_connection(c); + u->peer.connection = NULL; + + u->conn_closed = 1; + + spool = u->socket_pool; + if (spool == NULL) { + return; + } + + spool->connections--; + + if (spool->connections == 0) { + ngx_stream_lua_socket_free_pool(r->connection->log, spool); + return; + } + + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + } +} + + +static void +ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c) +{ +#if (NGX_STREAM_SSL) + + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; + + (void) ngx_ssl_shutdown(c); + } + +#endif + + if (c->pool) { + ngx_destroy_pool(c->pool); + c->pool = NULL; + } + + ngx_close_connection(c); +} + + +static ngx_int_t +ngx_stream_lua_socket_test_connect(ngx_stream_lua_request_t *r, + ngx_connection_t *c) +{ + int err; + socklen_t len; + + ngx_stream_lua_loc_conf_t *llcf; + +#if (NGX_HAVE_KQUEUE) + + ngx_event_t *ev; + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + dd("pending eof: (%p)%d (%p)%d", c->write, c->write->pending_eof, + c->read, c->read->pending_eof); + + if (c->write->pending_eof) { + ev = c->write; + + } else if (c->read->pending_eof) { + ev = c->read; + + } else { + ev = NULL; + } + + if (ev) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + if (llcf->log_socket_errors) { + (void) ngx_connection_error(c, ev->kq_errno, + "kevent() reported that " + "connect() failed"); + } + return ev->kq_errno; + } + + } else +#endif + { + err = 0; + len = sizeof(int); + + /* + * BSDs and Linux return 0 and set a pending error in err + * Solaris returns -1 and sets errno + */ + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) + == -1) + { + err = ngx_errno; + } + + if (err) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + if (llcf->log_socket_errors) { + (void) ngx_connection_error(c, err, "connect() failed"); + } + return err; + } + } + + return NGX_OK; +} + + +static void +ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket dummy handler"); +} + + +static int +ngx_stream_lua_socket_tcp_receiveuntil(lua_State *L) +{ + ngx_stream_lua_request_t *r; + int n; + ngx_str_t pat; + ngx_int_t rc; + size_t size; + unsigned inclusive = 0; + + ngx_stream_lua_socket_compiled_pattern_t *cp; + + n = lua_gettop(L); + if (n != 2 && n != 3) { + return luaL_error(L, "expecting 2 or 3 arguments " + "(including the object), but got %d", n); + } + + if (n == 3) { + /* check out the options table */ + + luaL_checktype(L, 3, LUA_TTABLE); + + lua_getfield(L, 3, "inclusive"); + + switch (lua_type(L, -1)) { + case LUA_TNIL: + /* do nothing */ + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + inclusive = 1; + } + break; + + default: + return luaL_error(L, "bad \"inclusive\" option value type: %s", + luaL_typename(L, -1)); + + } + + lua_pop(L, 2); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket calling receiveuntil() method"); + + luaL_checktype(L, 1, LUA_TTABLE); + + pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); + if (pat.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "pattern is empty"); + return 2; + } + + size = sizeof(ngx_stream_lua_socket_compiled_pattern_t); + + cp = lua_newuserdata(L, size); + if (cp == NULL) { + return luaL_error(L, "no memory"); + } + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + pattern_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + ngx_memzero(cp, size); + + cp->inclusive = inclusive; + + rc = ngx_stream_lua_socket_compile_pattern(pat.data, pat.len, cp, + r->connection->log); + + if (rc != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "failed to compile pattern"); + return 2; + } + + lua_pushcclosure(L, ngx_stream_lua_socket_receiveuntil_iterator, 3); + return 1; +} + + +static int +ngx_stream_lua_socket_receiveuntil_iterator(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_int_t rc; + ngx_stream_lua_ctx_t *ctx; + lua_Integer bytes; + int n; + + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_compiled_pattern_t *cp; + + n = lua_gettop(L); + if (n > 1) { + return luaL_error(L, "expecting 0 or 1 arguments, " + "but seen %d", n); + } + + if (n >= 1) { + bytes = luaL_checkinteger(L, 1); + if (bytes < 0) { + bytes = 0; + } + + } else { + bytes = 0; + } + + lua_rawgeti(L, lua_upvalueindex(1), SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket receiveuntil iterator"); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket read timeout: %M", u->read_timeout); + + u->input_filter = ngx_stream_lua_socket_read_until; + + cp = lua_touserdata(L, lua_upvalueindex(3)); + + dd("checking existing state: %d", cp->state); + + if (cp->state == -1) { + cp->state = 0; + + lua_pushnil(L); + lua_pushnil(L); + lua_pushnil(L); + return 3; + } + + cp->upstream = u; + + cp->pattern.data = + (u_char *) lua_tolstring(L, lua_upvalueindex(2), + &cp->pattern.len); + + u->input_filter_ctx = cp; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (u->bufs_in == NULL) { + u->bufs_in = + ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + u->conf->buffer_size); + + if (u->bufs_in == NULL) { + return luaL_error(L, "no memory"); + } + + u->buf_in = u->bufs_in; + u->buffer = *u->buf_in->buf; + } + + u->length = (size_t) bytes; + u->rest = u->length; + + if (u->raw_downstream || u->body_downstream) { + r->read_event_handler = ngx_stream_lua_req_socket_rev_handler; + } + + u->read_waiting = 0; + u->read_co_ctx = NULL; + + rc = ngx_stream_lua_socket_tcp_read(r, u); + + if (rc == NGX_ERROR) { + dd("read failed: %d", (int) u->ft_type); + rc = ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); + dd("tcp receive retval returned: %d", (int) rc); + return rc; + } + + if (rc == NGX_OK) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket receive done in a single run"); + + return ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); + } + + /* rc == NGX_AGAIN */ + + coctx = ctx->cur_co_ctx; + + u->read_event_handler = ngx_stream_lua_socket_read_handler; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + u->read_co_ctx = coctx; + u->read_waiting = 1; + u->read_prepare_retvals = ngx_stream_lua_socket_tcp_receive_retval_handler; + + dd("setting data to %p", u); + + if (u->raw_downstream || u->body_downstream) { + ctx->downstream = u; + } + + return lua_yield(L, 0); +} + + +static ngx_int_t +ngx_stream_lua_socket_compile_pattern(u_char *data, size_t len, + ngx_stream_lua_socket_compiled_pattern_t *cp, ngx_log_t *log) +{ + size_t i; + size_t prefix_len; + size_t size; + unsigned found; + int cur_state, new_state; + + ngx_stream_lua_dfa_edge_t *edge; + ngx_stream_lua_dfa_edge_t **last = NULL; + + cp->pattern.len = len; + + if (len <= 2) { + return NGX_OK; + } + + for (i = 1; i < len; i++) { + prefix_len = 1; + + while (prefix_len <= len - i - 1) { + + if (ngx_memcmp(data, &data[i], prefix_len) == 0) { + if (data[prefix_len] == data[i + prefix_len]) { + prefix_len++; + continue; + } + + cur_state = i + prefix_len; + new_state = prefix_len + 1; + + if (cp->recovering == NULL) { + size = sizeof(void *) * (len - 2); + cp->recovering = ngx_alloc(size, log); + if (cp->recovering == NULL) { + return NGX_ERROR; + } + + ngx_memzero(cp->recovering, size); + } + + edge = cp->recovering[cur_state - 2]; + + found = 0; + + if (edge == NULL) { + last = &cp->recovering[cur_state - 2]; + + } else { + + for (; edge; edge = edge->next) { + last = &edge->next; + + if (edge->chr == data[prefix_len]) { + found = 1; + + if (edge->new_state < new_state) { + edge->new_state = new_state; + } + + break; + } + } + } + + if (!found) { + ngx_log_debug7(NGX_LOG_DEBUG_STREAM, log, 0, + "stream lua tcp socket read until " + "recovering point: on state %d (%*s), if " + "next is '%c', then recover to state %d " + "(%*s)", cur_state, (size_t) cur_state, data, + data[prefix_len], new_state, + (size_t) new_state, data); + + edge = ngx_alloc(sizeof(ngx_stream_lua_dfa_edge_t), log); + if (edge == NULL) { + return NGX_ERROR; + } + + edge->chr = data[prefix_len]; + edge->new_state = new_state; + edge->next = NULL; + + *last = edge; + } + + break; + } + + break; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_socket_read_until(void *data, ssize_t bytes) +{ + ngx_stream_lua_socket_compiled_pattern_t *cp = data; + ngx_stream_lua_socket_tcp_upstream_t *u; + + ngx_stream_lua_request_t *r; + ngx_buf_t *b; + u_char c; + u_char *pat; + size_t pat_len; + int i; + int state; + int old_state = 0; /* just to make old + gcc happy */ + ngx_stream_lua_dfa_edge_t *edge; + unsigned matched; + ngx_int_t rc; + + u = cp->upstream; + r = u->request; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket read until"); + + if (bytes == 0) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + b = &u->buffer; + + pat = cp->pattern.data; + pat_len = cp->pattern.len; + state = cp->state; + + i = 0; + while (i < bytes) { + c = b->pos[i]; + + dd("%d: read char %d, state: %d", i, c, state); + + if (c == pat[state]) { + i++; + state++; + + if (state == (int) pat_len) { + /* already matched the whole pattern */ + dd("pat len: %d", (int) pat_len); + + b->pos += i; + + if (u->length) { + cp->state = -1; + + } else { + cp->state = 0; + } + + if (cp->inclusive) { + rc = ngx_stream_lua_socket_add_pending_data(r, u, b->pos, 0, + pat, state, + state); + + if (rc != NGX_OK) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + } + + return NGX_OK; + } + + continue; + } + + if (state == 0) { + u->buf_in->buf->last++; + + i++; + + if (u->length && --u->rest == 0) { + cp->state = state; + b->pos += i; + return NGX_OK; + } + + continue; + } + + matched = 0; + + if (cp->recovering && state >= 2) { + dd("accessing state: %d, index: %d", state, state - 2); + for (edge = cp->recovering[state - 2]; edge; edge = edge->next) { + + if (edge->chr == c) { + dd("matched '%c' and jumping to state %d", c, + edge->new_state); + + old_state = state; + state = edge->new_state; + matched = 1; + break; + } + } + } + + if (!matched) { +#if 1 + dd("adding pending data: %.*s", state, pat); + rc = ngx_stream_lua_socket_add_pending_data(r, u, b->pos, i, pat, + state, state); + + if (rc != NGX_OK) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + +#endif + + if (u->length) { + if (u->rest <= (size_t) state) { + u->rest = 0; + cp->state = 0; + b->pos += i; + return NGX_OK; + + } else { + u->rest -= state; + } + } + + state = 0; + continue; + } + + /* matched */ + + dd("adding pending data: %.*s", (int) (old_state + 1 - state), + (char *) pat); + + rc = ngx_stream_lua_socket_add_pending_data(r, u, b->pos, i, pat, + old_state + 1 - state, + old_state); + + if (rc != NGX_OK) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + + i++; + + if (u->length) { + if (u->rest <= (size_t) state) { + u->rest = 0; + cp->state = state; + b->pos += i; + return NGX_OK; + + } else { + u->rest -= state; + } + } + + continue; + } + + b->pos += i; + cp->state = state; + + return NGX_AGAIN; +} + + +static int +ngx_stream_lua_socket_cleanup_compiled_pattern(lua_State *L) +{ + ngx_stream_lua_socket_compiled_pattern_t *cp; + + ngx_stream_lua_dfa_edge_t *edge, *p; + unsigned i; + + dd("cleanup compiled pattern"); + + cp = lua_touserdata(L, 1); + if (cp == NULL || cp->recovering == NULL) { + return 0; + } + + dd("pattern len: %d", (int) cp->pattern.len); + + for (i = 0; i < cp->pattern.len - 2; i++) { + edge = cp->recovering[i]; + + while (edge) { + p = edge; + edge = edge->next; + + dd("freeing edge %p", p); + + ngx_free(p); + + dd("edge: %p", edge); + } + } + +#if 1 + ngx_free(cp->recovering); + cp->recovering = NULL; +#endif + + return 0; +} + + +int +ngx_stream_lua_req_socket_tcp(lua_State *L) +{ + int n, raw; + ngx_peer_connection_t *pc; + ngx_stream_lua_loc_conf_t *llcf; + ngx_connection_t *c; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 0 && n != 1) { + return luaL_error(L, "expecting zero arguments, but got %d", + lua_gettop(L)); + } + + if (n == 1) { + lua_pop(L, 1); + } + + raw = 1; + + r = ngx_stream_lua_get_req(L); + + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + |NGX_STREAM_LUA_CONTEXT_PREREAD); + + c = r->connection; + + if (raw) { + + if (c->buffered) { + lua_pushnil(L); + lua_pushliteral(L, "pending data to write"); + return 2; + } + + + + dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket); + + if (ctx->acquired_raw_req_socket) { + lua_pushnil(L); + lua_pushliteral(L, "duplicate call"); + return 2; + } + + ctx->acquired_raw_req_socket = 1; + + + } else { + } + + lua_createtable(L, 2 /* narr */, 3 /* nrec */); /* the object */ + + if (raw) { + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + raw_req_socket_metatable_key)); + + } + + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_tcp_upstream_t)); + if (u == NULL) { + return luaL_error(L, "no memory"); + } + +#if 1 + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + downstream_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +#endif + + lua_rawseti(L, 1, SOCKET_CTX_INDEX); + + ngx_memzero(u, sizeof(ngx_stream_lua_socket_tcp_upstream_t)); + + if (raw) { + u->raw_downstream = 1; + + } else { + } + + coctx = ctx->cur_co_ctx; + + u->request = r; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + u->conf = llcf; + + u->read_timeout = u->conf->read_timeout; + u->connect_timeout = u->conf->connect_timeout; + u->send_timeout = u->conf->send_timeout; + + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + cln->handler = ngx_stream_lua_socket_tcp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + + pc = &u->peer; + + pc->log = c->log; + pc->log_error = NGX_ERROR_ERR; + + pc->connection = c; + + dd("setting data to %p", u); + + coctx->data = u; + ctx->downstream = u; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (raw) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + } + + lua_settop(L, 1); + return 1; +} + + +static void +ngx_stream_lua_req_socket_rev_handler(ngx_stream_lua_request_t *r) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_socket_tcp_upstream_t *u; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua request socket read event handler"); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + r->read_event_handler = ngx_stream_lua_block_reading; + return; + } + + u = ctx->downstream; + if (u == NULL || u->peer.connection == NULL) { + r->read_event_handler = ngx_stream_lua_block_reading; + return; + } + + u->read_event_handler(r, u); +} + +static int +ngx_stream_lua_socket_tcp_getreusedtimes(lua_State *L) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument " + "(including the object), but got %d", lua_gettop(L)); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL + || u->peer.connection == NULL + || (u->read_closed && u->write_closed)) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + lua_pushinteger(L, u->reused); + return 1; +} + + +static int +ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) +{ + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_pool_t *spool; + ngx_stream_lua_socket_pool_item_t *item; + + ngx_connection_t *c; + ngx_str_t key; + ngx_queue_t *q; + ngx_peer_connection_t *pc; + ngx_stream_lua_request_t *r; + ngx_msec_t timeout; + ngx_int_t pool_size; + int n; + ngx_int_t rc; + ngx_buf_t *b; + const char *msg; + + n = lua_gettop(L); + + if (n < 1 || n > 3) { + return luaL_error(L, "expecting 1 to 3 arguments " + "(including the object), but got %d", n); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + /* stack: obj timeout? size? */ + + pc = &u->peer; + c = pc->connection; + + if (c == NULL || u->read_closed || u->write_closed) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + ngx_stream_lua_socket_check_busy_writing(r, u, L); + + b = &u->buffer; + + if (b->start && ngx_buf_size(b)) { + ngx_stream_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, b->pos, + b->last - b->pos); + + lua_pushnil(L); + lua_pushliteral(L, "unread data in buffer"); + return 2; + } + + if (c->read->eof + || c->read->error + || c->read->timedout + || c->write->error + || c->write->timedout) + { + lua_pushnil(L); + lua_pushliteral(L, "invalid connection"); + return 2; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "failed to handle read event"); + return 2; + } + + if (ngx_terminate || ngx_exiting) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "stream lua tcp socket set keepalive while " + "process exiting, closing connection %p", c); + + ngx_stream_lua_socket_tcp_finalize(r, u); + lua_pushinteger(L, 1); + return 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "stream lua tcp socket set keepalive: saving " + "connection %p", c); + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* stack: obj timeout? size? pools */ + + lua_rawgeti(L, 1, SOCKET_KEY_INDEX); + key.data = (u_char *) lua_tolstring(L, -1, &key.len); + if (key.data == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "key not found"); + return 2; + } + + dd("saving connection to key %s", lua_tostring(L, -1)); + + lua_pushvalue(L, -1); + lua_rawget(L, -3); + spool = lua_touserdata(L, -1); + lua_pop(L, 1); + + /* stack: obj timeout? size? pools cache_key */ + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (spool == NULL) { + /* create a new socket pool for the current peer key */ + + if (n >= 3 && !lua_isnil(L, 3)) { + pool_size = luaL_checkinteger(L, 3); + + } else { + pool_size = llcf->pool_size; + } + + if (pool_size <= 0) { + msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", + pool_size); + return luaL_argerror(L, n, msg); + } + + ngx_stream_lua_socket_tcp_create_socket_pool(L, r, key, + pool_size, -1, + &spool); + } + + if (ngx_queue_empty(&spool->free)) { + + q = ngx_queue_last(&spool->cache); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue); + + ngx_stream_lua_socket_tcp_close_connection(item->connection); + + /* only decrease the counter for connections which were counted */ + if (u->socket_pool != NULL) { + u->socket_pool->connections--; + } + + } else { + q = ngx_queue_head(&spool->free); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue); + + /* we should always increase connections after getting connected, + * and decrease connections after getting closed. + * however, we don't create connection pool in previous connect method. + * so we increase connections here for backward compatibility. + */ + if (u->socket_pool == NULL) { + spool->connections++; + } + } + + item->connection = c; + ngx_queue_insert_head(&spool->cache, q); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "stream lua tcp socket clear current socket connection"); + + pc->connection = NULL; + +#if 0 + if (u->cleanup) { + *u->cleanup = NULL; + u->cleanup = NULL; + } +#endif + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (n >= 2 && !lua_isnil(L, 2)) { + timeout = (ngx_msec_t) luaL_checkinteger(L, 2); + + } else { + timeout = llcf->keepalive_timeout; + } + +#if (NGX_DEBUG) + if (timeout == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket keepalive timeout: unlimited"); + } +#endif + + if (timeout) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket keepalive timeout: %M ms", + timeout); + + ngx_add_timer(c->read, timeout); + } + + c->write->handler = ngx_stream_lua_socket_keepalive_dummy_handler; + c->read->handler = ngx_stream_lua_socket_keepalive_rev_handler; + + c->data = item; + c->idle = 1; + c->log = ngx_cycle->log; + c->pool->log = ngx_cycle->log; + c->read->log = ngx_cycle->log; + c->write->log = ngx_cycle->log; + + item->socklen = pc->socklen; + ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); + item->reused = u->reused; + + if (c->read->ready) { + rc = ngx_stream_lua_socket_keepalive_close_handler(c->read); + if (rc != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "connection in dubious state"); + return 2; + } + } + +#if 1 + ngx_stream_lua_socket_tcp_finalize(r, u); +#endif + + /* since we set u->peer->connection to NULL previously, the connect + * operation won't be resumed in the + * ngx_stream_lua_socket_tcp_finalize. + * Therefore we need to resume it here. + */ + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + + lua_pushinteger(L, 1); + return 1; +} + + +static ngx_int_t +ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_stream_lua_socket_pool_item_t *item; + ngx_stream_lua_socket_pool_t *spool; + + ngx_stream_lua_cleanup_t *cln; + ngx_queue_t *q; + ngx_peer_connection_t *pc; + ngx_connection_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket pool get keepalive peer"); + + pc = &u->peer; + + spool = u->socket_pool; + + if (!ngx_queue_empty(&spool->cache)) { + q = ngx_queue_head(&spool->cache); + + item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue); + c = item->connection; + + ngx_queue_remove(q); + ngx_queue_insert_head(&spool->free, q); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "stream lua tcp socket get keepalive peer: " + "using connection %p, fd:%d", c, c->fd); + + c->idle = 0; + c->log = pc->log; + c->pool->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->data = u; + +#if 1 + c->write->handler = ngx_stream_lua_socket_tcp_handler; + c->read->handler = ngx_stream_lua_socket_tcp_handler; +#endif + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + pc->connection = c; + pc->cached = 1; + + u->reused = item->reused + 1; + +#if 1 + u->write_event_handler = ngx_stream_lua_socket_dummy_handler; + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; +#endif + + if (u->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + + cln->handler = ngx_stream_lua_socket_tcp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + } + + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "stream lua tcp socket keepalive: connection pool empty"); + + return NGX_DECLINED; +} + + +static void +ngx_stream_lua_socket_keepalive_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0, + "stream keepalive dummy handler"); +} + + +static void +ngx_stream_lua_socket_keepalive_rev_handler(ngx_event_t *ev) +{ + (void) ngx_stream_lua_socket_keepalive_close_handler(ev); +} + + +static ngx_int_t +ngx_stream_lua_socket_keepalive_close_handler(ngx_event_t *ev) +{ + ngx_stream_lua_socket_pool_item_t *item; + ngx_stream_lua_socket_pool_t *spool; + + int n; + char buf[1]; + ngx_connection_t *c; + + c = ev->data; + + if (c->close) { + goto close; + } + + if (c->read->timedout) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0, + "stream lua tcp socket keepalive max idle timeout"); + + goto close; + } + + dd("read event ready: %d", (int) c->read->ready); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0, + "stream lua tcp socket keepalive close handler " + "check stale events"); + + n = recv(c->fd, buf, 1, MSG_PEEK); + + if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + /* stale event */ + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto close; + } + + return NGX_OK; + } + +close: + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ev->log, 0, + "stream lua tcp socket keepalive close handler: fd:%d", + c->fd); + + item = c->data; + spool = item->socket_pool; + + ngx_stream_lua_socket_tcp_close_connection(c); + + ngx_queue_remove(&item->queue); + ngx_queue_insert_head(&spool->free, &item->queue); + spool->connections--; + + dd("keepalive: connections: %u", (unsigned) spool->connections); + + if (spool->connections == 0) { + ngx_stream_lua_socket_free_pool(ev->log, spool); + + } else { + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + } + + return NGX_DECLINED; +} + + +static void +ngx_stream_lua_socket_free_pool(ngx_log_t *log, + ngx_stream_lua_socket_pool_t *spool) +{ + lua_State *L; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "stream lua tcp socket keepalive: free " + "connection pool for \"%s\"", spool->key); + + L = spool->lua_vm; + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushstring(L, (char *) spool->key); + lua_pushnil(L); + lua_rawset(L, -3); + lua_pop(L, 1); +} + + +static void +ngx_stream_lua_socket_shutdown_pool_helper( + ngx_stream_lua_socket_pool_t *spool) +{ + ngx_queue_t *q; + ngx_connection_t *c; + ngx_stream_lua_socket_pool_item_t *item; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + while (!ngx_queue_empty(&spool->cache)) { + q = ngx_queue_head(&spool->cache); + + item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue); + c = item->connection; + + ngx_stream_lua_socket_tcp_close_connection(c); + + ngx_queue_remove(q); + ngx_queue_insert_head(&spool->free, q); + } + + while (!ngx_queue_empty(&spool->cache_connect_op)) { + q = ngx_queue_head(&spool->cache_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data(q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, + queue); + ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); + } + + while (!ngx_queue_empty(&spool->wait_connect_op)) { + q = ngx_queue_head(&spool->wait_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data(q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, + queue); + + if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); + } + + /* spool->connections will be decreased down to zero in + * ngx_stream_lua_socket_tcp_finalize */ +} + + +static int +ngx_stream_lua_socket_shutdown_pool(lua_State *L) +{ + ngx_stream_lua_socket_pool_t *spool; + + spool = lua_touserdata(L, 1); + + if (spool != NULL) { + ngx_stream_lua_socket_shutdown_pool_helper(spool); + } + + return 0; +} + + +static int +ngx_stream_lua_socket_tcp_upstream_destroy(lua_State *L) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + + dd("upstream destroy triggered by Lua GC"); + + u = lua_touserdata(L, 1); + if (u == NULL) { + return 0; + } + + if (u->cleanup) { + ngx_stream_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ + } + + return 0; +} + + +static int +ngx_stream_lua_socket_downstream_destroy(lua_State *L) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + + dd("downstream destroy"); + + u = lua_touserdata(L, 1); + if (u == NULL) { + dd("u is NULL"); + return 0; + } + + if (u->cleanup) { + ngx_stream_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ + } + + return 0; +} + + +static ngx_int_t +ngx_stream_lua_socket_push_input_data(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L) +{ + ngx_chain_t *cl; + ngx_chain_t **ll; +#if (DDEBUG) || (NGX_DTRACE) + size_t size = 0; +#endif + size_t chunk_size; + ngx_buf_t *b; + size_t nbufs; + luaL_Buffer luabuf; + + dd("bufs_in: %p, buf_in: %p", u->bufs_in, u->buf_in); + + nbufs = 0; + ll = NULL; + + luaL_buffinit(L, &luabuf); + + for (cl = u->bufs_in; cl; cl = cl->next) { + b = cl->buf; + chunk_size = b->last - b->pos; + + dd("copying input data chunk from %p: \"%.*s\"", cl, + (int) chunk_size, b->pos); + + luaL_addlstring(&luabuf, (char *) b->pos, chunk_size); + + if (cl->next) { + ll = &cl->next; + } + +#if (DDEBUG) || (NGX_DTRACE) + size += chunk_size; +#endif + + nbufs++; + } + + luaL_pushresult(&luabuf); + +#if (DDEBUG) + dd("size: %d, nbufs: %d", (int) size, (int) nbufs); +#endif + +#if (NGX_DTRACE) + ngx_stream_lua_probe_socket_tcp_receive_done(r, u, + (u_char *) lua_tostring(L, -1), + size); +#endif + + if (nbufs > 1 && ll) { + dd("recycle buffers: %d", (int) (nbufs - 1)); + + *ll = ctx->free_recv_bufs; + ctx->free_recv_bufs = u->bufs_in; + u->bufs_in = u->buf_in; + } + + if (u->buffer.pos == u->buffer.last) { + dd("resetting u->buffer pos & last"); + u->buffer.pos = u->buffer.start; + u->buffer.last = u->buffer.start; + } + + if (u->bufs_in) { + u->buf_in->buf->last = u->buffer.pos; + u->buf_in->buf->pos = u->buffer.pos; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_socket_add_input_buffer(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) +{ + ngx_chain_t *cl; + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + u->conf->buffer_size); + + if (cl == NULL) { + return NGX_ERROR; + } + + u->buf_in->next = cl; + u->buf_in = cl; + u->buffer = *cl->buf; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_socket_add_pending_data(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, + u_char *pat, int prefix, int old_state) +{ + u_char *last; + ngx_buf_t *b; + + dd("resuming data: %d: [%.*s]", prefix, prefix, pat); + + last = &pos[len]; + + b = u->buf_in->buf; + + if (last - b->last == old_state) { + b->last += prefix; + return NGX_OK; + } + + dd("need more buffers because %d != %d", (int) (last - b->last), + (int) old_state); + + if (ngx_stream_lua_socket_insert_buffer(r, u, pat, prefix) != NGX_OK) { + return NGX_ERROR; + } + + b->pos = last; + b->last = last; + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_lua_socket_insert_buffer( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + u_char *pat, size_t prefix) +{ + ngx_chain_t *cl, *new_cl, **ll; + size_t size; + ngx_buf_t *b; + + ngx_stream_lua_ctx_t *ctx; + + if (prefix <= u->conf->buffer_size) { + size = u->conf->buffer_size; + + } else { + size = prefix; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + new_cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + size); + + if (new_cl == NULL) { + return NGX_ERROR; + } + + b = new_cl->buf; + + b->last = ngx_copy(b->last, pat, prefix); + + dd("copy resumed data to %p: %d: \"%.*s\"", + new_cl, (int) (b->last - b->pos), (int) (b->last - b->pos), b->pos); + + dd("before resuming data: bufs_in %p, buf_in %p, buf_in next %p", + u->bufs_in, u->buf_in, u->buf_in->next); + + ll = &u->bufs_in; + for (cl = u->bufs_in; cl->next; cl = cl->next) { + ll = &cl->next; + } + + *ll = new_cl; + new_cl->next = u->buf_in; + + dd("after resuming data: bufs_in %p, buf_in %p, buf_in next %p", + u->bufs_in, u->buf_in, u->buf_in->next); + +#if (DDEBUG) + for (cl = u->bufs_in; cl; cl = cl->next) { + b = cl->buf; + + dd("result buf after resuming data: %p: %.*s", cl, + (int) ngx_buf_size(b), b->pos); + } +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_conn_op_resume(ngx_stream_lua_request_t *r) +{ + return ngx_stream_lua_socket_tcp_resume_helper(r, + SOCKET_OP_RESUME_CONN); +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_conn_resume(ngx_stream_lua_request_t *r) +{ + return ngx_stream_lua_socket_tcp_resume_helper(r, SOCKET_OP_CONNECT); +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_read_resume(ngx_stream_lua_request_t *r) +{ + return ngx_stream_lua_socket_tcp_resume_helper(r, SOCKET_OP_READ); +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_write_resume(ngx_stream_lua_request_t *r) +{ + return ngx_stream_lua_socket_tcp_resume_helper(r, SOCKET_OP_WRITE); +} + + +static ngx_int_t +ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r, + int socket_op) +{ + int nret; + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + ngx_stream_lua_socket_tcp_retval_handler prepare_retvals; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_stream_lua_wev_handler; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp operation done, resuming lua " + "thread"); + + coctx = ctx->cur_co_ctx; + + dd("coctx: %p", coctx); + + switch (socket_op) { + + case SOCKET_OP_RESUME_CONN: + conn_op_ctx = coctx->data; + u = conn_op_ctx->u; + prepare_retvals = u->write_prepare_retvals; + break; + + case SOCKET_OP_CONNECT: + case SOCKET_OP_WRITE: + u = coctx->data; + prepare_retvals = u->write_prepare_retvals; + break; + + case SOCKET_OP_READ: + u = coctx->data; + prepare_retvals = u->read_prepare_retvals; + break; + + default: + /* impossible to reach here */ + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket calling prepare retvals handler %p, " + "u:%p", prepare_retvals, u); + + nret = prepare_retvals(r, u, ctx->cur_co_ctx->co); + if (socket_op == SOCKET_OP_CONNECT + && nret > 1 + && !u->conn_closed + && u->socket_pool != NULL) + { + u->socket_pool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool); + } + + if (nret == NGX_AGAIN) { + return NGX_DONE; + } + + c = r->connection; + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_stream_lua_run_thread(vm, r, ctx, nret); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +static void +ngx_stream_lua_tcp_queue_conn_op_cleanup(void *data) +{ + ngx_stream_lua_co_ctx_t *coctx = data; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = coctx->data; + u = conn_op_ctx->u; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket abort queueing, " + "conn_op_ctx: %p, u: %p", + conn_op_ctx, u); + + if (conn_op_ctx->event.posted) { + ngx_delete_posted_event(&conn_op_ctx->event); + + } else if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + ngx_queue_remove(&conn_op_ctx->queue); + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + + u->socket_pool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool); +} + + +static void +ngx_stream_lua_tcp_resolve_cleanup(void *data) +{ + ngx_resolver_ctx_t *rctx; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_co_ctx_t *coctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket abort resolver"); + + u = coctx->data; + if (u == NULL) { + return; + } + + if (u->socket_pool != NULL) { + u->socket_pool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool); + } + + rctx = u->resolved->ctx; + if (rctx == NULL) { + return; + } + + /* postpone free the rctx in the handler */ + rctx->handler = ngx_resolve_name_done; +} + + +static void +ngx_stream_lua_coctx_cleanup(void *data) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_co_ctx_t *coctx = data; + + dd("running coctx cleanup"); + + u = coctx->data; + if (u == NULL) { + return; + } + + if (u->request == NULL) { + return; + } + + ngx_stream_lua_socket_tcp_finalize(u->request, u); +} + + +#if (NGX_STREAM_SSL) + +static int +ngx_stream_lua_ssl_free_session(lua_State *L) +{ + ngx_ssl_session_t **psession; + + psession = lua_touserdata(L, 1); + if (psession && *psession != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua ssl free session: %p", *psession); + + ngx_ssl_free_session(*psession); + } + + return 0; +} + +#endif /* NGX_STREAM_SSL */ + + +void +ngx_stream_lua_cleanup_conn_pools(lua_State *L) +{ + ngx_stream_lua_socket_pool_t *spool; + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); /* table */ + + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + /* tb key val */ + spool = lua_touserdata(L, -1); + + if (spool != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket keepalive: free " + "connection pool %p for \"%s\"", spool, spool->key); + + ngx_stream_lua_socket_shutdown_pool_helper(spool); + } + + lua_pop(L, 1); + } + + lua_pop(L, 1); +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_tcp.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_tcp.h new file mode 100644 index 000000000..3473250a2 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_tcp.h @@ -0,0 +1,189 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_socket_tcp.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SOCKET_TCP_H_INCLUDED_ +#define _NGX_STREAM_LUA_SOCKET_TCP_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +#define NGX_STREAM_LUA_SOCKET_FT_ERROR 0x0001 +#define NGX_STREAM_LUA_SOCKET_FT_TIMEOUT 0x0002 +#define NGX_STREAM_LUA_SOCKET_FT_CLOSED 0x0004 +#define NGX_STREAM_LUA_SOCKET_FT_RESOLVER 0x0008 +#define NGX_STREAM_LUA_SOCKET_FT_BUFTOOSMALL 0x0010 +#define NGX_STREAM_LUA_SOCKET_FT_NOMEM 0x0020 +#define NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE 0x0040 +#define NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT 0x0080 +#define NGX_STREAM_LUA_SOCKET_FT_SSL 0x0100 + + +typedef struct ngx_stream_lua_socket_tcp_upstream_s + ngx_stream_lua_socket_tcp_upstream_t; + + +typedef + int (*ngx_stream_lua_socket_tcp_retval_handler)(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L); + + +typedef void (*ngx_stream_lua_socket_tcp_upstream_handler_pt) + (ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); + + +typedef struct { + ngx_event_t event; + ngx_queue_t queue; + ngx_str_t host; + ngx_stream_lua_cleanup_pt *cleanup; + ngx_stream_lua_socket_tcp_upstream_t *u; + in_port_t port; +} ngx_stream_lua_socket_tcp_conn_op_ctx_t; + + +#define ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx) \ + ngx_free(conn_op_ctx->host.data); \ + ngx_free(conn_op_ctx) + + +typedef struct { + lua_State *lua_vm; + + ngx_int_t size; + ngx_queue_t cache_connect_op; + ngx_queue_t wait_connect_op; + + /* connections == active connections + pending connect operations, + * while active connections == out-of-pool reused connections + * + in-pool connections */ + ngx_int_t connections; + + /* queues of ngx_stream_lua_socket_pool_item_t: */ + ngx_queue_t cache; + ngx_queue_t free; + + ngx_int_t backlog; + + u_char key[1]; + +} ngx_stream_lua_socket_pool_t; + + +struct ngx_stream_lua_socket_tcp_upstream_s { + ngx_stream_lua_socket_tcp_retval_handler read_prepare_retvals; + ngx_stream_lua_socket_tcp_retval_handler write_prepare_retvals; + ngx_stream_lua_socket_tcp_upstream_handler_pt read_event_handler; + ngx_stream_lua_socket_tcp_upstream_handler_pt write_event_handler; + + ngx_stream_lua_socket_pool_t *socket_pool; + + ngx_stream_lua_loc_conf_t *conf; + ngx_stream_lua_cleanup_pt *cleanup; + ngx_stream_lua_request_t *request; + + ngx_peer_connection_t peer; + + ngx_msec_t read_timeout; + ngx_msec_t send_timeout; + ngx_msec_t connect_timeout; + + ngx_stream_upstream_resolved_t *resolved; + + ngx_chain_t *bufs_in; /* input data buffers */ + ngx_chain_t *buf_in; /* last input data buffer */ + ngx_buf_t buffer; /* receive buffer */ + + size_t length; + size_t rest; + + ngx_err_t socket_errno; + + ngx_int_t (*input_filter)(void *data, ssize_t bytes); + void *input_filter_ctx; + + size_t request_len; + ngx_chain_t *request_bufs; + + ngx_stream_lua_co_ctx_t *read_co_ctx; + ngx_stream_lua_co_ctx_t *write_co_ctx; + + ngx_uint_t reused; + +#if (NGX_STREAM_SSL) + ngx_str_t ssl_name; +#endif + + unsigned ft_type:16; + unsigned no_close:1; + unsigned conn_waiting:1; + unsigned read_waiting:1; + unsigned write_waiting:1; + unsigned eof:1; + unsigned body_downstream:1; + unsigned raw_downstream:1; + unsigned read_closed:1; + unsigned write_closed:1; + unsigned conn_closed:1; + unsigned read_consumed:1; +#if (NGX_STREAM_SSL) + unsigned ssl_verify:1; + unsigned ssl_session_reuse:1; +#endif +}; + + +typedef struct ngx_stream_lua_dfa_edge_s ngx_stream_lua_dfa_edge_t; + + +struct ngx_stream_lua_dfa_edge_s { + ngx_stream_lua_dfa_edge_t *next; + int new_state; + u_char chr; +}; + + +typedef struct { + ngx_stream_lua_socket_tcp_upstream_t *upstream; + + ngx_str_t pattern; + ngx_stream_lua_dfa_edge_t **recovering; + int state; + + unsigned inclusive:1; +} ngx_stream_lua_socket_compiled_pattern_t; + + +typedef struct { + ngx_stream_lua_socket_pool_t *socket_pool; + + ngx_queue_t queue; + ngx_connection_t *connection; + + socklen_t socklen; + struct sockaddr_storage sockaddr; + + ngx_uint_t reused; + +} ngx_stream_lua_socket_pool_item_t; + + +void ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L); +void ngx_stream_lua_cleanup_conn_pools(lua_State *L); +int ngx_stream_lua_req_socket_tcp(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_SOCKET_TCP_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_udp.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_udp.c new file mode 100644 index 000000000..a951d0099 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_udp.c @@ -0,0 +1,1954 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_socket_udp.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_socket_udp.h" +#include "ngx_stream_lua_socket_tcp.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_output.h" +#include "ngx_stream_lua_probe.h" + + +#if 1 +#undef ngx_stream_lua_probe_info +#define ngx_stream_lua_probe_info(msg) +#endif + + +#define UDP_MAX_DATAGRAM_SIZE 8192 + + +static int ngx_stream_lua_socket_udp(lua_State *L); +static int ngx_stream_lua_socket_udp_setpeername(lua_State *L); +static int ngx_stream_lua_socket_udp_send(lua_State *L); +static int ngx_stream_lua_socket_udp_receive(lua_State *L); +static int ngx_stream_lua_socket_udp_settimeout(lua_State *L); +static void ngx_stream_lua_socket_udp_finalize(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u); +static int ngx_stream_lua_socket_udp_upstream_destroy(lua_State *L); +static int ngx_stream_lua_socket_resolve_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u, + lua_State *L); +static void ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx); +static int ngx_stream_lua_socket_error_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u, + lua_State *L); +static void ngx_stream_lua_socket_udp_handle_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type); +static void ngx_stream_lua_socket_udp_cleanup(void *data); +static void ngx_stream_lua_socket_udp_handler(ngx_event_t *ev); +static void ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u); +static int ngx_stream_lua_socket_udp_receive_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u, + lua_State *L); +static ngx_int_t ngx_stream_lua_socket_udp_read(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u); +static void ngx_stream_lua_socket_udp_read_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u); +static void ngx_stream_lua_socket_udp_handle_success( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u); +static ngx_int_t ngx_stream_lua_udp_connect( + ngx_stream_lua_udp_connection_t *uc); +static int ngx_stream_lua_socket_udp_close(lua_State *L); +static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_lua_request_t *r); +static void ngx_stream_lua_udp_resolve_cleanup(void *data); +static void ngx_stream_lua_udp_socket_cleanup(void *data); +#ifndef NGX_WIN32 +static ssize_t ngx_stream_lua_udp_sendmsg(ngx_connection_t *c, + ngx_iovec_t *vec); +#endif + + +enum { + SOCKET_CTX_INDEX = 1, + SOCKET_TIMEOUT_INDEX = 2 +}; + + +static char ngx_stream_lua_socket_udp_metatable_key; +static char ngx_stream_lua_udp_udata_metatable_key; +static char ngx_stream_lua_socket_udp_raw_req_socket_metatable_key; +static char ngx_stream_lua_socket_udp_downstream_udata_metatable_key; +static u_char ngx_stream_lua_socket_udp_buffer[UDP_MAX_DATAGRAM_SIZE]; + + +void +ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L) +{ + lua_getfield(L, -1, "socket"); /* ngx socket */ + + lua_pushcfunction(L, ngx_stream_lua_socket_udp); + lua_setfield(L, -2, "udp"); /* ngx socket */ + + /* udp upstream socket object metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_udp_metatable_key)); + lua_createtable(L, 0 /* narr */, 6 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_setpeername); + lua_setfield(L, -2, "setpeername"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_send); + lua_setfield(L, -2, "send"); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_close); + lua_setfield(L, -2, "close"); /* ngx socket mt */ + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* udp downstream socket object metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_udp_raw_req_socket_metatable_key)); + lua_createtable(L, 0 /* narr */, 4 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_send); + lua_setfield(L, -2, "send"); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* udp upstream socket object metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + udp_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_stream_lua_socket_udp_upstream_destroy); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* udp downstream socket object metatable */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_udp_downstream_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + /* share the same destructor as upstream */ + lua_pushcfunction(L, ngx_stream_lua_socket_udp_upstream_destroy); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + lua_pop(L, 1); +} + + +static int +ngx_stream_lua_socket_udp(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + if (lua_gettop(L) != 0) { + return luaL_error(L, "expecting zero arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + lua_createtable(L, 3 /* narr */, 1 /* nrec */); + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_udp_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + dd("top: %d", lua_gettop(L)); + + return 1; +} + + +static int +ngx_stream_lua_socket_udp_setpeername(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_str_t host; + int port; + ngx_resolver_ctx_t *rctx, temp; + ngx_stream_core_srv_conf_t *clcf; + int saved_top; + int n; + u_char *p; + size_t len; + ngx_url_t url; + ngx_int_t rc; + int timeout; + + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_udp_connection_t *uc; + ngx_stream_lua_socket_udp_upstream_t *u; + + /* + * TODO: we should probably accept an extra argument to setpeername() + * to allow the user bind the datagram unix domain socket himself, + * which is necessary for systems without autobind support. + */ + + n = lua_gettop(L); + if (n != 2 && n != 3) { + return luaL_error(L, "ngx.socket.udp setpeername: expecting 2 or 3 " + "arguments (including the object), but seen %d", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + luaL_checktype(L, 1, LUA_TTABLE); + + p = (u_char *) luaL_checklstring(L, 2, &len); + + host.data = ngx_palloc(r->pool, len + 1); + if (host.data == NULL) { + return luaL_error(L, "no memory"); + } + + host.len = len; + + ngx_memcpy(host.data, p, len); + host.data[len] = '\0'; + + if (n == 3) { + port = luaL_checkinteger(L, 3); + + if (port < 0 || port > 65535) { + lua_pushnil(L); + lua_pushfstring(L, "bad port number: %d", port); + return 2; + } + + } else { /* n == 2 */ + port = 0; + } + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u) { + if (u->request && u->request != r) { + return luaL_error(L, "bad request"); + } + + if (u->waiting) { + lua_pushnil(L); + lua_pushliteral(L, "socket busy"); + return 2; + } + + if (u->udp_connection.connection) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket reconnect without shutting down"); + + ngx_stream_lua_socket_udp_finalize(r, u); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua reuse socket upstream ctx"); + + } else { + u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_udp_upstream_t)); + if (u == NULL) { + return luaL_error(L, "no memory"); + } + +#if 1 + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + udp_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +#endif + + lua_rawseti(L, 1, SOCKET_CTX_INDEX); + } + + ngx_memzero(u, sizeof(ngx_stream_lua_socket_udp_upstream_t)); + + u->request = r; /* set the controlling request */ + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + u->conf = llcf; + + uc = &u->udp_connection; + + uc->log = *r->connection->log; + + dd("lua peer connection log: %p", &uc->log); + + lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); + timeout = (ngx_int_t) lua_tointeger(L, -1); + lua_pop(L, 1); + + if (timeout > 0) { + u->read_timeout = (ngx_msec_t) timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + } + + ngx_memzero(&url, sizeof(ngx_url_t)); + + url.url.len = host.len; + url.url.data = host.data; + url.default_port = (in_port_t) port; + url.no_resolve = 1; + + if (ngx_parse_url(r->pool, &url) != NGX_OK) { + lua_pushnil(L); + + if (url.err) { + lua_pushfstring(L, "failed to parse host name \"%s\": %s", + host.data, url.err); + + } else { + lua_pushfstring(L, "failed to parse host name \"%s\"", host.data); + } + + return 2; + } + + u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_stream_upstream_resolved_t)); + if (u->resolved == NULL) { + return luaL_error(L, "no memory"); + } + + if (url.addrs && url.addrs[0].sockaddr) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket network address given directly"); + + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = host; + u->resolved->port = (in_port_t) port; + } + + if (u->resolved->sockaddr) { + rc = ngx_stream_lua_socket_resolve_retval_handler(r, u, L); + if (rc == NGX_AGAIN) { + return lua_yield(L, 0); + } + + return rc; + } + + clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module); + + temp.name = host; + rctx = ngx_resolve_start(clcf->resolver, &temp); + if (rctx == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushliteral(L, "failed to start the resolver"); + return 2; + } + + if (rctx == NGX_NO_RESOLVER) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); + return 2; + } + + rctx->name = host; + rctx->handler = ngx_stream_lua_socket_resolve_handler; + rctx->data = u; + rctx->timeout = clcf->resolver_timeout; + + u->co_ctx = ctx->cur_co_ctx; + u->resolved->ctx = rctx; + + saved_top = lua_gettop(L); + + coctx = ctx->cur_co_ctx; + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_udp_resolve_cleanup; + + if (ngx_resolve_name(rctx) != NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket fail to run resolver immediately"); + + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + + u->resolved->ctx = NULL; + lua_pushnil(L); + lua_pushfstring(L, "%s could not be resolved", host.data); + + return 2; + } + + if (u->waiting == 1) { + /* resolved and already connecting */ + return lua_yield(L, 0); + } + + n = lua_gettop(L) - saved_top; + if (n) { + /* errors occurred during resolving or connecting + * or already connected */ + return n; + } + + /* still resolving */ + + u->waiting = 1; + u->prepare_retvals = ngx_stream_lua_socket_resolve_retval_handler; + + coctx->data = u; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + return lua_yield(L, 0); +} + + +static void +ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_stream_lua_request_t *r; +#if (NGX_DEBUG) + ngx_connection_t *c; +#endif + lua_State *L; + u_char *p; + size_t len; + socklen_t socklen; + struct sockaddr *sockaddr; + ngx_uint_t i; + unsigned waiting; + + ngx_stream_upstream_resolved_t *ur; + ngx_stream_lua_ctx_t *lctx; + ngx_stream_lua_socket_udp_upstream_t *u; + + u = ctx->data; + r = u->request; + +#if (NGX_DEBUG) + + c = r->connection; + +#endif + + ur = u->resolved; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua udp socket resolve handler"); + + lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (lctx == NULL) { + return; + } + + lctx->cur_co_ctx = u->co_ctx; + + u->co_ctx->cleanup = NULL; + + L = lctx->cur_co_ctx->co; + + dd("setting socket_ready to 1"); + + waiting = u->waiting; + + if (ctx->state) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua udp socket resolver error: %s (waiting: %d)", + ngx_resolver_strerror(ctx->state), (int) u->waiting); + + lua_pushnil(L); + lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); + lua_pushfstring(L, " could not be resolved (%d: %s)", + (int) ctx->state, + ngx_resolver_strerror(ctx->state)); + lua_concat(L, 2); + +#if 1 + ngx_resolve_name_done(ctx); + ur->ctx = NULL; +#endif + + u->prepare_retvals = ngx_stream_lua_socket_error_retval_handler; + ngx_stream_lua_socket_udp_handle_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_RESOLVER); + + + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + ngx_stream_lua_assert(ur->naddrs > 0); + + if (ur->naddrs == 1) { + i = 0; + + } else { + i = ngx_random() % ur->naddrs; + } + + dd("selected addr index: %d", (int) i); + + socklen = ur->addrs[i].socklen; + + sockaddr = ngx_palloc(r->pool, socklen); + if (sockaddr == NULL) { + goto nomem; + } + + ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); + + switch (sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); + break; +#endif + default: /* AF_INET */ + ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); + } + + p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + goto nomem; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + ur->sockaddr = sockaddr; + ur->socklen = socklen; + ur->host.data = p; + ur->host.len = len; + ur->naddrs = 1; + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + u->waiting = 0; + + if (waiting) { + lctx->resume_handler = ngx_stream_lua_socket_udp_resume; + r->write_event_handler(r); + + + } else { + (void) ngx_stream_lua_socket_resolve_retval_handler(r, u, L); + } + + return; + +nomem: + + if (ur->ctx) { + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + } + + u->prepare_retvals = ngx_stream_lua_socket_error_retval_handler; + ngx_stream_lua_socket_udp_handle_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_NOMEM); + + if (waiting) { + + } else { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + } +} + + +static int +ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_connection_t *c; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_upstream_resolved_t *ur; + ngx_int_t rc; + ngx_stream_lua_udp_connection_t *uc; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket resolve retval handler"); + + if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_RESOLVER) { + return 2; + } + + uc = &u->udp_connection; + + ur = u->resolved; + + if (ur->sockaddr) { + uc->sockaddr = ur->sockaddr; + uc->socklen = ur->socklen; + uc->server = ur->host; + + } else { + lua_pushnil(L); + lua_pushliteral(L, "resolver not working"); + return 2; + } + + rc = ngx_stream_lua_udp_connect(uc); + + if (rc != NGX_OK) { + u->socket_errno = ngx_socket_errno; + } + + if (u->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + cln->handler = ngx_stream_lua_socket_udp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket connect: %i", rc); + + if (rc != NGX_OK) { + return ngx_stream_lua_socket_error_retval_handler(r, u, L); + } + + /* rc == NGX_OK */ + + c = uc->connection; + + c->data = u; + + c->write->handler = NULL; + c->read->handler = ngx_stream_lua_socket_udp_handler; + c->read->resolver = 0; + + c->pool = r->pool; + c->log = r->connection->log; + c->read->log = c->log; + c->write->log = c->log; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + coctx = ctx->cur_co_ctx; + + coctx->data = u; + + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_stream_lua_socket_error_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L) +{ + u_char errstr[NGX_MAX_ERROR_STR]; + u_char *p; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket error retval handler"); + + if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_RESOLVER) { + return 2; + } + + lua_pushnil(L); + + if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE) { + lua_pushliteral(L, "partial write"); + + } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_TIMEOUT) { + lua_pushliteral(L, "timeout"); + + } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_CLOSED) { + lua_pushliteral(L, "closed"); + + } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_BUFTOOSMALL) { + lua_pushliteral(L, "buffer too small"); + + } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_NOMEM) { + lua_pushliteral(L, "no memory"); + + } else { + + if (u->socket_errno) { + p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); + /* for compatibility with LuaSocket */ + ngx_strlow(errstr, errstr, p - errstr); + lua_pushlstring(L, (char *) errstr, p - errstr); + + } else { + lua_pushliteral(L, "error"); + } + } + + return 2; +} + + +static int +ngx_stream_lua_socket_udp_send(lua_State *L) +{ + ssize_t n; + ngx_stream_lua_request_t *r; + u_char *p; + size_t len; + int type; + const char *msg; + ngx_str_t query; +#ifndef NGX_WIN32 + ngx_iovec_t vec; + struct iovec iovs[1]; +#endif + + ngx_stream_lua_socket_udp_upstream_t *u; + ngx_stream_lua_loc_conf_t *llcf; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments (including the object), " + "but got %d", lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "request object not found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->udp_connection.connection == NULL) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to send data on a closed socket: u:%p, c:%p", + u, u ? u->udp_connection.connection : NULL); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + if (u->ft_type) { + u->ft_type = 0; + } + + if (u->waiting) { + lua_pushnil(L); + lua_pushliteral(L, "socket busy"); + return 2; + } + + type = lua_type(L, 2); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_tolstring(L, 2, &len); + break; + + case LUA_TTABLE: + len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); + break; + + case LUA_TNIL: + len = sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + len = sizeof("true") - 1; + + } else { + len = sizeof("false") - 1; + } + + break; + + default: + msg = lua_pushfstring(L, "string, number, boolean, nil, " + "or array table expected, got %s", + lua_typename(L, type)); + + return luaL_argerror(L, 2, msg); + } + + query.data = lua_newuserdata(L, len); + query.len = len; + + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) lua_tolstring(L, 2, &len); + ngx_memcpy(query.data, (u_char *) p, len); + break; + + case LUA_TTABLE: + (void) ngx_stream_lua_copy_str_in_table(L, 2, query.data); + break; + + case LUA_TNIL: + p = query.data; + *p++ = 'n'; + *p++ = 'i'; + *p++ = 'l'; + break; + + case LUA_TBOOLEAN: + p = query.data; + + if (lua_toboolean(L, 2)) { + *p++ = 't'; + *p++ = 'r'; + *p++ = 'u'; + *p++ = 'e'; + + } else { + *p++ = 'f'; + *p++ = 'a'; + *p++ = 'l'; + *p++ = 's'; + *p++ = 'e'; + } + + break; + + default: + return luaL_error(L, "impossible to reach here"); + } + + u->ft_type = 0; + + /* mimic ngx_http_upstream_init_request here */ + +#if 1 + u->waiting = 0; +#endif + + dd("sending query %.*s", (int) query.len, query.data); +#ifdef NGX_WIN32 + n = ngx_udp_send(u->udp_connection.connection, query.data, query.len); + dd("ngx_udp_send returns %d (query len %d)", (int) n, (int) query.len); + +#else + vec.iovs = iovs; + vec.nalloc = 1; + vec.count = 1; + iovs[0].iov_base = query.data; + iovs[0].iov_len = query.len; + vec.size = query.len; + n = ngx_stream_lua_udp_sendmsg(u->udp_connection.connection, &vec); + + dd("ngx_stream_lua_udp_sendmsg returns %d (query len %d)", + (int) n, (int) query.len); +#endif + + if (n == NGX_ERROR || n == NGX_AGAIN) { + u->socket_errno = ngx_socket_errno; + + return ngx_stream_lua_socket_error_retval_handler(r, u, L); + } + + if (n != (ssize_t) query.len) { + dd("not the while query was sent"); + + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE; + return ngx_stream_lua_socket_error_retval_handler(r, u, L); + } + + dd("n == len"); + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_stream_lua_socket_udp_receive(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_int_t rc; + size_t size; + int nargs; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_udp_upstream_t *u; + ngx_stream_lua_loc_conf_t *llcf; + + nargs = lua_gettop(L); + if (nargs != 1 && nargs != 2) { + return luaL_error(L, "expecting 1 or 2 arguments " + "(including the object), but got %d", nargs); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket calling receive() method"); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->udp_connection.connection == NULL) { + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to receive data on a closed socket: u:%p, " + "c:%p", u, u ? u->udp_connection.connection : NULL); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + if (u->ft_type) { + u->ft_type = 0; + } + +#if 1 + if (u->waiting) { + lua_pushnil(L); + lua_pushliteral(L, "socket busy"); + return 2; + } +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket read timeout: %M", u->read_timeout); + + size = (size_t) luaL_optnumber(L, 2, UDP_MAX_DATAGRAM_SIZE); + size = ngx_min(size, UDP_MAX_DATAGRAM_SIZE); + + u->recv_buf_size = size; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket receive buffer size: %uz", u->recv_buf_size); + + if (u->raw_downstream) { + if (ngx_buf_size(r->connection->buffer) > 0) { + /* we still have unread data */ + u->received = ngx_min((size_t) ngx_buf_size(r->connection->buffer), + u->recv_buf_size); + ngx_memcpy(ngx_stream_lua_socket_udp_buffer, + r->connection->buffer->pos, u->received); + r->connection->buffer->pos += u->received; + + ngx_stream_lua_socket_udp_handle_success(r, u); + + rc = NGX_OK; + + } else { + lua_pushnil(L); + lua_pushliteral(L, "no more data"); + return 2; + } + + } else { + rc = ngx_stream_lua_socket_udp_read(r, u); + } + + if (rc == NGX_ERROR) { + dd("read failed: %d", (int) u->ft_type); + rc = ngx_stream_lua_socket_udp_receive_retval_handler(r, u, L); + dd("udp receive retval returned: %d", (int) rc); + return rc; + } + + if (rc == NGX_OK) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket receive done in a single run"); + + return ngx_stream_lua_socket_udp_receive_retval_handler(r, u, L); + } + + /* n == NGX_AGAIN */ + + u->read_event_handler = ngx_stream_lua_socket_udp_read_handler; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + coctx = ctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_udp_socket_cleanup; + coctx->data = u; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + u->co_ctx = coctx; + u->waiting = 1; + u->prepare_retvals = ngx_stream_lua_socket_udp_receive_retval_handler; + + return lua_yield(L, 0); +} + + +static int +ngx_stream_lua_socket_udp_receive_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket receive return value handler"); + + if (u->ft_type) { + return ngx_stream_lua_socket_error_retval_handler(r, u, L); + } + + lua_pushlstring(L, (char *) ngx_stream_lua_socket_udp_buffer, u->received); + return 1; +} + + +static int +ngx_stream_lua_socket_udp_settimeout(lua_State *L) +{ + int n; + ngx_int_t timeout; + + ngx_stream_lua_socket_udp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "ngx.socket settimout: expecting at least 2 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + + lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (timeout > 0) { + u->read_timeout = (ngx_msec_t) timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + } + } + + return 0; +} + + +static void +ngx_stream_lua_socket_udp_finalize(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua finalize socket"); + + if (u->cleanup) { + *u->cleanup = NULL; + u->cleanup = NULL; + } + + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + /* + * do not close if it is a downstream connection as that will + * be handled by stream subsystem itself + */ + if (u->udp_connection.connection && !u->raw_downstream) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua close socket connection"); + + ngx_close_connection(u->udp_connection.connection); + u->udp_connection.connection = NULL; + } + + if (u->waiting) { + u->waiting = 0; + } +} + + +static int +ngx_stream_lua_socket_udp_upstream_destroy(lua_State *L) +{ + ngx_stream_lua_socket_udp_upstream_t *u; + + dd("upstream destroy triggered by Lua GC"); + + u = lua_touserdata(L, 1); + if (u == NULL) { + return 0; + } + + if (u->cleanup) { + ngx_stream_lua_socket_udp_cleanup(u); /* it will clear u->cleanup */ + } + + return 0; +} + + +static void +ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket dummy handler"); +} + + +static ngx_int_t +ngx_stream_lua_socket_udp_read(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_event_t *rev; + ssize_t n; + + c = u->udp_connection.connection; + rev = c->read; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua udp socket read data: waiting: %d", (int) u->waiting); + + n = ngx_udp_recv(u->udp_connection.connection, + ngx_stream_lua_socket_udp_buffer, u->recv_buf_size); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua udp recv returned %z", n); + + if (n >= 0) { + u->received = n; + ngx_stream_lua_socket_udp_handle_success(r, u); + return NGX_OK; + } + + if (n == NGX_ERROR) { + u->socket_errno = ngx_socket_errno; + ngx_stream_lua_socket_udp_handle_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + /* n == NGX_AGAIN */ + +#if 1 + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_lua_socket_udp_handle_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } +#endif + + if (rev->active) { + ngx_add_timer(rev, u->read_timeout); + + } else if (rev->timer_set) { + ngx_del_timer(rev); + } + + return NGX_AGAIN; +} + + +static void +ngx_stream_lua_socket_udp_read_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u) +{ + ngx_connection_t *c; + + ngx_stream_lua_loc_conf_t *llcf; + + c = u->udp_connection.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket read handler"); + + if (c->read->timedout) { + c->read->timedout = 0; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua udp socket read timed out"); + } + + ngx_stream_lua_socket_udp_handle_error(r, u, + NGX_STREAM_LUA_SOCKET_FT_TIMEOUT); + return; + } + +#if 1 + if (c->read->timer_set) { + ngx_del_timer(c->read); + } +#endif + + (void) ngx_stream_lua_socket_udp_read(r, u); +} + + +static void +ngx_stream_lua_socket_udp_handle_error(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket handle error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_stream_lua_socket_udp_finalize(r, u); +#endif + + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + + coctx = u->co_ctx; + + if (coctx) { + coctx->cleanup = NULL; + } + + if (u->waiting) { + u->waiting = 0; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_stream_lua_socket_udp_resume; + ctx->cur_co_ctx = coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_stream_lua_socket_udp_cleanup(void *data) +{ + ngx_stream_lua_socket_udp_upstream_t *u = data; + + ngx_stream_lua_request_t *r; + + r = u->request; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "cleanup lua udp socket upstream request"); + + ngx_stream_lua_socket_udp_finalize(r, u); +} + + +static void +ngx_stream_lua_socket_udp_handler(ngx_event_t *ev) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_socket_udp_upstream_t *u; + ngx_connection_t *c; + + c = ev->data; + u = c->data; + r = u->request; + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua udp socket handler: wev %d", (int) ev->write); + + u->read_event_handler(r, u); + +} + + +static void +ngx_stream_lua_socket_udp_handle_success(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u) +{ + ngx_stream_lua_ctx_t *ctx; + + u->read_event_handler = ngx_stream_lua_socket_dummy_handler; + + if (u->co_ctx) { + u->co_ctx->cleanup = NULL; + } + + if (u->waiting) { + u->waiting = 0; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_stream_lua_socket_udp_resume; + ctx->cur_co_ctx = u->co_ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static ngx_int_t +ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) +{ + int rc; + ngx_int_t event; + ngx_event_t *rev, *wev; + ngx_socket_t s; + ngx_connection_t *c; + + s = ngx_socket(uc->sockaddr->sa_family, SOCK_DGRAM, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s); + + if (s == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_socket_n " failed"); + + return NGX_ERROR; + } + + c = ngx_get_connection(s, &uc->log); + + if (c == NULL) { + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_close_socket_n "failed"); + } + + return NGX_ERROR; + } + + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + ngx_free_connection(c); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + return NGX_ERROR; + } + + rev = c->read; + wev = c->write; + + rev->log = &uc->log; + wev->log = &uc->log; + + uc->connection = c; + + c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + +#if (NGX_STREAM_LUA_HAVE_SO_PASSCRED) + if (uc->sockaddr->sa_family == AF_UNIX) { + struct sockaddr addr; + + addr.sa_family = AF_UNIX; + + /* just to make valgrind happy */ + ngx_memzero(addr.sa_data, sizeof(addr.sa_data)); + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "datagram unix " + "domain socket autobind"); + + if (bind(uc->connection->fd, &addr, sizeof(sa_family_t)) != 0) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, + "bind() failed"); + + return NGX_ERROR; + } + } +#endif + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, + "connect to %V, fd:%d #%d", &uc->server, s, c->number); + + rc = connect(s, uc->sockaddr, uc->socklen); + + /* TODO: aio, iocp */ + + if (rc == -1) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, + "connect() failed"); + + return NGX_ERROR; + } + + /* UDP sockets are always ready to write */ + wev->ready = 1; + + if (ngx_add_event) { + + event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? + /* kqueue, epoll */ NGX_CLEAR_EVENT: + /* select, poll, /dev/poll */ NGX_LEVEL_EVENT; + /* eventport event type has no meaning: oneshot only */ + + if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { + return NGX_ERROR; + } + + } else { + /* rtsig */ + + if (ngx_add_conn(c) == NGX_ERROR) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static int +ngx_stream_lua_socket_udp_close(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_socket_udp_upstream_t *u; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument " + "(including the object) but seen %d", lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->udp_connection.connection == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + if (u->waiting) { + lua_pushnil(L); + lua_pushliteral(L, "socket busy"); + return 2; + } + + ngx_stream_lua_socket_udp_finalize(r, u); + + lua_pushinteger(L, 1); + return 1; +} + + +static ngx_int_t +ngx_stream_lua_socket_udp_resume(ngx_stream_lua_request_t *r) +{ + int nret; + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_stream_lua_socket_udp_upstream_t *u; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_stream_lua_wev_handler; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp operation done, resuming lua thread"); + + coctx = ctx->cur_co_ctx; + +#if 0 + ngx_stream_lua_probe_info("udp resume"); +#endif + + u = coctx->data; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua udp socket calling prepare retvals handler %p, " + "u:%p", u->prepare_retvals, u); + + nret = u->prepare_retvals(r, u, ctx->cur_co_ctx->co); + if (nret == NGX_AGAIN) { + return NGX_DONE; + } + + c = r->connection; + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_stream_lua_run_thread(vm, r, ctx, nret); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +static void +ngx_stream_lua_udp_resolve_cleanup(void *data) +{ + ngx_resolver_ctx_t *rctx; + + ngx_stream_lua_socket_udp_upstream_t *u; + ngx_stream_lua_co_ctx_t *coctx = data; + + u = coctx->data; + if (u == NULL) { + return; + } + + rctx = u->resolved->ctx; + if (rctx == NULL) { + return; + } + + /* postpone free the rctx in the handler */ + rctx->handler = ngx_resolve_name_done; +} + + +static void +ngx_stream_lua_udp_socket_cleanup(void *data) +{ + ngx_stream_lua_socket_udp_upstream_t *u; + ngx_stream_lua_co_ctx_t *coctx = data; + + u = coctx->data; + if (u == NULL) { + return; + } + + if (u->request == NULL) { + return; + } + + ngx_stream_lua_socket_udp_finalize(u->request, u); +} + + +int +ngx_stream_lua_req_socket_udp(lua_State *L) +{ + int n; + ngx_stream_lua_udp_connection_t *pc; + ngx_stream_lua_srv_conf_t *lscf; + ngx_connection_t *c; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_co_ctx_t *coctx; + + ngx_stream_lua_socket_udp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 0 && n != 1) { + return luaL_error(L, "expecting zero arguments, but got %d", + lua_gettop(L)); + } + + if (n == 1) { + lua_pop(L, 1); + } + + r = ngx_stream_lua_get_req(L); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + |NGX_STREAM_LUA_CONTEXT_PREREAD); + + c = r->connection; + + if (c->buffered) { + lua_pushnil(L); + lua_pushliteral(L, "pending data to write"); + return 2; + } + + dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket); + + if (ctx->acquired_raw_req_socket) { + lua_pushnil(L); + lua_pushliteral(L, "duplicate call"); + return 2; + } + + ctx->acquired_raw_req_socket = 1; + + lua_createtable(L, 3 /* narr */, 1 /* nrec */); /* the object */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_udp_raw_req_socket_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_udp_upstream_t)); + if (u == NULL) { + return luaL_error(L, "no memory"); + } + +#if 1 + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_udp_downstream_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +#endif + + lua_rawseti(L, 1, SOCKET_CTX_INDEX); + + ngx_memzero(u, sizeof(ngx_stream_lua_socket_udp_upstream_t)); + + u->raw_downstream = 1; + + coctx = ctx->cur_co_ctx; + + u->request = r; + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + u->conf = lscf; + + u->read_timeout = u->conf->read_timeout; + + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + cln->handler = ngx_stream_lua_socket_udp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + + pc = &u->udp_connection; + pc->log = *c->log; + pc->connection = c; + + dd("setting data to %p", u); + + coctx->data = u; + ctx->downstream = u; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + lua_settop(L, 1); + return 1; +} + + +#ifndef NGX_WIN32 + +static ssize_t +ngx_stream_lua_udp_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec) +{ + ssize_t n; + ngx_err_t err; + struct msghdr msg; + +#if (NGX_HAVE_MSGHDR_MSG_CONTROL) + +#if (NGX_HAVE_IP_SENDSRCADDR) + u_char msg_control[CMSG_SPACE(sizeof(struct in_addr))]; +#elif (NGX_HAVE_IP_PKTINFO) + u_char msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; +#endif + +#endif + + ngx_memzero(&msg, sizeof(struct msghdr)); + + if (c->socklen) { + msg.msg_name = c->sockaddr; + msg.msg_namelen = c->socklen; + } + + msg.msg_iov = vec->iovs; + msg.msg_iovlen = vec->count; + +#if (NGX_HAVE_MSGHDR_MSG_CONTROL) + + if (c->listening && c->listening->wildcard && c->local_sockaddr) { + +#if (NGX_HAVE_IP_SENDSRCADDR) + + if (c->local_sockaddr->sa_family == AF_INET) { + struct cmsghdr *cmsg; + struct in_addr *addr; + struct sockaddr_in *sin; + + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + + sin = (struct sockaddr_in *) c->local_sockaddr; + + addr = (struct in_addr *) CMSG_DATA(cmsg); + *addr = sin->sin_addr; + } + +#elif (NGX_HAVE_IP_PKTINFO) + + if (c->local_sockaddr->sa_family == AF_INET) { + struct cmsghdr *cmsg; + struct in_pktinfo *pkt; + struct sockaddr_in *sin; + + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + sin = (struct sockaddr_in *) c->local_sockaddr; + + pkt = (struct in_pktinfo *) CMSG_DATA(cmsg); + ngx_memzero(pkt, sizeof(struct in_pktinfo)); + pkt->ipi_spec_dst = sin->sin_addr; + } + +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + + if (c->local_sockaddr->sa_family == AF_INET6) { + struct cmsghdr *cmsg; + struct in6_pktinfo *pkt6; + struct sockaddr_in6 *sin6; + + msg.msg_control = &msg_control6; + msg.msg_controllen = sizeof(msg_control6); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + ngx_memzero(pkt6, sizeof(struct in6_pktinfo)); + pkt6->ipi6_addr = sin6->sin6_addr; + } + +#endif + } + +#endif + +eintr: + + n = sendmsg(c->fd, &msg, 0); + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendto: fd:%d %z of %uz to \"%V\"", + c->fd, n, vec->size, &c->addr_text); + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() not ready"); + return NGX_AGAIN; + + case NGX_EINTR: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() was interrupted"); + goto eintr; + + default: + c->write->error = 1; + ngx_connection_error(c, err, "sendmsg() failed"); + return NGX_ERROR; + } + } + + return n; +} + +#endif + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_udp.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_udp.h new file mode 100644 index 000000000..abcc76d30 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_socket_udp.h @@ -0,0 +1,76 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_socket_udp.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SOCKET_UDP_H_INCLUDED_ +#define _NGX_STREAM_LUA_SOCKET_UDP_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +typedef struct ngx_stream_lua_socket_udp_upstream_s + ngx_stream_lua_socket_udp_upstream_t; + + +typedef + int (*ngx_stream_lua_socket_udp_retval_handler)(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L); + + +typedef void (*ngx_stream_lua_socket_udp_upstream_handler_pt) + (ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u); + + +typedef struct { + ngx_connection_t *connection; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t server; + ngx_log_t log; +} ngx_stream_lua_udp_connection_t; + + +struct ngx_stream_lua_socket_udp_upstream_s { + ngx_stream_lua_socket_udp_retval_handler prepare_retvals; + ngx_stream_lua_socket_udp_upstream_handler_pt read_event_handler; + + ngx_stream_lua_loc_conf_t *conf; + ngx_stream_lua_cleanup_pt *cleanup; + ngx_stream_lua_request_t *request; + ngx_stream_lua_udp_connection_t udp_connection; + + ngx_msec_t read_timeout; + + ngx_stream_upstream_resolved_t *resolved; + + ngx_uint_t ft_type; + ngx_err_t socket_errno; + size_t received; /* for receive */ + size_t recv_buf_size; + + ngx_stream_lua_co_ctx_t *co_ctx; + + unsigned waiting:1; + + unsigned raw_downstream:1; +}; + + +void ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L); +int ngx_stream_lua_req_socket_udp(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_SOCKET_UDP_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl.c new file mode 100644 index 000000000..2a6a951da --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl.c @@ -0,0 +1,47 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_ssl.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_STREAM_SSL) + + +int ngx_stream_lua_ssl_ctx_index = -1; + + +ngx_int_t +ngx_stream_lua_ssl_init(ngx_log_t *log) +{ + if (ngx_stream_lua_ssl_ctx_index == -1) { + ngx_stream_lua_ssl_ctx_index = SSL_get_ex_new_index(0, NULL, + NULL, + NULL, + NULL); + + if (ngx_stream_lua_ssl_ctx_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "lua: SSL_get_ex_new_index() for ctx failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +#endif /* NGX_STREAM_SSL */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl.h new file mode 100644 index 000000000..352b709ea --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl.h @@ -0,0 +1,61 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_ssl.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SSL_H_INCLUDED_ +#define _NGX_STREAM_LUA_SSL_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +#if (NGX_STREAM_SSL) + + +typedef struct { + ngx_connection_t *connection; /* original true connection */ + ngx_stream_lua_request_t *request; /* fake request */ + ngx_pool_cleanup_pt *cleanup; + + ngx_ssl_session_t *session; /* retrurn value for openssl's + * session_get_cb */ + + ngx_str_t session_id; + + int exit_code; /* exit code for openssl's + set_client_hello_cb or + set_cert_cb callback */ + + int ctx_ref; /* reference to anchor + request ctx data in lua + registry */ + + unsigned done:1; + unsigned aborted:1; + + unsigned entered_client_hello_handler:1; + unsigned entered_cert_handler:1; + unsigned entered_sess_fetch_handler:1; +} ngx_stream_lua_ssl_ctx_t; + + +ngx_int_t ngx_stream_lua_ssl_init(ngx_log_t *log); + + +extern int ngx_stream_lua_ssl_ctx_index; + + +#endif + + +#endif /* _NGX_STREAM_LUA_SSL_H_INCLUDED_ */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_certby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_certby.c new file mode 100644 index 000000000..7b4cc5bfa --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_certby.c @@ -0,0 +1,1506 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_ssl_certby.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_STREAM_SSL) + + +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_initworkerby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_ssl_module.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_ssl_certby.h" +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_ssl.h" + + +enum { + NGX_STREAM_LUA_ADDR_TYPE_UNIX = 0, + NGX_STREAM_LUA_ADDR_TYPE_INET = 1, + NGX_STREAM_LUA_ADDR_TYPE_INET6 = 2 +}; + + +static void ngx_stream_lua_ssl_cert_done(void *data); +static void ngx_stream_lua_ssl_cert_aborted(void *data); +static u_char *ngx_stream_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, + size_t len); +static ngx_int_t ngx_stream_lua_ssl_cert_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); + + +ngx_int_t +ngx_stream_lua_ssl_cert_handler_file(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, + lscf->srv.ssl_cert_src.data, + lscf->srv.ssl_cert_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_ssl_cert_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_ssl_cert_handler_inline(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + lscf->srv.ssl_cert_src.data, + lscf->srv.ssl_cert_src.len, + lscf->srv.ssl_cert_src_key, + "=ssl_certificate_by_lua"); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_ssl_cert_by_chunk(L, r); +} + + +char * +ngx_stream_lua_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_ssl_cert_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#if OPENSSL_VERSION_NUMBER < 0x1000205fL + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 1.0.2e required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + u_char *p; + u_char *name; + ngx_str_t *value; + ngx_stream_lua_srv_conf_t *lscf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.ssl_cert_handler) { + return "is duplicate"; + } + + if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + lscf->srv.ssl_cert_handler = (ngx_stream_lua_srv_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_stream_lua_ssl_cert_handler_file) { + /* Lua code in an external file */ + + name = ngx_stream_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_cert_src.data = name; + lscf->srv.ssl_cert_src.len = ngx_strlen(name); + + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_cert_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + /* inlined Lua code */ + + lscf->srv.ssl_cert_src = value[1]; + + p = ngx_palloc(cf->pool, + sizeof("ssl_certificate_by_lua") + + NGX_STREAM_LUA_INLINE_KEY_LEN); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_cert_src_key = p; + + p = ngx_copy(p, "ssl_certificate_by_lua", + sizeof("ssl_certificate_by_lua") - 1); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + + return NGX_CONF_OK; + +#endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +} + + +int +ngx_stream_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_stream_lua_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_ssl_ctx_t *cctx; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_session_t *s, *fs; + + c = ngx_ssl_get_connection(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream ssl cert: connection reusable: %ud", c->reusable); + + cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection); + + dd("ssl cert handler, cert-ctx=%p", cctx); + + if (cctx && cctx->entered_cert_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua_certificate_by_lua:" + " cert cb exit code: %d", + cctx->exit_code); + + dd("lua ssl cert done, finally"); + return cctx->exit_code; + } + + return -1; + } + + dd("first time"); + + ngx_reusable_connection(c, 0); + + s = c->data; + + fc = ngx_stream_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_stream_lua_log_ssl_cert_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + fs = ngx_stream_lua_create_fake_session(fc); + if (fs == NULL) { + goto failed; + } + + fs->main_conf = s->main_conf; + fs->srv_conf = s->srv_conf; + + r = ngx_stream_lua_create_fake_request(fs); + if (r == NULL) { + goto failed; + } + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + cscf = ngx_stream_get_module_srv_conf(fs, ngx_stream_core_module); + +#if defined(nginx_version) && nginx_version >= 1009000 + ngx_set_connection_log(fc, cscf->error_log); + +#else +# error "stream ssl_cert_by_lua only supports nginx >= 1.13.0" +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + + cctx->ctx_ref = LUA_NOREF; + } + + cctx->exit_code = 1; /* successful by default */ + cctx->connection = c; + cctx->request = r; + cctx->entered_cert_handler = 1; + cctx->done = 0; + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_stream_lua_ssl_ctx_index, + cctx) == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_stream_lua_get_lua_vm(r, NULL); + + c->log->action = "loading SSL certificate by lua"; + + if (lscf->srv.ssl_cert_handler == NULL) { + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "no ssl_certificate_by_lua* defined in " + "server %s:%ui", &cscf->file_name, &cscf->line); + + goto failed; + } + + rc = lscf->srv.ssl_cert_handler(r, lscf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua_certificate_by_lua:" + " handler return value: %i, " + "cert cb exit code: %d", rc, cctx->exit_code); + + c->log->action = "SSL handshaking"; + return cctx->exit_code; + } + + /* rc == NGX_DONE */ + + cln = ngx_pool_cleanup_add(fc->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_stream_lua_ssl_cert_done; + cln->data = cctx; + + if (cctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = cctx; + cctx->cleanup = &cln->handler; + } + + *cctx->cleanup = ngx_stream_lua_ssl_cert_aborted; + + return -1; + +#if 1 +failed: + + if (r && r->pool) { + ngx_stream_lua_free_fake_request(r); + } + + if (fc) { + ngx_stream_lua_close_fake_connection(fc); + } + + return 0; +#endif +} + + +static void +ngx_stream_lua_ssl_cert_done(void *data) +{ + ngx_connection_t *c; + ngx_stream_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl cert done"); + + if (cctx->aborted) { + return; + } + + ngx_stream_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + c->log->action = "SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); +} + + +static void +ngx_stream_lua_ssl_cert_aborted(void *data) +{ + ngx_stream_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl cert done"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cctx->connection->log, 0, + "stream lua_certificate_by_lua: cert cb aborted"); + + cctx->aborted = 1; + cctx->request->connection->ssl = NULL; + + ngx_stream_lua_finalize_fake_request(cctx->request, NGX_ERROR); +} + + +static u_char * +ngx_stream_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + p = ngx_snprintf(buf, len, ", context: ssl_certificate_by_lua*"); + len -= p - buf; + buf = p; + + c = log->data; + + if (c != NULL) { + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", + &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + } + + return buf; +} + + +static ngx_int_t +ngx_stream_lua_ssl_cert_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(r->session); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_stream_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_stream_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream failed to create new" + " coroutine to handle request"); + + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_stream_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_STREAM_LUA_CONTEXT_SSL_CERT; + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_stream_lua_finalize_request(r, rc); + return rc; +} + + +int +ngx_stream_lua_ffi_ssl_get_tls1_version(ngx_stream_lua_request_t *r, char **err) +{ + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + dd("tls1 ver: %d", SSL_version(ssl_conn)); + + return SSL_version(ssl_conn); +} + + +int +ngx_stream_lua_ffi_ssl_clear_certs(ngx_stream_lua_request_t *r, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER < 0x1000205fL + + *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT; + return NGX_ERROR; + +# else + + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + SSL_certs_clear(ssl_conn); + return NGX_OK; + +# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +#endif +} + + +int +ngx_stream_lua_ffi_ssl_set_der_certificate(ngx_stream_lua_request_t *r, + const char *data, size_t len, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER < 0x1000205fL + + *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT; + return NGX_ERROR; + +# else + + BIO *bio = NULL; + X509 *x509 = NULL; + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + bio = BIO_new_mem_buf((char *) data, len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + goto failed; + } + + x509 = d2i_X509_bio(bio, NULL); + if (x509 == NULL) { + *err = "d2i_X509_bio() failed"; + goto failed; + } + + if (SSL_use_certificate(ssl_conn, x509) == 0) { + *err = "SSL_use_certificate() failed"; + goto failed; + } + +#if 0 + if (SSL_set_ex_data(ssl_conn, ngx_ssl_certificate_index, x509) == 0) { + *err = "SSL_set_ex_data() failed"; + goto failed; + } +#endif + + X509_free(x509); + x509 = NULL; + + /* read rest of the chain */ + + while (!BIO_eof(bio)) { + + x509 = d2i_X509_bio(bio, NULL); + if (x509 == NULL) { + *err = "d2i_X509_bio() failed"; + goto failed; + } + + if (SSL_add0_chain_cert(ssl_conn, x509) == 0) { + *err = "SSL_add0_chain_cert() failed"; + goto failed; + } + } + + BIO_free(bio); + + *err = NULL; + return NGX_OK; + +failed: + + if (bio) { + BIO_free(bio); + } + + if (x509) { + X509_free(x509); + } + + ERR_clear_error(); + + return NGX_ERROR; + +# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +#endif +} + + +int +ngx_stream_lua_ffi_ssl_set_der_private_key(ngx_stream_lua_request_t *r, + const char *data, size_t len, char **err) +{ + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + bio = BIO_new_mem_buf((char *) data, len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + goto failed; + } + + pkey = d2i_PrivateKey_bio(bio, NULL); + if (pkey == NULL) { + *err = "d2i_PrivateKey_bio() failed"; + goto failed; + } + + if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) { + *err = "SSL_use_PrivateKey() failed"; + goto failed; + } + + EVP_PKEY_free(pkey); + BIO_free(bio); + + return NGX_OK; + +failed: + + if (pkey) { + EVP_PKEY_free(pkey); + } + + if (bio) { + BIO_free(bio); + } + + ERR_clear_error(); + + return NGX_ERROR; +} + + +int +ngx_stream_lua_ffi_ssl_raw_server_addr(ngx_stream_lua_request_t *r, char **addr, + size_t *addrlen, int *addrtype, char **err) +{ +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun; +#endif + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + c = ngx_ssl_get_connection(ssl_conn); + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + return 0; + } + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + *addrlen = 16; + *addr = (char *) &sin6->sin6_addr.s6_addr; + *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET6; + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + saun = (struct sockaddr_un *) c->local_sockaddr; + + /* on Linux sockaddr might not include sun_path at all */ + if (c->local_socklen <= (socklen_t) + offsetof(struct sockaddr_un, sun_path)) + { + *addr = ""; + *addrlen = 0; + + } else { + *addr = saun->sun_path; + *addrlen = ngx_strlen(saun->sun_path); + } + + *addrtype = NGX_STREAM_LUA_ADDR_TYPE_UNIX; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + *addr = (char *) &sin->sin_addr.s_addr; + *addrlen = 4; + *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET; + break; + } + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_ssl_server_name(ngx_stream_lua_request_t *r, char **name, + size_t *namelen, char **err) +{ + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + + *name = (char *) SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + + if (*name) { + *namelen = ngx_strlen(*name); + return NGX_OK; + } + + return NGX_DECLINED; + +#else + + *err = "no TLS extension support"; + return NGX_ERROR; + +#endif +} + + +int +ngx_stream_lua_ffi_ssl_server_port(ngx_stream_lua_request_t *r, + unsigned short *server_port, char **err) +{ + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + c = ngx_ssl_get_connection(ssl_conn); + + switch (c->local_sockaddr->sa_family) { + + case AF_UNIX: + *err = "unix domain has no port"; + return NGX_ERROR; + + default: + *server_port = (unsigned short) ngx_inet_get_port(c->local_sockaddr); + return NGX_OK; + } +} + + +int +ngx_stream_lua_ffi_ssl_raw_client_addr(ngx_stream_lua_request_t *r, char **addr, + size_t *addrlen, int *addrtype, char **err) +{ +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun; +#endif + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + c = ngx_ssl_get_connection(ssl_conn); + + switch (c->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->sockaddr; + *addrlen = 16; + *addr = (char *) &sin6->sin6_addr.s6_addr; + *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET6; + + break; +#endif + +# if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + saun = (struct sockaddr_un *)c->sockaddr; + /* on Linux sockaddr might not include sun_path at all */ + if (c->socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) { + *addr = ""; + *addrlen = 0; + + } else { + *addr = saun->sun_path; + *addrlen = ngx_strlen(saun->sun_path); + } + + *addrtype = NGX_STREAM_LUA_ADDR_TYPE_UNIX; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->sockaddr; + *addr = (char *) &sin->sin_addr.s_addr; + *addrlen = 4; + *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET; + break; + } + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_cert_pem_to_der(const u_char *pem, size_t pem_len, u_char *der, + char **err) +{ + int total, len; + BIO *bio; + X509 *x509; + u_long n; + + bio = BIO_new_mem_buf((char *) pem, (int) pem_len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + ERR_clear_error(); + return NGX_ERROR; + } + + x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (x509 == NULL) { + *err = "PEM_read_bio_X509_AUX() failed"; + BIO_free(bio); + ERR_clear_error(); + return NGX_ERROR; + } + + total = i2d_X509(x509, &der); + if (total < 0) { + *err = "i2d_X509() failed"; + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NGX_ERROR; + } + + X509_free(x509); + + /* read rest of the chain */ + + for ( ;; ) { + + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509 == NULL) { + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_PEM + && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509() failed"; + BIO_free(bio); + ERR_clear_error(); + return NGX_ERROR; + } + + len = i2d_X509(x509, &der); + if (len < 0) { + *err = "i2d_X509() failed"; + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NGX_ERROR; + } + + total += len; + + X509_free(x509); + } + + BIO_free(bio); + + return total; +} + + +int +ngx_stream_lua_ffi_priv_key_pem_to_der(const u_char *pem, size_t pem_len, + const u_char *passphrase, u_char *der, char **err) +{ + int len; + BIO *in; + EVP_PKEY *pkey; + + in = BIO_new_mem_buf((char *) pem, (int) pem_len); + if (in == NULL) { + *err = "BIO_new_mem_buf() failed"; + ERR_clear_error(); + return NGX_ERROR; + } + + pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (void *)passphrase); + if (pkey == NULL) { + BIO_free(in); + *err = "PEM_read_bio_PrivateKey() failed"; + ERR_clear_error(); + return NGX_ERROR; + } + + BIO_free(in); + + len = i2d_PrivateKey(pkey, &der); + if (len < 0) { + EVP_PKEY_free(pkey); + *err = "i2d_PrivateKey() failed"; + ERR_clear_error(); + return NGX_ERROR; + } + + EVP_PKEY_free(pkey); + + return len; +} + + +void * +ngx_stream_lua_ffi_parse_pem_cert(const u_char *pem, size_t pem_len, + char **err) +{ + BIO *bio; + X509 *x509; + u_long n; + STACK_OF(X509) *chain; + + bio = BIO_new_mem_buf((char *) pem, (int) pem_len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + ERR_clear_error(); + return NULL; + } + + x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (x509 == NULL) { + *err = "PEM_read_bio_X509_AUX() failed"; + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + chain = sk_X509_new_null(); + if (chain == NULL) { + *err = "sk_X509_new_null() failed"; + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + sk_X509_free(chain); + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + /* read rest of the chain */ + + for ( ;; ) { + + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509 == NULL) { + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_PEM + && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509() failed"; + sk_X509_pop_free(chain, X509_free); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + sk_X509_pop_free(chain, X509_free); + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + +void +ngx_stream_lua_ffi_free_cert(void *cdata) +{ + STACK_OF(X509) *chain = cdata; + + sk_X509_pop_free(chain, X509_free); +} + + +void * +ngx_stream_lua_ffi_parse_pem_priv_key(const u_char *pem, size_t pem_len, + char **err) +{ + BIO *in; + EVP_PKEY *pkey; + + in = BIO_new_mem_buf((char *) pem, (int) pem_len); + if (in == NULL) { + *err = "BIO_new_mem_buf() failed"; + ERR_clear_error(); + return NULL; + } + + pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); + if (pkey == NULL) { + *err = "PEM_read_bio_PrivateKey() failed"; + BIO_free(in); + ERR_clear_error(); + return NULL; + } + + BIO_free(in); + + return pkey; +} + + +void +ngx_stream_lua_ffi_free_priv_key(void *cdata) +{ + EVP_PKEY *pkey = cdata; + + EVP_PKEY_free(pkey); +} + + +int +ngx_stream_lua_ffi_set_cert(ngx_stream_lua_request_t *r, + void *cdata, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER < 0x1000205fL + + *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT; + return NGX_ERROR; + +# else + +#ifdef OPENSSL_IS_BORINGSSL + size_t i; +#else + int i; +#endif + X509 *x509 = NULL; + ngx_ssl_conn_t *ssl_conn; + STACK_OF(X509) *chain = cdata; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + if (sk_X509_num(chain) < 1) { + *err = "invalid certificate chain"; + goto failed; + } + + x509 = sk_X509_value(chain, 0); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + if (SSL_use_certificate(ssl_conn, x509) == 0) { + *err = "SSL_use_certificate() failed"; + goto failed; + } + + x509 = NULL; + + /* read rest of the chain */ + + for (i = 1; i < sk_X509_num(chain); i++) { + + x509 = sk_X509_value(chain, i); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + if (SSL_add1_chain_cert(ssl_conn, x509) == 0) { + *err = "SSL_add1_chain_cert() failed"; + goto failed; + } + } + + *err = NULL; + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; + +# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +#endif +} + + +int +ngx_stream_lua_ffi_set_priv_key(ngx_stream_lua_request_t *r, + void *cdata, char **err) +{ + EVP_PKEY *pkey = NULL; + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + pkey = cdata; + if (pkey == NULL) { + *err = "invalid private key failed"; + goto failed; + } + + if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) { + *err = "SSL_use_PrivateKey() failed"; + goto failed; + } + + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; +} + + +#ifndef LIBRESSL_VERSION_NUMBER +static int +ngx_stream_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) +{ + /* + * we never terminate handshake here and user can later use + * $ssl_client_verify to check verification result. + * + * this is consistent with Nginx behavior. + */ + return 1; +} +#endif + + +int +ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r, + void *ca_certs, int depth, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + + ngx_stream_lua_ctx_t *ctx; + ngx_ssl_conn_t *ssl_conn; + ngx_stream_ssl_conf_t *sscf; + STACK_OF(X509) *chain = ca_certs; + STACK_OF(X509_NAME) *name_chain = NULL; + X509 *x509 = NULL; + X509_NAME *subject = NULL; + X509_STORE *ca_store = NULL; +#ifdef OPENSSL_IS_BORINGSSL + size_t i; +#else + int i; +#endif + + ctx = ngx_stream_get_module_ctx(r->session, ngx_stream_lua_module); + if (ctx == NULL) { + *err = "no request ctx found"; + return NGX_ERROR; + } + + if (!(ctx->context & NGX_STREAM_LUA_CONTEXT_SSL_CERT)) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + /* enable verify */ + + SSL_set_verify(ssl_conn, SSL_VERIFY_PEER, + ngx_stream_lua_ssl_verify_callback); + + /* set depth */ + + if (depth < 0) { + sscf = ngx_stream_get_module_srv_conf(r->session, + ngx_stream_ssl_module); + if (sscf != NULL) { + depth = sscf->verify_depth; + + } else { + /* same as the default value of ssl_verify_depth */ + depth = 1; + } + } + + SSL_set_verify_depth(ssl_conn, depth); + + /* set CA chain */ + + if (chain != NULL) { + ca_store = X509_STORE_new(); + if (ca_store == NULL) { + *err = "X509_STORE_new() failed"; + return NGX_ERROR; + } + + /* construct name chain */ + + name_chain = sk_X509_NAME_new_null(); + if (name_chain == NULL) { + *err = "sk_X509_NAME_new_null() failed"; + goto failed; + } + + for (i = 0; i < sk_X509_num(chain); i++) { + x509 = sk_X509_value(chain, i); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + /* add subject to name chain, which will be sent to client */ + subject = X509_NAME_dup(X509_get_subject_name(x509)); + if (subject == NULL) { + *err = "X509_get_subject_name() failed"; + goto failed; + } + + if (!sk_X509_NAME_push(name_chain, subject)) { + *err = "sk_X509_NAME_push() failed"; + X509_NAME_free(subject); + goto failed; + } + + /* add to trusted CA store */ + if (X509_STORE_add_cert(ca_store, x509) == 0) { + *err = "X509_STORE_add_cert() failed"; + goto failed; + } + } + + if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) { + *err = "SSL_set0_verify_cert_store() failed"; + goto failed; + } + + SSL_set_client_CA_list(ssl_conn, name_chain); + } + + return NGX_OK; + +failed: + + sk_X509_NAME_free(name_chain); + + X509_STORE_free(ca_store); + + return NGX_ERROR; +#endif +} + + +#endif /* NGX_STREAM_SSL */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_certby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_certby.h new file mode 100644 index 000000000..976234188 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_certby.h @@ -0,0 +1,45 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_ssl_certby.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_SSL_CERTBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_SSL_CERTBY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +#if (NGX_STREAM_SSL) + + +ngx_int_t ngx_stream_lua_ssl_cert_handler_inline(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +ngx_int_t ngx_stream_lua_ssl_cert_handler_file(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +char *ngx_stream_lua_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +char *ngx_stream_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_stream_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data); + + +#endif /* NGX_STREAM_SSL */ + + +#endif /* _NGX_STREAM_LUA_SSL_CERTBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_client_helloby.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_client_helloby.c new file mode 100644 index 000000000..3b5f6710f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_client_helloby.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_STREAM_SSL) + +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_initworkerby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_ssl_module.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_ssl_certby.h" +#include "ngx_stream_lua_ssl_client_helloby.h" +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_ssl.h" + + +static void ngx_stream_lua_ssl_client_hello_done(void *data); +static void ngx_stream_lua_ssl_client_hello_aborted(void *data); +static u_char *ngx_stream_lua_log_ssl_client_hello_error(ngx_log_t *log, + u_char *buf, size_t len); +static ngx_int_t ngx_stream_lua_ssl_client_hello_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); + + +ngx_int_t +ngx_stream_lua_ssl_client_hello_handler_file(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, + lscf->srv.ssl_client_hello_src.data, + lscf->srv.ssl_client_hello_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_ssl_client_hello_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_ssl_client_hello_handler_inline(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + lscf->srv.ssl_client_hello_src.data, + lscf->srv.ssl_client_hello_src.len, + lscf->srv.ssl_client_hello_src_key, + "=ssl_client_hello_by_lua"); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_ssl_client_hello_by_chunk(L, r); +} + + +char * +ngx_stream_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_ssl_client_hello_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#ifndef SSL_ERROR_WANT_CLIENT_HELLO_CB + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 1.1.1 required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + u_char *p; + u_char *name; + ngx_str_t *value; + ngx_stream_lua_srv_conf_t *lscf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.ssl_client_hello_handler) { + return "is duplicate"; + } + + if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + lscf->srv.ssl_client_hello_handler = + (ngx_stream_lua_srv_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_stream_lua_ssl_client_hello_handler_file) { + /* Lua code in an external file */ + + name = ngx_stream_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src.data = name; + lscf->srv.ssl_client_hello_src.len = ngx_strlen(name); + + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + /* inlined Lua code */ + + lscf->srv.ssl_client_hello_src = value[1]; + + p = ngx_palloc(cf->pool, + sizeof("ssl_client_hello_by_lua") + + NGX_STREAM_LUA_INLINE_KEY_LEN); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src_key = p; + + p = ngx_copy(p, "ssl_client_hello_by_lua", + sizeof("ssl_client_hello_by_lua") - 1); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + + return NGX_CONF_OK; + +#endif /* NO SSL_ERROR_WANT_CLIENT_HELLO_CB */ +} + + +int +ngx_stream_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, + int *al, void *arg) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_stream_lua_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_ssl_ctx_t *cctx; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_session_t *s, *fs; + + c = ngx_ssl_get_connection(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream ssl client hello: connection reusable: %ud", + c->reusable); + + cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection); + + dd("ssl client hello handler, client-hello-ctx=%p", cctx); + + if (cctx && cctx->entered_client_hello_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua_client_hello_by_lua:" + " client hello cb exit code: %d", + cctx->exit_code); + + dd("lua ssl client hello done, finally"); + return cctx->exit_code; + } + + return -1; + } + + dd("first time"); + + ngx_reusable_connection(c, 0); + + s = c->data; + + fc = ngx_stream_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_stream_lua_log_ssl_client_hello_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + fs = ngx_stream_lua_create_fake_session(fc); + if (fs == NULL) { + goto failed; + } + + fs->main_conf = s->main_conf; + fs->srv_conf = s->srv_conf; + + r = ngx_stream_lua_create_fake_request(fs); + if (r == NULL) { + goto failed; + } + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + cscf = ngx_stream_get_module_srv_conf(fs, ngx_stream_core_module); + +#if defined(nginx_version) && nginx_version >= 1009000 + ngx_set_connection_log(fc, cscf->error_log); + +#else +#error "stream ssl_client_hello_by_lua only supports nginx >= 1.19.3" +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + + cctx->ctx_ref = LUA_NOREF; + } + + cctx->exit_code = 1; /* successful by default */ + cctx->connection = c; + cctx->request = r; + cctx->entered_client_hello_handler = 1; + cctx->done = 0; + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_stream_lua_ssl_ctx_index, + cctx) == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_stream_lua_get_lua_vm(r, NULL); + + c->log->action = "loading SSL client hello by lua"; + + if (lscf->srv.ssl_client_hello_handler == NULL) { + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "no ssl_client_hello_by_lua* defined in " + "server %s:%ui", &cscf->file_name, &cscf->line); + + goto failed; + } + + rc = lscf->srv.ssl_client_hello_handler(r, lscf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua_client_hello_by_lua:" + " handler return value: %i, " + "client hello cb exit code: %d", rc, cctx->exit_code); + + c->log->action = "SSL handshaking"; + return cctx->exit_code; + } + + /* rc == NGX_DONE */ + + cln = ngx_pool_cleanup_add(fc->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_stream_lua_ssl_client_hello_done; + cln->data = cctx; + + if (cctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = cctx; + cctx->cleanup = &cln->handler; + } + + *cctx->cleanup = ngx_stream_lua_ssl_client_hello_aborted; + + return -1; + +#if 1 +failed: + + if (r && r->pool) { + ngx_stream_lua_free_fake_request(r); + } + + if (fc) { + ngx_stream_lua_close_fake_connection(fc); + } + + return 0; +#endif +} + + +static void +ngx_stream_lua_ssl_client_hello_done(void *data) +{ + ngx_connection_t *c; + ngx_stream_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl client hello done"); + + if (cctx->aborted) { + return; + } + + ngx_stream_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + c->log->action = "SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); +} + + +static void +ngx_stream_lua_ssl_client_hello_aborted(void *data) +{ + ngx_stream_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl client hello done"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cctx->connection->log, 0, + "stream lua_client_hello_by_lua: client hello cb aborted"); + + cctx->aborted = 1; + cctx->request->connection->ssl = NULL; + + ngx_stream_lua_finalize_fake_request(cctx->request, NGX_ERROR); +} + + +static u_char * +ngx_stream_lua_log_ssl_client_hello_error(ngx_log_t *log, u_char *buf, + size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + p = ngx_snprintf(buf, len, ", context: ssl_client_hello_by_lua*"); + len -= p - buf; + buf = p; + + c = log->data; + + if (c && c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c && c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + + return buf; +} + + +static ngx_int_t +ngx_stream_lua_ssl_client_hello_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(r->session); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_stream_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_stream_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream failed to create new" + " coroutine to handle request"); + + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_stream_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO; + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_stream_lua_finalize_request(r, rc); + return rc; +} + + +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) +{ + ngx_ssl_conn_t *ssl_conn; +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + const unsigned char *p; + size_t remaining, len; +#endif + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + remaining = 0; + + /* This code block is taken from OpenSSL's client_hello_select_server_ctx() + * */ + if (!SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, &p, + &remaining)) + { + return NGX_DECLINED; + } + + if (remaining <= 2) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + len = (*(p++) << 8); + len += *(p++); + if (len + 2 != remaining) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + remaining = len; + if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + remaining--; + if (remaining <= 2) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + len = (*(p++) << 8); + len += *(p++); + if (len + 2 > remaining) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + remaining = len; + *name = (const char *) p; + *namelen = len; + + return NGX_OK; + +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; +#endif + +#else + *err = "no TLS extension support"; + return NGX_ERROR; +#endif +} + + +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) +{ + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + if (SSL_client_hello_get0_ext(ssl_conn, type, out, outlen) == 0) { + return NGX_DECLINED; + } + + return NGX_OK; + +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; +#endif +} + + +int +ngx_stream_lua_ffi_ssl_set_protocols(ngx_stream_lua_request_t *r, + int protocols, char **err) +{ + + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL + /* only in 0.9.8m+ */ + SSL_clear_options(ssl_conn, + SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1); +#endif + + if (!(protocols & NGX_SSL_SSLv2)) { + SSL_set_options(ssl_conn, SSL_OP_NO_SSLv2); + } + + if (!(protocols & NGX_SSL_SSLv3)) { + SSL_set_options(ssl_conn, SSL_OP_NO_SSLv3); + } + + if (!(protocols & NGX_SSL_TLSv1)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1); + } + +#ifdef SSL_OP_NO_TLSv1_1 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_1); + if (!(protocols & NGX_SSL_TLSv1_1)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_1); + } +#endif + +#ifdef SSL_OP_NO_TLSv1_2 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_2); + if (!(protocols & NGX_SSL_TLSv1_2)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_2); + } +#endif + +#ifdef SSL_OP_NO_TLSv1_3 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_3); + if (!(protocols & NGX_SSL_TLSv1_3)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_3); + } +#endif + + return NGX_OK; +} + +#endif /* NGX_STREAM_SSL */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_client_helloby.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_client_helloby.h new file mode 100644 index 000000000..05b0e17f0 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_ssl_client_helloby.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_STREAM_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ + +#include "ngx_stream_lua_common.h" + +#if (NGX_STREAM_SSL) + +ngx_int_t ngx_stream_lua_ssl_client_hello_handler_inline( + ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +ngx_int_t ngx_stream_lua_ssl_client_hello_handler_file( + ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +char *ngx_stream_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_stream_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_stream_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, + int *al, void *arg); + +#endif /* NGX_STREAM_SSL */ +#endif /* _NGX_STREAM_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_string.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_string.c new file mode 100644 index 000000000..23460a17a --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_string.c @@ -0,0 +1,463 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_string.c.tt2 + */ + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_string.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_args.h" +#include "ngx_crc32.h" + +#if (NGX_HAVE_SHA1) +#include "ngx_sha1.h" +#endif + +#include "ngx_md5.h" + +#if (NGX_OPENSSL) +#include +#include +#endif + + +static uintptr_t ngx_stream_lua_ngx_escape_sql_str(u_char *dst, + u_char *src, size_t size); +static int ngx_stream_lua_ngx_quote_sql_str(lua_State *L); +static int ngx_stream_lua_ngx_encode_args(lua_State *L); +static int ngx_stream_lua_ngx_decode_args(lua_State *L); +#if (NGX_OPENSSL) +static int ngx_stream_lua_ngx_hmac_sha1(lua_State *L); +#endif + + +void +ngx_stream_lua_inject_string_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_stream_lua_ngx_encode_args); + lua_setfield(L, -2, "encode_args"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_decode_args); + lua_setfield(L, -2, "decode_args"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_quote_sql_str); + lua_setfield(L, -2, "quote_sql_str"); + +#if (NGX_OPENSSL) + lua_pushcfunction(L, ngx_stream_lua_ngx_hmac_sha1); + lua_setfield(L, -2, "hmac_sha1"); +#endif +} + + +static int +ngx_stream_lua_ngx_quote_sql_str(lua_State *L) +{ + size_t len, dlen, escape; + u_char *p; + u_char *src, *dst; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + src = (u_char *) luaL_checklstring(L, 1, &len); + + if (len == 0) { + dst = (u_char *) "''"; + dlen = sizeof("''") - 1; + lua_pushlstring(L, (char *) dst, dlen); + return 1; + } + + escape = ngx_stream_lua_ngx_escape_sql_str(NULL, src, len); + + dlen = sizeof("''") - 1 + len + escape; + + p = lua_newuserdata(L, dlen); + + dst = p; + + *p++ = '\''; + + if (escape == 0) { + p = ngx_copy(p, src, len); + + } else { + p = (u_char *) ngx_stream_lua_ngx_escape_sql_str(p, src, len); + } + + *p++ = '\''; + + if (p != dst + dlen) { + return NGX_ERROR; + } + + lua_pushlstring(L, (char *) dst, p - dst); + + return 1; +} + + +static uintptr_t +ngx_stream_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size) +{ + ngx_uint_t n; + + if (dst == NULL) { + /* find the number of chars to be escaped */ + n = 0; + while (size) { + /* the highest bit of all the UTF-8 chars + * is always 1 */ + if ((*src & 0x80) == 0) { + switch (*src) { + case '\0': + case '\b': + case '\n': + case '\r': + case '\t': + case 26: /* \Z */ + case '\\': + case '\'': + case '"': + n++; + break; + default: + break; + } + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if ((*src & 0x80) == 0) { + switch (*src) { + case '\0': + *dst++ = '\\'; + *dst++ = '0'; + break; + + case '\b': + *dst++ = '\\'; + *dst++ = 'b'; + break; + + case '\n': + *dst++ = '\\'; + *dst++ = 'n'; + break; + + case '\r': + *dst++ = '\\'; + *dst++ = 'r'; + break; + + case '\t': + *dst++ = '\\'; + *dst++ = 't'; + break; + + case 26: + *dst++ = '\\'; + *dst++ = 'Z'; + break; + + case '\\': + *dst++ = '\\'; + *dst++ = '\\'; + break; + + case '\'': + *dst++ = '\\'; + *dst++ = '\''; + break; + + case '"': + *dst++ = '\\'; + *dst++ = '"'; + break; + + default: + *dst++ = *src; + break; + } + } else { + *dst++ = *src; + } + src++; + size--; + } /* while (size) */ + + return (uintptr_t) dst; +} + + +static void +ngx_stream_lua_encode_base64(ngx_str_t *dst, ngx_str_t *src, int no_padding) +{ + u_char *d, *s; + size_t len; + static u_char basis[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + len = src->len; + s = src->data; + d = dst->data; + + while (len > 2) { + *d++ = basis[(s[0] >> 2) & 0x3f]; + *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)]; + *d++ = basis[((s[1] & 0x0f) << 2) | (s[2] >> 6)]; + *d++ = basis[s[2] & 0x3f]; + + s += 3; + len -= 3; + } + + if (len) { + *d++ = basis[(s[0] >> 2) & 0x3f]; + + if (len == 1) { + *d++ = basis[(s[0] & 3) << 4]; + if (!no_padding) { + *d++ = '='; + } + + } else { + *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)]; + *d++ = basis[(s[1] & 0x0f) << 2]; + } + + if (!no_padding) { + *d++ = '='; + } + } + + dst->len = d - dst->data; +} + + +static int +ngx_stream_lua_ngx_encode_args(lua_State *L) +{ + ngx_str_t args; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument but seen %d", + lua_gettop(L)); + } + + luaL_checktype(L, 1, LUA_TTABLE); + ngx_stream_lua_process_args_option(NULL, L, 1, &args); + lua_pushlstring(L, (char *) args.data, args.len); + return 1; +} + + +static int +ngx_stream_lua_ngx_decode_args(lua_State *L) +{ + u_char *buf; + u_char *tmp; + size_t len = 0; + int n; + int max; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n); + } + + buf = (u_char *) luaL_checklstring(L, 1, &len); + + if (n == 2) { + max = luaL_checkint(L, 2); + lua_pop(L, 1); + + } else { + max = NGX_STREAM_LUA_MAX_ARGS; + } + + tmp = lua_newuserdata(L, len); + ngx_memcpy(tmp, buf, len); + + lua_createtable(L, 0, 4); + + return ngx_stream_lua_parse_args(L, tmp, tmp + len, max); +} + + +#if (NGX_OPENSSL) +static int +ngx_stream_lua_ngx_hmac_sha1(lua_State *L) +{ + u_char *sec, *sts; + size_t lsec, lsts; + unsigned int md_len; + unsigned char md[EVP_MAX_MD_SIZE]; + const EVP_MD *evp_md; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + sec = (u_char *) luaL_checklstring(L, 1, &lsec); + sts = (u_char *) luaL_checklstring(L, 2, &lsts); + + evp_md = EVP_sha1(); + + HMAC(evp_md, sec, lsec, sts, lsts, md, &md_len); + + lua_pushlstring(L, (char *) md, md_len); + + return 1; +} +#endif + + +void +ngx_stream_lua_ffi_md5_bin(const u_char *src, size_t len, u_char *dst) +{ + ngx_md5_t md5; + + ngx_md5_init(&md5); + ngx_md5_update(&md5, src, len); + ngx_md5_final(dst, &md5); +} + + +void +ngx_stream_lua_ffi_md5(const u_char *src, size_t len, u_char *dst) +{ + ngx_md5_t md5; + u_char md5_buf[MD5_DIGEST_LENGTH]; + + ngx_md5_init(&md5); + ngx_md5_update(&md5, src, len); + ngx_md5_final(md5_buf, &md5); + + ngx_hex_dump(dst, md5_buf, sizeof(md5_buf)); +} + + +int +ngx_stream_lua_ffi_sha1_bin(const u_char *src, size_t len, u_char *dst) +{ +#if (NGX_HAVE_SHA1) + ngx_sha1_t sha; + + ngx_sha1_init(&sha); + ngx_sha1_update(&sha, src, len); + ngx_sha1_final(dst, &sha); + + return 1; +#else + return 0; +#endif +} + + +unsigned int +ngx_stream_lua_ffi_crc32_short(const u_char *src, size_t len) +{ + return ngx_crc32_short((u_char *) src, len); +} + + +unsigned int +ngx_stream_lua_ffi_crc32_long(const u_char *src, size_t len) +{ + return ngx_crc32_long((u_char *) src, len); +} + + +size_t +ngx_stream_lua_ffi_encode_base64(const u_char *src, size_t slen, u_char *dst, + int no_padding) +{ + ngx_str_t in, out; + + in.data = (u_char *) src; + in.len = slen; + + out.data = dst; + + ngx_stream_lua_encode_base64(&out, &in, no_padding); + + return out.len; +} + + +int +ngx_stream_lua_ffi_decode_base64(const u_char *src, size_t slen, u_char *dst, + size_t *dlen) +{ + ngx_int_t rc; + ngx_str_t in, out; + + in.data = (u_char *) src; + in.len = slen; + + out.data = dst; + + rc = ngx_decode_base64(&out, &in); + + *dlen = out.len; + + return rc == NGX_OK; +} + + +size_t +ngx_stream_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst) +{ + u_char *p = dst; + + ngx_stream_lua_unescape_uri(&p, (u_char **) &src, len, + NGX_UNESCAPE_URI_COMPONENT); + return p - dst; +} + + +size_t +ngx_stream_lua_ffi_uri_escaped_length(const u_char *src, size_t len, + int type) +{ + return len + 2 * ngx_stream_lua_escape_uri(NULL, (u_char *) src, len, + type); +} + + +void +ngx_stream_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst, + int type) +{ + ngx_stream_lua_escape_uri(dst, (u_char *) src, len, + type); +} + + + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_string.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_string.h new file mode 100644 index 000000000..ef0878861 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_string.h @@ -0,0 +1,28 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_string.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_STRING_H_INCLUDED_ +#define _NGX_STREAM_LUA_STRING_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_string_api(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_STRING_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_time.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_time.c new file mode 100644 index 000000000..19bfedac1 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_time.c @@ -0,0 +1,104 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_time.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_common.h" + + +double +ngx_stream_lua_ffi_now(void) +{ + ngx_time_t *tp; + + tp = ngx_timeofday(); + + return tp->sec + tp->msec / 1000.0; +} + + +double +ngx_stream_lua_ffi_req_start_time(ngx_stream_lua_request_t *r) +{ + return r->session->start_sec + r->session->start_msec / 1000.0; +} + + +long +ngx_stream_lua_ffi_time(void) +{ + return (long) ngx_time(); +} + + +long +ngx_stream_lua_ffi_monotonic_msec(void) +{ + return (long) ngx_current_msec; +} + + +void +ngx_stream_lua_ffi_update_time(void) +{ + ngx_time_update(); +} + + +void +ngx_stream_lua_ffi_today(u_char *buf) +{ + ngx_tm_t tm; + + ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm); + + ngx_sprintf(buf, "%04d-%02d-%02d", tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday); +} + + +void +ngx_stream_lua_ffi_localtime(u_char *buf) +{ + ngx_tm_t tm; + + ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm); + + ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year, + tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, + tm.ngx_tm_sec); +} + + +void +ngx_stream_lua_ffi_utctime(u_char *buf) +{ + ngx_tm_t tm; + + ngx_gmtime(ngx_time(), &tm); + + ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year, + tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, + tm.ngx_tm_sec); +} + + + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_timer.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_timer.c new file mode 100644 index 000000000..a2d9bfa4e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_timer.c @@ -0,0 +1,966 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_timer.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_timer.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_probe.h" + + +#define NGX_STREAM_LUA_TIMER_ERRBUF_SIZE 128 + + +typedef struct { + void **main_conf; + void **srv_conf; + + + lua_State *co; + + ngx_pool_t *pool; + + ngx_listening_t *listening; + ngx_str_t client_addr_text; + + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_vm_state_t *vm_state; + + int co_ref; + unsigned delay:31; + unsigned premature:1; +} ngx_stream_lua_timer_ctx_t; + + +static int ngx_stream_lua_ngx_timer_at(lua_State *L); +static int ngx_stream_lua_ngx_timer_every(lua_State *L); +static int ngx_stream_lua_ngx_timer_helper(lua_State *L, int every); +static int ngx_stream_lua_ngx_timer_running_count(lua_State *L); +static int ngx_stream_lua_ngx_timer_pending_count(lua_State *L); +static ngx_int_t ngx_stream_lua_timer_copy( + ngx_stream_lua_timer_ctx_t *old_tctx); +static void ngx_stream_lua_timer_handler(ngx_event_t *ev); +static u_char *ngx_stream_lua_log_timer_error(ngx_log_t *log, u_char *buf, + size_t len); +static void ngx_stream_lua_abort_pending_timers(ngx_event_t *ev); + + +void +ngx_stream_lua_inject_timer_api(lua_State *L) +{ + lua_createtable(L, 0 /* narr */, 4 /* nrec */); /* ngx.timer. */ + + lua_pushcfunction(L, ngx_stream_lua_ngx_timer_at); + lua_setfield(L, -2, "at"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_timer_every); + lua_setfield(L, -2, "every"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_timer_running_count); + lua_setfield(L, -2, "running_count"); + + lua_pushcfunction(L, ngx_stream_lua_ngx_timer_pending_count); + lua_setfield(L, -2, "pending_count"); + + lua_setfield(L, -2, "timer"); +} + + +static int +ngx_stream_lua_ngx_timer_running_count(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_main_conf_t *lmcf; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request"); + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + lua_pushnumber(L, lmcf->running_timers); + + return 1; +} + + +static int +ngx_stream_lua_ngx_timer_pending_count(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_main_conf_t *lmcf; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request"); + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + lua_pushnumber(L, lmcf->pending_timers); + + return 1; +} + + +static int +ngx_stream_lua_ngx_timer_at(lua_State *L) +{ + return ngx_stream_lua_ngx_timer_helper(L, 0); +} + + +/* + * TODO: return a timer handler instead which can be passed to + * the ngx.timer.cancel method to cancel the timer. + */ +static int +ngx_stream_lua_ngx_timer_every(lua_State *L) +{ + return ngx_stream_lua_ngx_timer_helper(L, 1); +} + + +static int +ngx_stream_lua_ngx_timer_helper(lua_State *L, int every) +{ + int nargs, co_ref; + u_char *p; + lua_State *vm; /* the main thread */ + lua_State *co; + ngx_msec_t delay; + ngx_event_t *ev = NULL; + ngx_stream_lua_request_t *r; + ngx_connection_t *saved_c = NULL; + ngx_stream_lua_ctx_t *ctx; +#if 0 + ngx_http_connection_t *hc; +#endif + + ngx_stream_lua_timer_ctx_t *tctx = NULL; + ngx_stream_lua_main_conf_t *lmcf; +#if 0 + ngx_http_core_main_conf_t *cmcf; +#endif + + nargs = lua_gettop(L); + if (nargs < 2) { + return luaL_error(L, "expecting at least 2 arguments but got %d", + nargs); + } + + delay = (ngx_msec_t) (luaL_checknumber(L, 1) * 1000); + + if (every && delay == 0) { + return luaL_error(L, "delay cannot be zero"); + } + + luaL_argcheck(L, lua_isfunction(L, 2) && !lua_iscfunction(L, 2), 2, + "Lua function expected"); + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ngx_exiting && delay > 0) { + lua_pushnil(L); + lua_pushliteral(L, "process exiting"); + return 2; + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + + if (lmcf->pending_timers >= lmcf->max_pending_timers) { + lua_pushnil(L); + lua_pushliteral(L, "too many pending timers"); + return 2; + } + + if (lmcf->watcher == NULL) { + /* create the watcher fake connection */ + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "lua creating fake watcher connection"); + + if (ngx_cycle->files) { + saved_c = ngx_cycle->files[0]; + } + + lmcf->watcher = ngx_get_connection(0, ngx_cycle->log); + + if (ngx_cycle->files) { + ngx_cycle->files[0] = saved_c; + } + + if (lmcf->watcher == NULL) { + return luaL_error(L, "no memory"); + } + + /* to work around the -1 check in ngx_worker_process_cycle: */ + lmcf->watcher->fd = (ngx_socket_t) -2; + + lmcf->watcher->idle = 1; + lmcf->watcher->read->handler = ngx_stream_lua_abort_pending_timers; + lmcf->watcher->data = lmcf; + } + + vm = ngx_stream_lua_get_lua_vm(r, ctx); + + co = lua_newthread(vm); + + /* L stack: time func [args] */ + + ngx_stream_lua_probe_user_coroutine_create(r, L, co); + +#ifndef OPENRESTY_LUAJIT + lua_createtable(co, 0, 0); /* the new globals table */ + + /* co stack: global_tb */ + + lua_createtable(co, 0, 1); /* the metatable */ + ngx_stream_lua_get_globals_table(co); + lua_setfield(co, -2, "__index"); + lua_setmetatable(co, -2); + + /* co stack: global_tb */ + + ngx_stream_lua_set_globals_table(co); +#endif + + /* co stack: */ + + dd("stack top: %d", lua_gettop(L)); + + lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ + + /* L stack: time func [args] thread */ + /* vm stack: empty */ + + lua_pushvalue(L, 2); /* copy entry function to top of L*/ + + /* L stack: time func [args] thread func */ + + lua_xmove(L, co, 1); /* move entry function from L to co */ + + /* L stack: time func [args] thread */ + /* co stack: func */ + +#ifndef OPENRESTY_LUAJIT + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* co stack: func */ + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* L stack: time func [args] thread coroutines */ + + lua_pushvalue(L, -2); + + /* L stack: time func [args] thread coroutines thread */ + + co_ref = luaL_ref(L, -2); + lua_pop(L, 1); + + /* L stack: time func [args] thread */ + + if (nargs > 2) { + lua_pop(L, 1); /* L stack: time func [args] */ + lua_xmove(L, co, nargs - 2); /* L stack: time func */ + + /* co stack: func [args] */ + } + + p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_stream_lua_timer_ctx_t), + r->connection->log); + if (p == NULL) { + goto nomem; + } + + ev = (ngx_event_t *) p; + + ngx_memzero(ev, sizeof(ngx_event_t)); + + p += sizeof(ngx_event_t); + + tctx = (ngx_stream_lua_timer_ctx_t *) p; + + tctx->delay = every ? delay : 0; + + tctx->premature = 0; + tctx->co_ref = co_ref; + tctx->co = co; + + + tctx->main_conf = r->session->main_conf; + tctx->srv_conf = r->session->srv_conf; + + tctx->lmcf = lmcf; + + tctx->pool = ngx_create_pool(128, ngx_cycle->log); + if (tctx->pool == NULL) { + goto nomem; + } + + if (r->connection) { + tctx->listening = r->connection->listening; + + } else { + tctx->listening = NULL; + } + + if (r->connection->addr_text.len) { + tctx->client_addr_text.data = ngx_palloc(tctx->pool, + r->connection->addr_text.len); + if (tctx->client_addr_text.data == NULL) { + goto nomem; + } + + ngx_memcpy(tctx->client_addr_text.data, r->connection->addr_text.data, + r->connection->addr_text.len); + tctx->client_addr_text.len = r->connection->addr_text.len; + + } else { + tctx->client_addr_text.len = 0; + tctx->client_addr_text.data = NULL; + } + + if (ctx && ctx->vm_state) { + tctx->vm_state = ctx->vm_state; + tctx->vm_state->count++; + + } else { + tctx->vm_state = NULL; + } + + ev->handler = ngx_stream_lua_timer_handler; + ev->data = tctx; + ev->log = ngx_cycle->log; + + lmcf->pending_timers++; + + ngx_add_timer(ev, delay); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream created timer (co: %p delay: %M ms)", + tctx->co, delay); + + lua_pushinteger(L, 1); + return 1; + +nomem: + + if (tctx && tctx->pool) { + ngx_destroy_pool(tctx->pool); + } + + if (ev) { + ngx_free(ev); + } + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, co_ref); + + return luaL_error(L, "no memory"); +} + + +static ngx_int_t +ngx_stream_lua_timer_copy(ngx_stream_lua_timer_ctx_t *old_tctx) +{ + int nargs, co_ref, i; + u_char *p; + lua_State *vm; /* the main thread */ + lua_State *co; + lua_State *L; + ngx_event_t *ev = NULL; + + ngx_stream_lua_timer_ctx_t *tctx = NULL; + ngx_stream_lua_main_conf_t *lmcf; + + /* L stack: func [args] */ + L = old_tctx->co; + + lmcf = old_tctx->lmcf; + + vm = old_tctx->vm_state ? old_tctx->vm_state->vm : lmcf->lua; + + co = lua_newthread(vm); + +#ifndef OPENRESTY_LUAJIT + lua_createtable(co, 0, 0); /* the new globals table */ + + /* co stack: global_tb */ + + lua_createtable(co, 0, 1); /* the metatable */ + ngx_stream_lua_get_globals_table(co); + lua_setfield(co, -2, "__index"); + lua_setmetatable(co, -2); + + /* co stack: global_tb */ + + ngx_stream_lua_set_globals_table(co); +#endif + + /* co stack: */ + + dd("stack top: %d", lua_gettop(L)); + + lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ + + /* L stack: func [args] thread */ + /* vm stack: empty */ + + lua_pushvalue(L, 1); /* copy entry function to top of L*/ + + /* L stack: func [args] thread func */ + + lua_xmove(L, co, 1); /* move entry function from L to co */ + + /* L stack: func [args] thread */ + /* co stack: func */ + +#ifndef OPENRESTY_LUAJIT + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* co stack: func */ + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* L stack: func [args] thread coroutines */ + + lua_pushvalue(L, -2); + + /* L stack: func [args] thread coroutines thread */ + + co_ref = luaL_ref(L, -2); + lua_pop(L, 2); + + /* L stack: func [args] */ + + nargs = lua_gettop(L); + if (nargs > 1) { + for (i = 2; i <= nargs; i++) { + lua_pushvalue(L, i); + } + + /* L stack: func [args] [args] */ + + lua_xmove(L, co, nargs - 1); + + /* L stack: func [args] */ + /* co stack: func [args] */ + } + + p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_stream_lua_timer_ctx_t), + ngx_cycle->log); + if (p == NULL) { + goto nomem; + } + + ev = (ngx_event_t *) p; + + ngx_memzero(ev, sizeof(ngx_event_t)); + + p += sizeof(ngx_event_t); + + tctx = (ngx_stream_lua_timer_ctx_t *) p; + + ngx_memcpy(tctx, old_tctx, sizeof(ngx_stream_lua_timer_ctx_t)); + + tctx->co_ref = co_ref; + tctx->co = co; + + tctx->pool = ngx_create_pool(128, ngx_cycle->log); + if (tctx->pool == NULL) { + goto nomem; + } + + if (tctx->client_addr_text.len) { + tctx->client_addr_text.data = ngx_palloc(tctx->pool, + tctx->client_addr_text.len); + if (tctx->client_addr_text.data == NULL) { + goto nomem; + } + + ngx_memcpy(tctx->client_addr_text.data, old_tctx->client_addr_text.data, + tctx->client_addr_text.len); + } + + if (tctx->vm_state) { + tctx->vm_state->count++; + } + + ev->handler = ngx_stream_lua_timer_handler; + ev->data = tctx; + ev->log = ngx_cycle->log; + + lmcf->pending_timers++; + + ngx_add_timer(ev, tctx->delay); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream created next timer (co: %p delay: %M ms)", + tctx->co, tctx->delay); + + return NGX_OK; + +nomem: + + if (tctx && tctx->pool) { + ngx_destroy_pool(tctx->pool); + } + + if (ev) { + ngx_free(ev); + } + + /* L stack: func [args] */ + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, co_ref); + + /* L stack: func [args] coroutines */ + + lua_pop(L, 1); + + return NGX_ERROR; +} + + +static void +ngx_stream_lua_timer_handler(ngx_event_t *ev) +{ + int n; + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c = NULL; + ngx_pool_cleanup_t *pcln; + + ngx_stream_lua_request_t *r = NULL; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_timer_ctx_t tctx; + ngx_stream_lua_main_conf_t *lmcf; + + ngx_stream_core_srv_conf_t *clcf; + ngx_stream_session_t *s; + + lua_Debug ar; + u_char *p; + u_char errbuf[NGX_STREAM_LUA_TIMER_ERRBUF_SIZE]; + const char *source; + const char *errmsg; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua ngx.timer expired"); + + ngx_memcpy(&tctx, ev->data, sizeof(ngx_stream_lua_timer_ctx_t)); + ngx_free(ev); + + ngx_stream_lua_assert(tctx.co_ref && tctx.co); + + lmcf = tctx.lmcf; + + lmcf->pending_timers--; + + if (!ngx_exiting && tctx.delay > 0) { + rc = ngx_stream_lua_timer_copy(&tctx); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "failed to create the next timer of delay %ud ms", + (unsigned) tctx.delay); + } + } + + if (lmcf->running_timers >= lmcf->max_running_timers) { + p = ngx_snprintf(errbuf, NGX_STREAM_LUA_TIMER_ERRBUF_SIZE - 1, + "stream lua: %i lua_max_running_timers are not enough", + lmcf->max_running_timers); + *p = '\0'; + errmsg = (const char *) errbuf; + goto failed; + } + + c = ngx_stream_lua_create_fake_connection(tctx.pool); + if (c == NULL) { + errmsg = "could not create fake connection"; + goto failed; + } + + c->log->handler = ngx_stream_lua_log_timer_error; + c->log->data = c; + + c->listening = tctx.listening; + c->addr_text = tctx.client_addr_text; + + s = ngx_stream_lua_create_fake_session(c); + if (s == NULL) { + errmsg = "could not create fake session"; + goto failed; + } + + + s->main_conf = tctx.main_conf; + s->srv_conf = tctx.srv_conf; + + clcf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + +#if defined(nginx_version) && nginx_version >= 1009000 + ngx_set_connection_log(s->connection, clcf->error_log); + +#else +#endif + + dd("lmcf: %p", lmcf); + + ctx = ngx_stream_lua_create_ctx(s); + if (ctx == NULL) { + errmsg = "could not create ctx"; + goto failed; + } + + r = ctx->request; + + if (tctx.vm_state) { + ctx->vm_state = tctx.vm_state; + + pcln = ngx_pool_cleanup_add(r->pool, 0); + if (pcln == NULL) { + errmsg = "could not add vm cleanup"; + goto failed; + } + + pcln->handler = ngx_stream_lua_cleanup_vm; + pcln->data = tctx.vm_state; + } + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + + L = ngx_stream_lua_get_lua_vm(r, ctx); + + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + errmsg = "could not add request cleanup"; + goto failed; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + + ctx->entered_content_phase = 1; + ctx->context = NGX_STREAM_LUA_CONTEXT_TIMER; + + r->read_event_handler = ngx_stream_lua_block_reading; + + ctx->cur_co_ctx->co_ref = tctx.co_ref; + ctx->cur_co_ctx->co = tctx.co; + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + + dd("r connection: %p, log %p", r->connection, r->connection->log); + + /* save the request in coroutine globals table */ + ngx_stream_lua_set_req(tctx.co, r); + + lmcf->running_timers++; + + lua_pushboolean(tctx.co, tctx.premature); + + n = lua_gettop(tctx.co); + if (n > 2) { + lua_insert(tctx.co, 2); + } + +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + rc = ngx_stream_lua_run_thread(L, r, ctx, n - 1); + + dd("timer lua run thread: %d", (int) rc); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_stream_lua_finalize_request(r, rc); + return; + +failed: + + /* co stack: func [args] */ + lua_pushvalue(tctx.co, 1); + /* co stack: func [args] func */ + lua_getinfo(tctx.co, ">Sf", &ar); + + source = ar.source; + + if (source == NULL) { + source = "(unknown)"; + } + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "stream lua failed to run timer with function " + "defined at %s:%d: %s", + source, ar.linedefined, errmsg); + + lua_pushlightuserdata(tctx.co, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(tctx.co, LUA_REGISTRYINDEX); + luaL_unref(tctx.co, -1, tctx.co_ref); + lua_settop(tctx.co, 0); + + if (tctx.vm_state) { + ngx_stream_lua_cleanup_vm(tctx.vm_state); + } + + if (c) { + ngx_stream_lua_close_fake_connection(c); + + } else if (tctx.pool) { + ngx_destroy_pool(tctx.pool); + } +} + + +static u_char * +ngx_stream_lua_log_timer_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + c = log->data; + + dd("ctx = %p", c); + + p = ngx_snprintf(buf, len, ", context: ngx.timer"); + len -= p - buf; + buf = p; + + if (c != NULL) { + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", + &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + } + + return buf; +} + + +static void +ngx_stream_lua_abort_pending_timers(ngx_event_t *ev) +{ + ngx_int_t i, n; + ngx_event_t **events; + ngx_connection_t *c, *saved_c = NULL; + ngx_rbtree_node_t *cur, *prev, *next, *sentinel, *temp; + + ngx_stream_lua_timer_ctx_t *tctx; + ngx_stream_lua_main_conf_t *lmcf; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "lua abort pending timers"); + + c = ev->data; + lmcf = c->data; + + dd("lua connection fd: %d", (int) c->fd); + + if (!c->close) { + return; + } + + c->read->closed = 1; + c->write->closed = 1; + + /* we temporarily use a valid fd (0) to make ngx_free_connection happy */ + + c->fd = 0; + + if (ngx_cycle->files) { + saved_c = ngx_cycle->files[0]; + } + + ngx_free_connection(c); + + c->fd = (ngx_socket_t) -1; + + if (ngx_cycle->files) { + ngx_cycle->files[0] = saved_c; + } + + if (lmcf->pending_timers == 0) { + return; + } + + /* expire pending timers immediately */ + + sentinel = ngx_event_timer_rbtree.sentinel; + + cur = ngx_event_timer_rbtree.root; + + /* XXX nginx does not guarantee the parent of root is meaningful, + * so we temporarily override it to simplify tree traversal. */ + temp = cur->parent; + cur->parent = NULL; + + prev = NULL; + + events = ngx_pcalloc(ngx_cycle->pool, + lmcf->pending_timers * sizeof(ngx_event_t *)); + if (events == NULL) { + return; + } + + n = 0; + + dd("root: %p, root parent: %p, sentinel: %p", cur, cur->parent, sentinel); + + while (n < lmcf->pending_timers) { + if (cur == sentinel || cur == NULL) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "lua pending timer counter got out of sync: %i", + lmcf->pending_timers); + break; + } + + dd("prev: %p, cur: %p, cur parent: %p, cur left: %p, cur right: %p", + prev, cur, cur->parent, cur->left, cur->right); + + if (prev == cur->parent) { + /* neither of the children has been accessed yet */ + + next = cur->left; + if (next == sentinel) { + ev = (ngx_event_t *) + ((char *) cur - offsetof(ngx_event_t, timer)); + + if (ev->handler == ngx_stream_lua_timer_handler) { + dd("found node: %p", cur); + events[n++] = ev; + } + + next = (cur->right != sentinel) ? cur->right : cur->parent; + } + + } else if (prev == cur->left) { + /* just accessed the left child */ + + ev = (ngx_event_t *) + ((char *) cur - offsetof(ngx_event_t, timer)); + + if (ev->handler == ngx_stream_lua_timer_handler) { + dd("found node 2: %p", cur); + events[n++] = ev; + } + + next = (cur->right != sentinel) ? cur->right : cur->parent; + + } else if (prev == cur->right) { + /* already accessed both children */ + next = cur->parent; + + } else { + /* not reacheable */ + next = NULL; + } + + prev = cur; + cur = next; + } + + /* restore the old tree root's parent */ + ngx_event_timer_rbtree.root->parent = temp; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua found %i pending timers to be " + "aborted prematurely", n); + + for (i = 0; i < n; i++) { + ev = events[i]; + + ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer); + +#if (NGX_DEBUG) + ev->timer.left = NULL; + ev->timer.right = NULL; + ev->timer.parent = NULL; +#endif + + ev->timer_set = 0; + + ev->timedout = 1; + + tctx = ev->data; + tctx->premature = 1; + + dd("calling timer handler prematurely"); + ev->handler(ev); + } + +#if 0 + if (pending_timers) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "lua pending timer counter got out of sync: %i", + pending_timers); + } +#endif +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_timer.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_timer.h new file mode 100644 index 000000000..3472b5ecf --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_timer.h @@ -0,0 +1,28 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_timer.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_TIMER_H_INCLUDED_ +#define _NGX_STREAM_LUA_TIMER_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +void ngx_stream_lua_inject_timer_api(lua_State *L); + + +#endif /* _NGX_STREAM_LUA_TIMER_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_uthread.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_uthread.c new file mode 100644 index 000000000..c48e944cf --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_uthread.c @@ -0,0 +1,288 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_uthread.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_uthread.h" +#include "ngx_stream_lua_coroutine.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_probe.h" + + +#if 1 +#undef ngx_stream_lua_probe_info +#define ngx_stream_lua_probe_info(msg) +#endif + + +static int ngx_stream_lua_uthread_spawn(lua_State *L); +static int ngx_stream_lua_uthread_wait(lua_State *L); +static int ngx_stream_lua_uthread_kill(lua_State *L); + + +void +ngx_stream_lua_inject_uthread_api(ngx_log_t *log, lua_State *L) +{ + /* new thread table */ + lua_createtable(L, 0 /* narr */, 3 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_uthread_spawn); + lua_setfield(L, -2, "spawn"); + + lua_pushcfunction(L, ngx_stream_lua_uthread_wait); + lua_setfield(L, -2, "wait"); + + lua_pushcfunction(L, ngx_stream_lua_uthread_kill); + lua_setfield(L, -2, "kill"); + + lua_setfield(L, -2, "thread"); +} + + +static int +ngx_stream_lua_uthread_spawn(lua_State *L) +{ + int n; + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx = NULL; + + n = lua_gettop(L); + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx); + + /* anchor the newly created coroutine into the Lua registry */ + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, -2); + coctx->co_ref = luaL_ref(L, -2); + lua_pop(L, 1); + + if (n > 1) { + lua_replace(L, 1); + lua_xmove(L, coctx->co, n - 1); + } + + coctx->is_uthread = 1; + ctx->uthreads++; + + coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + ctx->co_op = NGX_STREAM_LUA_USER_THREAD_RESUME; + + ctx->cur_co_ctx->thread_spawn_yielded = 1; + + if (ngx_stream_lua_post_thread(r, ctx, ctx->cur_co_ctx) != NGX_OK) { + return luaL_error(L, "no memory"); + } + + coctx->parent_co_ctx = ctx->cur_co_ctx; + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_probe_user_thread_spawn(r, L, coctx->co); + + dd("yielding with arg %s, top=%d, index-1:%s", luaL_typename(L, -1), + (int) lua_gettop(L), luaL_typename(L, 1)); + return lua_yield(L, 1); +} + + +static int +ngx_stream_lua_uthread_wait(lua_State *L) +{ + int i, nargs, nrets; + lua_State *sub_co; + ngx_stream_lua_request_t *r; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx, *sub_coctx; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); + + coctx = ctx->cur_co_ctx; + + nargs = lua_gettop(L); + + for (i = 1; i <= nargs; i++) { + sub_co = lua_tothread(L, i); + + luaL_argcheck(L, sub_co, i, "lua thread expected"); + + sub_coctx = ngx_stream_lua_get_co_ctx(sub_co, ctx); + if (sub_coctx == NULL) { + return luaL_error(L, "no co ctx found"); + } + + if (!sub_coctx->is_uthread) { + return luaL_error(L, "attempt to wait on a coroutine that is " + "not a user thread"); + } + + if (sub_coctx->parent_co_ctx != coctx) { + return luaL_error(L, "only the parent coroutine can wait on the " + "thread"); + } + + switch (sub_coctx->co_status) { + case NGX_STREAM_LUA_CO_ZOMBIE: + + ngx_stream_lua_probe_info("found zombie child"); + + nrets = lua_gettop(sub_coctx->co); + + dd("child retval count: %d, %s: %s", (int) nrets, + luaL_typename(sub_coctx->co, -1), + lua_tostring(sub_coctx->co, -1)); + + if (nrets) { + lua_xmove(sub_coctx->co, L, nrets); + } + +#if 1 + ngx_stream_lua_del_thread(r, L, ctx, sub_coctx); + ctx->uthreads--; +#endif + + return nrets; + + case NGX_STREAM_LUA_CO_DEAD: + dd("uthread already waited: %p (parent %p)", sub_coctx, + coctx); + + if (i < nargs) { + /* just ignore it if it is not the last one */ + continue; + } + + /* being the last one */ + lua_pushnil(L); + lua_pushliteral(L, "already waited or killed"); + return 2; + + default: + dd("uthread %p still alive, status: %d, parent %p", sub_coctx, + sub_coctx->co_status, coctx); + break; + } + + ngx_stream_lua_probe_user_thread_wait(L, sub_coctx->co); + sub_coctx->waited_by_parent = 1; + } + + return lua_yield(L, 0); +} + + +static int +ngx_stream_lua_uthread_kill(lua_State *L) +{ + lua_State *sub_co; + ngx_stream_lua_request_t *r; + + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx, *sub_coctx; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + + | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_TIMER); + + coctx = ctx->cur_co_ctx; + + sub_co = lua_tothread(L, 1); + luaL_argcheck(L, sub_co, 1, "lua thread expected"); + + sub_coctx = ngx_stream_lua_get_co_ctx(sub_co, ctx); + + if (sub_coctx == NULL) { + return luaL_error(L, "no co ctx found"); + } + + if (!sub_coctx->is_uthread) { + lua_pushnil(L); + lua_pushliteral(L, "not user thread"); + return 2; + } + + if (sub_coctx->parent_co_ctx != coctx) { + lua_pushnil(L); + lua_pushliteral(L, "killer not parent"); + return 2; + } + + + switch (sub_coctx->co_status) { + case NGX_STREAM_LUA_CO_ZOMBIE: + ngx_stream_lua_del_thread(r, L, ctx, sub_coctx); + ctx->uthreads--; + + lua_pushnil(L); + lua_pushliteral(L, "already terminated"); + return 2; + + case NGX_STREAM_LUA_CO_DEAD: + lua_pushnil(L); + lua_pushliteral(L, "already waited or killed"); + return 2; + + default: + ngx_stream_lua_cleanup_pending_operation(sub_coctx); + ngx_stream_lua_del_thread(r, L, ctx, sub_coctx); + ctx->uthreads--; + + lua_pushinteger(L, 1); + return 1; + } + + /* not reacheable */ +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_uthread.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_uthread.h new file mode 100644 index 000000000..6e8b54ee4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_uthread.h @@ -0,0 +1,44 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_uthread.h.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_UTHREAD_H_INCLUDED_ +#define _NGX_STREAM_LUA_UTHREAD_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +#define ngx_stream_lua_is_thread(ctx) \ + ((ctx)->cur_co_ctx->is_uthread || (ctx)->cur_co_ctx == &(ctx)->entry_co_ctx) + + +#define ngx_stream_lua_is_entry_thread(ctx) \ + ((ctx)->cur_co_ctx == &(ctx)->entry_co_ctx) + + +#define ngx_stream_lua_entry_thread_alive(ctx) \ + ((ctx)->entry_co_ctx.co_ref != LUA_NOREF) + + +#define ngx_stream_lua_coroutine_alive(coctx) \ + ((coctx)->co_status != NGX_STREAM_LUA_CO_DEAD \ + && (coctx)->co_status != NGX_STREAM_LUA_CO_ZOMBIE) + + +void ngx_stream_lua_inject_uthread_api(ngx_log_t *log, lua_State *L); + + +#endif /* _NGX_STREAM_LUA_UTHREAD_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_util.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_util.c new file mode 100644 index 000000000..58061b497 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_util.c @@ -0,0 +1,3676 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_util.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "nginx.h" +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_exception.h" +#include "ngx_stream_lua_pcrefix.h" +#include "ngx_stream_lua_args.h" +#include "ngx_stream_lua_output.h" +#include "ngx_stream_lua_control.h" +#include "ngx_stream_lua_log.h" +#include "ngx_stream_lua_string.h" +#include "ngx_stream_lua_misc.h" +#include "ngx_stream_lua_consts.h" +#include "ngx_stream_lua_shdict.h" +#include "ngx_stream_lua_coroutine.h" +#include "ngx_stream_lua_socket_tcp.h" +#include "ngx_stream_lua_socket_udp.h" +#include "ngx_stream_lua_sleep.h" +#include "ngx_stream_lua_probe.h" +#include "ngx_stream_lua_uthread.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_timer.h" +#include "ngx_stream_lua_config.h" +#include "ngx_stream_lua_ssl.h" + + +#include "ngx_stream_lua_phase.h" + + +#if 1 +#undef ngx_stream_lua_probe_info +#define ngx_stream_lua_probe_info(msg) +#endif + + +#ifndef NGX_STREAM_LUA_BT_DEPTH +#define NGX_STREAM_LUA_BT_DEPTH 22 +#endif + + +#ifndef NGX_STREAM_LUA_BT_MAX_COROS +#define NGX_STREAM_LUA_BT_MAX_COROS 5 +#endif + + +#if (NGX_STREAM_LUA_HAVE_SA_RESTART) +#define NGX_STREAM_LUA_SA_RESTART_SIGS { \ + ngx_signal_value(NGX_RECONFIGURE_SIGNAL), \ + ngx_signal_value(NGX_REOPEN_SIGNAL), \ + ngx_signal_value(NGX_NOACCEPT_SIGNAL), \ + ngx_signal_value(NGX_TERMINATE_SIGNAL), \ + ngx_signal_value(NGX_SHUTDOWN_SIGNAL), \ + ngx_signal_value(NGX_CHANGEBIN_SIGNAL), \ + SIGALRM, \ + SIGINT, \ + SIGIO, \ + SIGCHLD, \ + SIGSYS, \ + SIGPIPE, \ + 0 \ +}; +#endif + + +char ngx_stream_lua_code_cache_key; +char ngx_stream_lua_regex_cache_key; +char ngx_stream_lua_socket_pool_key; +char ngx_stream_lua_coroutines_key; + +static void ngx_stream_lua_init_registry(lua_State *L, ngx_log_t *log); +static void ngx_stream_lua_init_globals(lua_State *L, ngx_cycle_t *cycle, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log); +#ifdef OPENRESTY_LUAJIT +static void ngx_stream_lua_inject_global_write_guard(lua_State *L, + ngx_log_t *log); +#endif +static void ngx_stream_lua_set_path(ngx_cycle_t *cycle, lua_State *L, + int tab_idx, const char *fieldname, const char *path, + const char *default_path, ngx_log_t *log); +static ngx_int_t ngx_stream_lua_handle_exit(lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx); +static int ngx_stream_lua_thread_traceback(lua_State *L, lua_State *co, + ngx_stream_lua_co_ctx_t *coctx); +static void ngx_stream_lua_inject_ngx_api(lua_State *L, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log); +static ngx_int_t ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r, + ngx_chain_t *in); +static void ngx_stream_lua_finalize_threads(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, lua_State *L); +static ngx_int_t ngx_stream_lua_post_zombie_thread(ngx_stream_lua_request_t *r, + ngx_stream_lua_co_ctx_t *parent, ngx_stream_lua_co_ctx_t *thread); +static void ngx_stream_lua_cleanup_zombie_child_uthreads( + ngx_stream_lua_request_t *r, lua_State *L, ngx_stream_lua_ctx_t *ctx, + ngx_stream_lua_co_ctx_t *coctx); +static ngx_int_t ngx_stream_lua_on_abort_resume(ngx_stream_lua_request_t *r); +static void ngx_stream_lua_close_fake_request(ngx_stream_lua_request_t *r); +static ngx_int_t ngx_stream_lua_flush_pending_output( + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx); +static ngx_int_t + ngx_stream_lua_process_flushing_coroutines(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx); +static lua_State *ngx_stream_lua_new_state(lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log); +static int ngx_stream_lua_get_raw_phase_context(lua_State *L); +static int ngx_stream_lua_req_socket(lua_State *L); + + +#ifndef LUA_PATH_SEP +#define LUA_PATH_SEP ";" +#endif + + +#if !defined(LUA_DEFAULT_PATH) && (NGX_DEBUG) +#define LUA_DEFAULT_PATH "../lua-resty-core/lib/?.lua;" \ + "../lua-resty-lrucache/lib/?.lua" +#endif + + +#define AUX_MARK "\1" + + +static void +ngx_stream_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx, + const char *fieldname, const char *path, const char *default_path, + ngx_log_t *log) +{ + const char *tmp_path; + const char *prefix; + + /* XXX here we use some hack to simplify string manipulation */ + tmp_path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, + LUA_PATH_SEP AUX_MARK LUA_PATH_SEP); + + lua_pushlstring(L, (char *) cycle->prefix.data, cycle->prefix.len); + prefix = lua_tostring(L, -1); + tmp_path = luaL_gsub(L, tmp_path, "$prefix", prefix); + tmp_path = luaL_gsub(L, tmp_path, "${prefix}", prefix); + lua_pop(L, 3); + + dd("tmp_path path: %s", tmp_path); + +#if (NGX_DEBUG) + tmp_path = +#else + (void) +#endif + luaL_gsub(L, tmp_path, AUX_MARK, default_path); + +#if (NGX_DEBUG) + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0, + "lua setting lua package.%s to \"%s\"", fieldname, tmp_path); +#endif + + lua_remove(L, -2); + + /* fix negative index as there's new data on stack */ + tab_idx = (tab_idx < 0) ? (tab_idx - 1) : tab_idx; + lua_setfield(L, tab_idx, fieldname); +} + + +#ifndef OPENRESTY_LUAJIT +/** + * Create new table and set _G field to itself. + * + * After: + * | new table | <- top + * | ... | + * */ +void +ngx_stream_lua_create_new_globals_table(lua_State *L, int narr, int nrec) +{ + lua_createtable(L, narr, nrec + 1); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_G"); +} +#endif /* OPENRESTY_LUAJIT */ + + +static lua_State * +ngx_stream_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log) +{ + lua_State *L; + const char *old_path; + const char *new_path; + size_t old_path_len; + const char *old_cpath; + const char *new_cpath; + size_t old_cpath_len; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0, "lua creating new vm state"); + + L = luaL_newstate(); + if (L == NULL) { + return NULL; + } + + luaL_openlibs(L); + + lua_getglobal(L, "package"); + + if (!lua_istable(L, -1)) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "the \"package\" table does not exist"); + return NULL; + } + + if (parent_vm) { + lua_getglobal(parent_vm, "package"); + lua_getfield(parent_vm, -1, "path"); + old_path = lua_tolstring(parent_vm, -1, &old_path_len); + lua_pop(parent_vm, 1); + + lua_pushlstring(L, old_path, old_path_len); + lua_setfield(L, -2, "path"); + + lua_getfield(parent_vm, -1, "cpath"); + old_path = lua_tolstring(parent_vm, -1, &old_path_len); + lua_pop(parent_vm, 2); + + lua_pushlstring(L, old_path, old_path_len); + lua_setfield(L, -2, "cpath"); + + } else { +#ifdef LUA_DEFAULT_PATH +# define LUA_DEFAULT_PATH_LEN (sizeof(LUA_DEFAULT_PATH) - 1) + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "lua prepending default package.path with %s", + LUA_DEFAULT_PATH); + + lua_pushliteral(L, LUA_DEFAULT_PATH ";"); /* package default */ + lua_getfield(L, -2, "path"); /* package default old */ + lua_concat(L, 2); /* package new */ + lua_setfield(L, -2, "path"); /* package */ +#endif + +#ifdef LUA_DEFAULT_CPATH +# define LUA_DEFAULT_CPATH_LEN (sizeof(LUA_DEFAULT_CPATH) - 1) + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "lua prepending default package.cpath with %s", + LUA_DEFAULT_CPATH); + + lua_pushliteral(L, LUA_DEFAULT_CPATH ";"); /* package default */ + lua_getfield(L, -2, "cpath"); /* package default old */ + old_cpath = lua_tolstring(L, -1, &old_cpath_len); + lua_concat(L, 2); /* package new */ + lua_setfield(L, -2, "cpath"); /* package */ +#endif + + if (lmcf->lua_path.len != 0) { + lua_getfield(L, -1, "path"); /* get original package.path */ + old_path = lua_tolstring(L, -1, &old_path_len); + + dd("old path: %s", old_path); + + lua_pushlstring(L, (char *) lmcf->lua_path.data, + lmcf->lua_path.len); + new_path = lua_tostring(L, -1); + + ngx_stream_lua_set_path(cycle, L, -3, "path", new_path, old_path, + log); + + lua_pop(L, 2); + } + + if (lmcf->lua_cpath.len != 0) { + lua_getfield(L, -1, "cpath"); /* get original package.cpath */ + old_cpath = lua_tolstring(L, -1, &old_cpath_len); + + dd("old cpath: %s", old_cpath); + + lua_pushlstring(L, (char *) lmcf->lua_cpath.data, + lmcf->lua_cpath.len); + new_cpath = lua_tostring(L, -1); + + ngx_stream_lua_set_path(cycle, L, -3, "cpath", new_cpath, old_cpath, + log); + + + lua_pop(L, 2); + } + } + + lua_pop(L, 1); /* remove the "package" table */ + + ngx_stream_lua_init_registry(L, log); + ngx_stream_lua_init_globals(L, cycle, lmcf, log); + + return L; +} + + +lua_State * +ngx_stream_lua_new_thread(ngx_stream_lua_request_t *r, lua_State *L, int *ref) +{ + int base; + lua_State *co; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua creating new thread"); + + base = lua_gettop(L); + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + co = lua_newthread(L); + +#ifndef OPENRESTY_LUAJIT + /* {{{ inherit coroutine's globals to main thread's globals table + * for print() function will try to find tostring() in current + * globals table. + */ + /* new globals table for coroutine */ + ngx_stream_lua_create_new_globals_table(co, 0, 0); + + lua_createtable(co, 0, 1); + ngx_stream_lua_get_globals_table(co); + lua_setfield(co, -2, "__index"); + lua_setmetatable(co, -2); + + ngx_stream_lua_set_globals_table(co); + /* }}} */ +#endif /* OPENRESTY_LUAJIT */ + + *ref = luaL_ref(L, -2); + + if (*ref == LUA_NOREF) { + lua_settop(L, base); /* restore main thread stack */ + return NULL; + } + + lua_settop(L, base); + return co; +} + + +void +ngx_stream_lua_del_thread(ngx_stream_lua_request_t *r, lua_State *L, + ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx) +{ + if (coctx->co_ref == LUA_NOREF) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua deleting light thread"); + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx); + + luaL_unref(L, -1, coctx->co_ref); + coctx->co_ref = LUA_NOREF; + coctx->co_status = NGX_STREAM_LUA_CO_DEAD; + + lua_pop(L, 1); +} + + +u_char * +ngx_stream_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len) +{ + u_char *p; + ngx_str_t dst; + + dst.data = ngx_palloc(pool, len + 1); + if (dst.data == NULL) { + return NULL; + } + + dst.len = len; + + p = ngx_copy(dst.data, src, len); + *p = '\0'; + + if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->prefix, &dst) + != NGX_OK) + { + return NULL; + } + + return dst.data; +} + + + + +ngx_int_t +ngx_stream_lua_send_chain_link(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_chain_t *in) +{ + if (in == NULL) { + ctx->eof = 1; + + return NGX_OK; + } + + return ngx_stream_lua_output_filter(r, in); +} + + + + +static ngx_int_t +ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r, ngx_chain_t *in) +{ + ngx_int_t rc; + ngx_stream_lua_ctx_t *ctx; + + rc = ngx_stream_top_filter(r->session, in, 1); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + ngx_chain_update_chains(r->pool, + &ctx->free_bufs, &ctx->busy_bufs, &in, + (ngx_buf_tag_t) &ngx_stream_lua_module); + + return rc; +} + + + + +static void +ngx_stream_lua_init_registry(lua_State *L, ngx_log_t *log) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0, + "lua initializing lua registry"); + + /* {{{ register a table to anchor lua coroutines reliably: + * {([int]ref) = [cort]} */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_createtable(L, 0, 32 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* create the registry entry for the Lua request ctx data table */ + lua_pushliteral(L, ngx_stream_lua_ctx_tables_key); + lua_createtable(L, 0, 32 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* create the registry entry for the Lua socket connection pool table */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_createtable(L, 0, 8 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + +#if (NGX_PCRE) + /* create the registry entry for the Lua precompiled regex object cache */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + regex_cache_key)); + lua_createtable(L, 0, 16 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); +#endif + + /* {{{ register table to cache user code: + * { [(string)cache_key] = } */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + code_cache_key)); + lua_createtable(L, 0, 8 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ +} + + +static void +ngx_stream_lua_init_globals(lua_State *L, ngx_cycle_t *cycle, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0, + "lua initializing lua globals"); + + + ngx_stream_lua_inject_ngx_api(L, lmcf, log); +} + + +static void +ngx_stream_lua_inject_ngx_api(lua_State *L, ngx_stream_lua_main_conf_t *lmcf, + ngx_log_t *log) +{ + lua_createtable(L, 0 /* narr */, 113 /* nrec */); /* ngx.* */ + + lua_pushcfunction(L, ngx_stream_lua_get_raw_phase_context); + lua_setfield(L, -2, "_phase_ctx"); + + + ngx_stream_lua_inject_core_consts(L); + + ngx_stream_lua_inject_log_api(L); + ngx_stream_lua_inject_output_api(L); + ngx_stream_lua_inject_string_api(L); + ngx_stream_lua_inject_control_api(log, L); + + + ngx_stream_lua_inject_sleep_api(L); + ngx_stream_lua_inject_phase_api(L); + + ngx_stream_lua_inject_req_api(log, L); + + + ngx_stream_lua_inject_shdict_api(lmcf, L); + ngx_stream_lua_inject_socket_tcp_api(log, L); + ngx_stream_lua_inject_socket_udp_api(log, L); + ngx_stream_lua_inject_uthread_api(log, L); + ngx_stream_lua_inject_timer_api(L); + ngx_stream_lua_inject_config_api(L); + + lua_getglobal(L, "package"); /* ngx package */ + lua_getfield(L, -1, "loaded"); /* ngx package loaded */ + lua_pushvalue(L, -3); /* ngx package loaded ngx */ + lua_setfield(L, -2, "ngx"); /* ngx package loaded */ + lua_pop(L, 2); + + lua_setglobal(L, "ngx"); + + ngx_stream_lua_inject_coroutine_api(log, L); +} + +#ifdef OPENRESTY_LUAJIT +static void +ngx_stream_lua_inject_global_write_guard(lua_State *L, ngx_log_t *log) +{ + int rc; + + const char buf[] = + "local ngx_log = ngx.log\n" + "local ngx_WARN = ngx.WARN\n" + "local tostring = tostring\n" + "local ngx_get_phase = ngx.get_phase\n" + "local traceback = require 'debug'.traceback\n" + "local function newindex(table, key, value)\n" + "rawset(table, key, value)\n" + "local phase = ngx_get_phase()\n" + "if phase == 'init_worker' or phase == 'init' then\n" + "return\n" + "end\n" + "ngx_log(ngx_WARN, 'writing a global Lua variable " + "(\\'', tostring(key), '\\') which may lead to " + "race conditions between concurrent requests, so " + "prefer the use of \\'local\\' variables', " + "traceback('', 2))\n" + "end\n" + "setmetatable(_G, { __newindex = newindex })\n" + ; + + rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=_G write guard"); + + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to load Lua code (%i): %s", + rc, lua_tostring(L, -1)); + + lua_pop(L, 1); + return; + } + + rc = lua_pcall(L, 0, 0, 0); + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to run Lua code (%i): %s", + rc, lua_tostring(L, -1)); + lua_pop(L, 1); + } +} +#endif + + +void +ngx_stream_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in) +{ + ngx_chain_t *cl; + + for (cl = in; cl; cl = cl->next) { + cl->buf->pos = cl->buf->last; + cl->buf->file_pos = cl->buf->file_last; + } +} + + +ngx_int_t +ngx_stream_lua_add_copy_chain(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in, + ngx_int_t *eof) +{ + ngx_chain_t *cl; + size_t len; + ngx_buf_t *b; + + len = 0; + *eof = 0; + + for (cl = in; cl; cl = cl->next) { + if (ngx_buf_in_memory(cl->buf)) { + len += cl->buf->last - cl->buf->pos; + } + + if (cl->buf->last_in_chain || cl->buf->last_buf) { + *eof = 1; + } + } + + if (len == 0) { + return NGX_OK; + } + + cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_bufs, len); + if (cl == NULL) { + return NGX_ERROR; + } + + dd("chains get free buf: %d == %d", (int) (cl->buf->end - cl->buf->start), + (int) len); + + b = cl->buf; + + while (in) { + if (ngx_buf_in_memory(in->buf)) { + b->last = ngx_copy(b->last, in->buf->pos, + in->buf->last - in->buf->pos); + } + + in = in->next; + } + + **plast = cl; + *plast = &cl->next; + + return NGX_OK; +} + + +void +ngx_stream_lua_reset_ctx(ngx_stream_lua_request_t *r, lua_State *L, + ngx_stream_lua_ctx_t *ctx) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua reset ctx"); + + ngx_stream_lua_finalize_threads(r, ctx, L); + +#if 0 + if (ctx->user_co_ctx) { + /* no way to destroy a list but clean up the whole pool */ + ctx->user_co_ctx = NULL; + } +#endif + + ngx_memzero(&ctx->entry_co_ctx, sizeof(ngx_stream_lua_co_ctx_t)); + + ctx->entry_co_ctx.co_ref = LUA_NOREF; + + + ctx->entered_content_phase = 0; + + ctx->exit_code = 0; + ctx->exited = 0; + ctx->resume_handler = ngx_stream_lua_wev_handler; + + + ctx->co_op = 0; +} + + + + +void +ngx_stream_lua_request_cleanup_handler(void *data) +{ + ngx_stream_lua_ctx_t *ctx = data; + + ngx_stream_lua_request_cleanup(ctx, 0 /* forcible */); +} + + +void +ngx_stream_lua_request_cleanup(ngx_stream_lua_ctx_t *ctx, int forcible) +{ + lua_State *L; + ngx_stream_lua_request_t *r; + ngx_stream_lua_main_conf_t *lmcf; + + /* force coroutine handling the request quit */ + if (ctx == NULL) { + dd("ctx is NULL"); + return; + } + + r = ctx->request; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua request cleanup: forcible=%d", forcible); + + if (ctx->cleanup) { + *ctx->cleanup = NULL; + ctx->cleanup = NULL; + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + +#if 1 + if (r->connection->fd == (ngx_socket_t) -1) { + /* being a fake request */ + + if (ctx->context == NGX_STREAM_LUA_CONTEXT_TIMER) { + /* being a timer handler */ + lmcf->running_timers--; + } + } +#endif + + L = ngx_stream_lua_get_lua_vm(r, ctx); + + ngx_stream_lua_finalize_threads(r, ctx, L); +} + + +/* + * description: + * run a Lua coroutine specified by ctx->cur_co_ctx->co + * return value: + * NGX_AGAIN: I/O interruption: r->main->count intact + * NGX_DONE: I/O interruption: r->main->count already incremented by 1 + * NGX_ERROR: error + * >= 200 HTTP status code + */ +ngx_int_t +ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, volatile int nrets) +{ + ngx_stream_lua_co_ctx_t *next_coctx, *parent_coctx, *orig_coctx; + + int rv, success = 1; + lua_State *next_co; + lua_State *old_co; + const char *err, *msg, *trace; + + +#if (NGX_PCRE) + ngx_pool_t *old_pool = NULL; +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread, top:%d", lua_gettop(L)); + + /* set Lua VM panic handler */ + lua_atpanic(L, ngx_stream_lua_atpanic); + + NGX_LUA_EXCEPTION_TRY { + + /* + * silence a -Werror=clobbered warning with gcc 5.4 + * due to above setjmp + */ + err = NULL; + msg = NULL; + trace = NULL; + + if (ctx->cur_co_ctx->thread_spawn_yielded) { + ngx_stream_lua_probe_info("thread spawn yielded"); + + ctx->cur_co_ctx->thread_spawn_yielded = 0; + nrets = 1; + } + + for ( ;; ) { + + dd("ctx: %p, co: %p, co status: %d, co is_wrap: %d", + ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status, + ctx->cur_co_ctx->is_wrap); + +#if (NGX_PCRE) + /* XXX: work-around to nginx regex subsystem */ + old_pool = ngx_stream_lua_pcre_malloc_init(r->pool); +#endif + + orig_coctx = ctx->cur_co_ctx; + +#ifdef NGX_LUA_USE_ASSERT + dd("%p: saved co top: %d, nrets: %d, true top: %d", + orig_coctx->co, + (int) orig_coctx->co_top, (int) nrets, + (int) lua_gettop(orig_coctx->co)); +#endif + +#if DDEBUG + if (lua_gettop(orig_coctx->co) > 0) { + dd("co top elem: %s", luaL_typename(orig_coctx->co, -1)); + } + + if (orig_coctx->propagate_error) { + dd("co propagate_error: %d", orig_coctx->propagate_error); + } +#endif + + if (orig_coctx->propagate_error) { + orig_coctx->propagate_error = 0; + goto propagate_error; + } + + ngx_stream_lua_assert(orig_coctx->co_top + nrets + == lua_gettop(orig_coctx->co)); + + rv = lua_resume(orig_coctx->co, nrets); + +#if (NGX_PCRE) + /* XXX: work-around to nginx regex subsystem */ + ngx_stream_lua_pcre_malloc_done(old_pool); +#endif + +#if 0 + /* test the longjmp thing */ + if (rand() % 2 == 0) { + NGX_LUA_EXCEPTION_THROW(1); + } +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua resume returned %d", rv); + + switch (rv) { + case LUA_YIELD: + /* yielded, let event handler do the rest job */ + /* FIXME: add io cmd dispatcher here */ + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua thread yielded"); + +#ifdef NGX_LUA_USE_ASSERT + dd("%p: saving curr top after yield: %d (co-op: %d)", + orig_coctx->co, + (int) lua_gettop(orig_coctx->co), (int) ctx->co_op); + orig_coctx->co_top = lua_gettop(orig_coctx->co); +#endif + + + if (ctx->exited) { + return ngx_stream_lua_handle_exit(L, r, ctx); + } + + + /* + * check if coroutine.resume or coroutine.yield called + * lua_yield() + */ + switch (ctx->co_op) { + + case NGX_STREAM_LUA_USER_CORO_NOP: + dd("hit! it is the API yield"); + + ngx_stream_lua_assert(lua_gettop(ctx->cur_co_ctx->co) == 0); + + ctx->cur_co_ctx = NULL; + + return NGX_AGAIN; + + case NGX_STREAM_LUA_USER_THREAD_RESUME: + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua user thread resume"); + + ctx->co_op = NGX_STREAM_LUA_USER_CORO_NOP; + nrets = lua_gettop(ctx->cur_co_ctx->co) - 1; + dd("nrets = %d", nrets); + +#ifdef NGX_LUA_USE_ASSERT + /* ignore the return value (the thread) already pushed */ + orig_coctx->co_top--; +#endif + + break; + + case NGX_STREAM_LUA_USER_CORO_RESUME: + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua coroutine: resume"); + + /* + * the target coroutine lies at the base of the + * parent's stack + */ + ctx->co_op = NGX_STREAM_LUA_USER_CORO_NOP; + + old_co = ctx->cur_co_ctx->parent_co_ctx->co; + + nrets = lua_gettop(old_co); + if (nrets) { + dd("moving %d return values to parent", nrets); + lua_xmove(old_co, ctx->cur_co_ctx->co, nrets); + +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->parent_co_ctx->co_top -= nrets; +#endif + } + + break; + + default: + /* ctx->co_op == NGX_STREAM_LUA_USER_CORO_YIELD */ + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua coroutine: yield"); + + ctx->co_op = NGX_STREAM_LUA_USER_CORO_NOP; + + if (ngx_stream_lua_is_thread(ctx)) { + ngx_stream_lua_probe_thread_yield(r, + ctx->cur_co_ctx->co); + + /* discard any return values from user + * coroutine.yield()'s arguments */ + lua_settop(ctx->cur_co_ctx->co, 0); + +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 0; +#endif + + ngx_stream_lua_probe_info("set co running"); + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + + if (ctx->posted_threads) { + ngx_stream_lua_post_thread(r, ctx, ctx->cur_co_ctx); + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* no pending threads, so resume the thread + * immediately */ + + nrets = 0; + continue; + } + + /* being a user coroutine that has a parent */ + + nrets = lua_gettop(ctx->cur_co_ctx->co); + + next_coctx = ctx->cur_co_ctx->parent_co_ctx; + next_co = next_coctx->co; + + if (nrets) { + dd("moving %d return values to next co", nrets); + lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top -= nrets; +#endif + } + + if (!ctx->cur_co_ctx->is_wrap) { + /* prepare return values for coroutine.resume + * (true plus any retvals) + */ + lua_pushboolean(next_co, 1); + lua_insert(next_co, 1); + nrets++; /* add the true boolean value */ + } + + ctx->cur_co_ctx = next_coctx; + + break; + } + + /* try resuming on the new coroutine again */ + continue; + + case 0: + + ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD; + + if (ctx->cur_co_ctx->zombie_child_threads) { + ngx_stream_lua_cleanup_zombie_child_uthreads(r, L, ctx, + ctx->cur_co_ctx); + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua light thread ended normally"); + + if (ngx_stream_lua_is_entry_thread(ctx)) { + + lua_settop(L, 0); + + ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + + dd("uthreads: %d", (int) ctx->uthreads); + + if (ctx->uthreads) { + + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* all user threads terminated already */ + goto done; + } + + if (ctx->cur_co_ctx->is_uthread) { + /* being a user thread */ + + lua_settop(L, 0); + + parent_coctx = ctx->cur_co_ctx->parent_co_ctx; + + if (ngx_stream_lua_coroutine_alive(parent_coctx)) { + if (ctx->cur_co_ctx->waited_by_parent) { + ngx_stream_lua_probe_info("parent already waiting"); + ctx->cur_co_ctx->waited_by_parent = 0; + success = 1; + goto user_co_done; + } + + ngx_stream_lua_probe_info("parent still alive"); + + if (ngx_stream_lua_post_zombie_thread(r, parent_coctx, + ctx->cur_co_ctx) + != NGX_OK) + { + return NGX_ERROR; + } + + lua_pushboolean(ctx->cur_co_ctx->co, 1); + lua_insert(ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_ZOMBIE; + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + ctx->uthreads--; + + if (ctx->uthreads == 0) { + if (ngx_stream_lua_entry_thread_alive(ctx)) { + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* all threads terminated already */ + goto done; + } + + /* some other user threads still running */ + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* being a user coroutine that has a parent */ + + success = 1; + +user_co_done: + + nrets = lua_gettop(ctx->cur_co_ctx->co); + + next_coctx = ctx->cur_co_ctx->parent_co_ctx; + + if (next_coctx == NULL) { + /* being a light thread */ + goto no_parent; + } + + next_co = next_coctx->co; + + if (nrets) { + lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); + } + + if (ctx->cur_co_ctx->is_uthread) { + ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + ctx->uthreads--; + } + + if (!ctx->cur_co_ctx->is_wrap) { + /* ended successfully, coroutine.resume returns true plus + * any return values + */ + lua_pushboolean(next_co, success); + lua_insert(next_co, 1); + nrets++; + } + + ctx->cur_co_ctx = next_coctx; + + ngx_stream_lua_probe_info("set parent running"); + + next_coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua coroutine: lua user thread ended normally"); + + continue; + + case LUA_ERRRUN: + err = "runtime error"; + break; + + case LUA_ERRSYNTAX: + err = "syntax error"; + break; + + case LUA_ERRMEM: + err = "[lua] memory allocation error"; + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, err); + abort(); + break; + + case LUA_ERRERR: + err = "error handler error"; + break; + + default: + err = "unknown error"; + break; + } + + if (ctx->cur_co_ctx != orig_coctx) { + ctx->cur_co_ctx = orig_coctx; + } + + ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0); + + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD; + + if (orig_coctx->is_uthread + || orig_coctx->is_wrap + || ngx_stream_lua_is_entry_thread(ctx)) + { + ngx_stream_lua_thread_traceback(L, orig_coctx->co, + orig_coctx); + trace = lua_tostring(L, -1); + + if (lua_isstring(orig_coctx->co, -1)) { + msg = lua_tostring(orig_coctx->co, -1); + dd("user custom error msg: %s", msg); + + } else { + msg = "unknown reason"; + } + } + +propagate_error: + + if (ctx->cur_co_ctx->is_uthread) { + ngx_stream_lua_assert(err != NULL && msg != NULL + && trace != NULL); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream lua user thread aborted: %s: %s\n%s", + err, msg, trace); + + lua_settop(L, 0); + + parent_coctx = ctx->cur_co_ctx->parent_co_ctx; + + if (ngx_stream_lua_coroutine_alive(parent_coctx)) { + if (ctx->cur_co_ctx->waited_by_parent) { + ctx->cur_co_ctx->waited_by_parent = 0; + success = 0; + goto user_co_done; + } + + if (ngx_stream_lua_post_zombie_thread(r, parent_coctx, + ctx->cur_co_ctx) + != NGX_OK) + { + return NGX_ERROR; + } + + lua_pushboolean(ctx->cur_co_ctx->co, 0); + lua_insert(ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_ZOMBIE; + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + ctx->uthreads--; + + if (ctx->uthreads == 0) { + if (ngx_stream_lua_entry_thread_alive(ctx)) { + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* all threads terminated already */ + goto done; + } + + /* some other user threads still running */ + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + if (ngx_stream_lua_is_entry_thread(ctx)) { + ngx_stream_lua_assert(err != NULL && msg != NULL + && trace != NULL); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua entry thread aborted: %s: %s\n%s", + err, msg, trace); + + lua_settop(L, 0); + + /* being the entry thread aborted */ + + + ngx_stream_lua_request_cleanup(ctx, 0); + + + if (ctx->no_abort) { + ctx->no_abort = 0; + return NGX_ERROR; + } + + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + /* being a user coroutine that has a parent */ + + next_coctx = ctx->cur_co_ctx->parent_co_ctx; + if (next_coctx == NULL) { + goto no_parent; + } + + next_co = next_coctx->co; + + ngx_stream_lua_probe_info("set parent running"); + + next_coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + + ctx->cur_co_ctx = next_coctx; + + if (orig_coctx->is_wrap) { + /* + * coroutine.wrap propagates errors + * to its parent coroutine + */ + next_coctx->propagate_error = 1; + continue; + } + + /* + * ended with error, coroutine.resume returns false plus + * err msg + */ + lua_pushboolean(next_co, 0); + lua_xmove(orig_coctx->co, next_co, 1); + nrets = 2; + + /* try resuming on the new coroutine again */ + continue; + } + + } NGX_LUA_EXCEPTION_CATCH { + dd("nginx execution restored"); + } + + return NGX_ERROR; + +no_parent: + + lua_settop(L, 0); + + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD; + + + ngx_stream_lua_request_cleanup(ctx, 0); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: " + "user coroutine has no parent"); + + return NGX_STREAM_INTERNAL_SERVER_ERROR; + +done: + + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_lua_wev_handler(ngx_stream_lua_request_t *r) +{ + ngx_int_t rc; + ngx_event_t *wev; + ngx_connection_t *c; + + ngx_stream_lua_ctx_t *ctx; + + ngx_stream_lua_srv_conf_t *cllscf; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + c = r->connection; + wev = c->write; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua run write event handler: timedout:%ud, ready:%ud, " + "writing_raw_req_socket:%ud", + wev->timedout, wev->ready, ctx->writing_raw_req_socket); + + cllscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (wev->timedout && !ctx->writing_raw_req_socket) { + if (!wev->delayed) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + c->timedout = 1; + + goto flush_coros; + } + + wev->timedout = 0; + wev->delayed = 0; + + if (!wev->ready) { + ngx_add_timer(wev, cllscf->send_timeout); + + if (ngx_handle_write_event(wev, cllscf->send_lowat) != NGX_OK) { + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, NGX_ERROR); + } + + return NGX_ERROR; + } + } + } + + if (!wev->ready && !wev->timedout) { + goto useless; + } + + if (ctx->writing_raw_req_socket) { + ctx->writing_raw_req_socket = 0; + + u = ctx->downstream; + if (u == NULL) { + return NGX_ERROR; + } + + u->write_event_handler(r, u); + return NGX_DONE; + } + + if (c->buffered) { + + rc = ngx_stream_lua_flush_pending_output(r, ctx); + + dd("flush pending output returned %d, c->error: %d", (int) rc, + c->error); + + if (rc != NGX_ERROR && rc != NGX_OK) { + goto useless; + } + + /* when rc == NGX_ERROR, c->error must be set */ + } + +flush_coros: + + dd("ctx->flushing_coros: %d", (int) ctx->flushing_coros); + + if (ctx->flushing_coros) { + return ngx_stream_lua_process_flushing_coroutines(r, ctx); + } + + /* ctx->flushing_coros == 0 */ + +useless: + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "useless lua write event handler"); + + if (ctx->entered_content_phase) { + return NGX_OK; + } + + return NGX_DONE; +} + + +static ngx_int_t +ngx_stream_lua_process_flushing_coroutines(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + ngx_int_t rc, n; + ngx_uint_t i; + ngx_list_part_t *part; + + ngx_stream_lua_co_ctx_t *coctx; + + dd("processing flushing coroutines"); + + coctx = &ctx->entry_co_ctx; + n = ctx->flushing_coros; + + if (coctx->flushing) { + coctx->flushing = 0; + + ctx->flushing_coros--; + n--; + ctx->cur_co_ctx = coctx; + + rc = ngx_stream_lua_flush_resume_helper(r, ctx); + if (rc == NGX_ERROR || rc >= NGX_OK) { + return rc; + } + + /* rc == NGX_DONE */ + } + + if (n) { + + if (ctx->user_co_ctx == NULL) { + return NGX_ERROR; + } + + part = &ctx->user_co_ctx->part; + coctx = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + coctx = part->elts; + i = 0; + } + + if (coctx[i].flushing) { + coctx[i].flushing = 0; + ctx->flushing_coros--; + n--; + ctx->cur_co_ctx = &coctx[i]; + + rc = ngx_stream_lua_flush_resume_helper(r, ctx); + if (rc == NGX_ERROR || rc >= NGX_OK) { + return rc; + } + + /* rc == NGX_DONE */ + + if (n == 0) { + return NGX_DONE; + } + } + } + } + + if (n) { + return NGX_ERROR; + } + + return NGX_DONE; +} + + +static ngx_int_t +ngx_stream_lua_flush_pending_output(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + ngx_int_t rc; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_connection_t *c; + + ngx_stream_lua_srv_conf_t *cllscf; + + c = r->connection; + wev = c->write; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua flushing output: buffered 0x%uxd", + c->buffered); + + if (ctx->busy_bufs) { + /* FIXME since cosockets also share this busy_bufs chain, this condition + * might not be strong enough. better use separate busy_bufs chains. */ + rc = ngx_stream_lua_output_filter(r, NULL); + + } else { + cl = ngx_stream_lua_get_flush_chain(r, ctx); + if (cl == NULL) { + return NGX_ERROR; + } + + rc = ngx_stream_lua_output_filter(r, cl); + } + + dd("output filter returned %d", (int) rc); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + if (c->buffered) { + + cllscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (!wev->delayed) { + ngx_add_timer(wev, cllscf->send_timeout); + } + + if (ngx_handle_write_event(wev, cllscf->send_lowat) != NGX_OK) { + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, NGX_ERROR); + } + + return NGX_ERROR; + } + + if (ctx->flushing_coros) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "lua flush still waiting: buffered 0x%uxd", + c->buffered); + + return NGX_DONE; + } + + } else { +#if 1 + if (wev->timer_set && !wev->delayed) { + ngx_del_timer(wev); + } +#endif + } + + return NGX_OK; +} + + +u_char * +ngx_stream_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len) +{ + ngx_md5_t md5; + u_char md5_buf[MD5_DIGEST_LENGTH]; + + ngx_md5_init(&md5); + ngx_md5_update(&md5, buf, buf_len); + ngx_md5_final(md5_buf, &md5); + + return ngx_hex_dump(dest, md5_buf, sizeof(md5_buf)); +} + + +void +ngx_stream_lua_set_multi_value_table(lua_State *L, int index) +{ + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + + lua_pushvalue(L, -2); /* stack: table key value key */ + lua_rawget(L, index); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* stack: table key value */ + lua_rawset(L, index); /* stack: table */ + + } else { + if (!lua_istable(L, -1)) { + /* just inserted one value */ + lua_createtable(L, 4, 0); + /* stack: table key value value table */ + lua_insert(L, -2); + /* stack: table key value table value */ + lua_rawseti(L, -2, 1); + /* stack: table key value table */ + lua_insert(L, -2); + /* stack: table key table value */ + + lua_rawseti(L, -2, 2); /* stack: table key table */ + + lua_rawset(L, index); /* stack: table */ + + } else { + /* stack: table key value table */ + lua_insert(L, -2); /* stack: table key table value */ + + lua_rawseti(L, -2, lua_objlen(L, -2) + 1); + /* stack: table key table */ + lua_pop(L, 2); /* stack: table */ + } + } +} + + +uintptr_t +ngx_stream_lua_escape_uri(u_char *dst, u_char *src, size_t size, + ngx_uint_t type) +{ + ngx_uint_t n; + uint32_t *escape; + static u_char hex[] = "0123456789ABCDEF"; + + /* " ", "#", "%", "?", %00-%1F, %7F-%FF */ + + static uint32_t uri[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", "%", "+", "?", %00-%1F, %7F-%FF */ + + static uint32_t args[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x80000829, /* 1000 0000 0000 0000 0000 1000 0010 1001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* not ALPHA, DIGIT, "-", ".", "_", "~" */ + + static uint32_t uri_component[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */ + + static uint32_t html[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x000000ad, /* 0000 0000 0000 0000 0000 0000 1010 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", """, "%", "'", %00-%1F, %7F-%FF */ + + static uint32_t refresh[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "%", %00-%1F */ + + static uint32_t memcached[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + /* mail_auth is the same as memcached */ + + static uint32_t *map[] = + { uri, args, uri_component, html, refresh, memcached, memcached }; + + escape = map[type]; + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + n++; + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + *dst++ = '%'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + size--; + } + + return (uintptr_t) dst; +} + + +/* XXX we also decode '+' to ' ' */ +void +ngx_stream_lua_unescape_uri(u_char **dst, u_char **src, size_t size, + ngx_uint_t type) +{ + u_char *d, *s, ch, c, decoded; + enum { + sw_usual = 0, + sw_quoted, + sw_quoted_second + } state; + + d = *dst; + s = *src; + + state = 0; + decoded = 0; + + while (size--) { + + ch = *s++; + + switch (state) { + case sw_usual: + if (ch == '?' + && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) + { + *d++ = ch; + goto done; + } + + if (ch == '%') { + state = sw_quoted; + break; + } + + if (ch == '+') { + *d++ = ' '; + break; + } + + *d++ = ch; + break; + + case sw_quoted: + + if (ch >= '0' && ch <= '9') { + decoded = (u_char) (ch - '0'); + state = sw_quoted_second; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + decoded = (u_char) (c - 'a' + 10); + state = sw_quoted_second; + break; + } + + /* the invalid quoted character */ + + state = sw_usual; + + *d++ = ch; + + break; + + case sw_quoted_second: + + state = sw_usual; + + if (ch >= '0' && ch <= '9') { + ch = (u_char) ((decoded << 4) + ch - '0'); + + if (type & NGX_UNESCAPE_REDIRECT) { + if (ch > '%' && ch < 0x7f) { + *d++ = ch; + break; + } + + *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); + break; + } + + *d++ = ch; + + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + ch = (u_char) ((decoded << 4) + c - 'a' + 10); + + if (type & NGX_UNESCAPE_URI) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + *d++ = ch; + break; + } + + if (type & NGX_UNESCAPE_REDIRECT) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + if (ch > '%' && ch < 0x7f) { + *d++ = ch; + break; + } + + *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); + break; + } + + *d++ = ch; + + break; + } + + /* the invalid quoted character */ + + break; + } + } + +done: + + *dst = d; + *src = s; +} + + +static int +ngx_stream_lua_req_socket(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + + r = ngx_stream_lua_get_req(L); + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_fake_request2(L, r, ctx); + + switch (r->connection->type) { + case SOCK_STREAM: + return ngx_stream_lua_req_socket_tcp(L); + + case SOCK_DGRAM: + return ngx_stream_lua_req_socket_udp(L); + } + + /* shouldn't happen */ + ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, + "stream unexpected connection type: %d", + r->connection->type); + + ngx_stream_lua_assert(0); + + return luaL_error(L, "unexpected connection type"); +} + + +void +ngx_stream_lua_inject_req_api(ngx_log_t *log, lua_State *L) +{ + /* ngx.req table */ + + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* .req */ + + + lua_pushcfunction(L, ngx_stream_lua_req_socket); + lua_setfield(L, -2, "socket"); + + lua_setfield(L, -2, "req"); +} + + + + +static ngx_int_t +ngx_stream_lua_handle_exit(lua_State *L, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua thread aborting request with status %d", + ctx->exit_code); + + ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD; + + + ngx_stream_lua_request_cleanup(ctx, 0); + + if (r->connection->fd == (ngx_socket_t) -1) { /* fake request */ + return ctx->exit_code; + } + + + return ctx->exit_code; +} + + +void +ngx_stream_lua_process_args_option(ngx_stream_lua_request_t *r, lua_State *L, + int table, ngx_str_t *args) +{ + u_char *key; + size_t key_len; + u_char *value; + size_t value_len; + size_t len = 0; + size_t key_escape = 0; + uintptr_t total_escape = 0; + int n; + int i; + u_char *p; + + if (table < 0) { + table = lua_gettop(L) + table + 1; + } + + n = 0; + lua_pushnil(L); + while (lua_next(L, table) != 0) { + if (lua_type(L, -2) != LUA_TSTRING) { + luaL_error(L, "attempt to use a non-string key in the " + "\"args\" option table"); + return; + } + + key = (u_char *) lua_tolstring(L, -2, &key_len); + + key_escape = 2 * ngx_stream_lua_escape_uri(NULL, key, key_len, + NGX_ESCAPE_URI_COMPONENT); + total_escape += key_escape; + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + case LUA_TSTRING: + value = (u_char *) lua_tolstring(L, -1, &value_len); + + total_escape += 2 * ngx_stream_lua_escape_uri(NULL, value, + value_len, + NGX_ESCAPE_URI_COMPONENT); + + len += key_len + value_len + (sizeof("=") - 1); + n++; + + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + len += key_len; + n++; + } + + break; + + case LUA_TTABLE: + + i = 0; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_isboolean(L, -1)) { + if (lua_toboolean(L, -1)) { + len += key_len; + + } else { + lua_pop(L, 1); + continue; + } + + } else { + value = (u_char *) lua_tolstring(L, -1, &value_len); + + if (value == NULL) { + luaL_error(L, "attempt to use %s as query arg value", + luaL_typename(L, -1)); + return; + } + + total_escape += + 2 * ngx_stream_lua_escape_uri(NULL, value, + value_len, + NGX_ESCAPE_URI_COMPONENT); + + len += key_len + value_len + (sizeof("=") - 1); + } + + if (i++ > 0) { + total_escape += key_escape; + } + + n++; + lua_pop(L, 1); + } + + break; + + default: + luaL_error(L, "attempt to use %s as query arg value", + luaL_typename(L, -1)); + return; + } + + lua_pop(L, 1); + } + + len += (size_t) total_escape; + + if (n > 1) { + len += (n - 1) * (sizeof("&") - 1); + } + + dd("len 1: %d", (int) len); + + if (r) { + p = ngx_palloc(r->pool, len); + if (p == NULL) { + luaL_error(L, "no memory"); + return; + } + + } else { + p = lua_newuserdata(L, len); + } + + args->data = p; + args->len = len; + + i = 0; + lua_pushnil(L); + while (lua_next(L, table) != 0) { + key = (u_char *) lua_tolstring(L, -2, &key_len); + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + case LUA_TSTRING: + + if (total_escape) { + p = (u_char *) ngx_stream_lua_escape_uri(p, key, key_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + *p++ = '='; + + value = (u_char *) lua_tolstring(L, -1, &value_len); + + if (total_escape) { + p = (u_char *) ngx_stream_lua_escape_uri(p, value, value_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + p = ngx_copy(p, value, value_len); + } + + if (i != n - 1) { + /* not the last pair */ + *p++ = '&'; + } + + i++; + + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + if (total_escape) { + p = (u_char *) ngx_stream_lua_escape_uri(p, key, key_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + if (i != n - 1) { + /* not the last pair */ + *p++ = '&'; + } + + i++; + } + + break; + + case LUA_TTABLE: + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + + if (lua_isboolean(L, -1)) { + if (lua_toboolean(L, -1)) { + if (total_escape) { + p = (u_char *) ngx_stream_lua_escape_uri(p, key, + key_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + } else { + lua_pop(L, 1); + continue; + } + + } else { + + if (total_escape) { + p = (u_char *) + ngx_stream_lua_escape_uri(p, key, + key_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + *p++ = '='; + + value = (u_char *) lua_tolstring(L, -1, &value_len); + + if (total_escape) { + p = (u_char *) + ngx_stream_lua_escape_uri(p, value, + value_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + p = ngx_copy(p, value, value_len); + } + } + + if (i != n - 1) { + /* not the last pair */ + *p++ = '&'; + } + + i++; + lua_pop(L, 1); + } + + break; + + default: + luaL_error(L, "should not reach here"); + return; + } + + lua_pop(L, 1); + } + + if (p - args->data != (ssize_t) len) { + luaL_error(L, "buffer error: %d != %d", + (int) (p - args->data), (int) len); + return; + } +} + + + +/* XXX ngx_open_and_stat_file is static in the core. sigh. */ +ngx_int_t +ngx_stream_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, + ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_file_info_t fi; + + if (of->fd != NGX_INVALID_FILE) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (of->uniq == ngx_file_uniq(&fi)) { + goto done; + } + + } else if (of->test_dir) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (ngx_is_dir(&fi)) { + goto done; + } + } + + if (!of->log) { + + /* + * Use non-blocking open() not to hang on FIFO files, etc. + * This flag has no effect on a regular files. + */ + + fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + } else { + fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + } + + if (fd == NGX_INVALID_FILE) { + of->failed = ngx_open_file_n; + goto failed; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + return NGX_ERROR; + } + + if (ngx_is_dir(&fi)) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + } else { + of->fd = fd; + + if (of->directio <= ngx_file_size(&fi)) { + if (ngx_directio_on(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_on_n " \"%s\" failed", name); + + } else { + of->is_directio = 1; + } + } + } + +done: + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->fs_size = ngx_file_fs_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + +failed: + + of->fd = NGX_INVALID_FILE; + of->err = ngx_errno; + + return NGX_ERROR; +} + + +ngx_chain_t * +ngx_stream_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p, + ngx_chain_t **free, size_t len) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + u_char *start, *end; + + const ngx_buf_tag_t tag = (ngx_buf_tag_t) &ngx_stream_lua_module; + + if (*free) { + cl = *free; + *free = cl->next; + cl->next = NULL; + + b = cl->buf; + start = b->start; + end = b->end; + if (start && (size_t) (end - start) >= len) { + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0, + "lua reuse free buf memory %O >= %uz, cl:%p, p:%p", + (off_t) (end - start), len, cl, start); + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->start = start; + b->pos = start; + b->last = start; + b->end = end; + b->tag = tag; + + if (len) { + b->temporary = 1; + } + + return cl; + } + + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0, + "lua reuse free buf chain, but reallocate memory " + "because %uz >= %O, cl:%p, p:%p", len, + (off_t) (b->end - b->start), cl, b->start); + + if (ngx_buf_in_memory(b) && b->start) { + ngx_pfree(p, b->start); + } + + ngx_memzero(b, sizeof(ngx_buf_t)); + + if (len == 0) { + return cl; + } + + b->start = ngx_palloc(p, len); + if (b->start == NULL) { + return NULL; + } + + b->end = b->start + len; + + dd("buf start: %p", cl->buf->start); + + b->pos = b->start; + b->last = b->start; + b->tag = tag; + b->temporary = 1; + + return cl; + } + + cl = ngx_alloc_chain_link(p); + if (cl == NULL) { + return NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0, + "lua allocate new chainlink and new buf of size %uz, cl:%p", + len, cl); + + cl->buf = len ? ngx_create_temp_buf(p, len) : ngx_calloc_buf(p); + if (cl->buf == NULL) { + return NULL; + } + + dd("buf start: %p", cl->buf->start); + + cl->buf->tag = tag; + cl->next = NULL; + + return cl; +} + + +static int +ngx_stream_lua_thread_traceback(lua_State *L, lua_State *co, + ngx_stream_lua_co_ctx_t *coctx) +{ + int base; + int level, coid; + lua_Debug ar; + + base = lua_gettop(L); + lua_checkstack(L, 3); + lua_pushliteral(L, "stack traceback:"); + coid = 0; + + while (co) { + + if (coid >= NGX_STREAM_LUA_BT_MAX_COROS) { + break; + } + + lua_checkstack(L, 2); + lua_pushfstring(L, "\ncoroutine %d:", coid++); + + level = 0; + + while (lua_getstack(co, level++, &ar)) { + + lua_checkstack(L, 5); + + if (level > NGX_STREAM_LUA_BT_DEPTH) { + lua_pushliteral(L, "\n\t..."); + break; + } + + lua_pushliteral(L, "\n\t"); + lua_getinfo(co, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + + if (ar.currentline > 0) { + lua_pushfstring(L, "%d:", ar.currentline); + } + + if (*ar.namewhat != '\0') { /* is there a name? */ + lua_pushfstring(L, " in function " LUA_QS, ar.name); + + } else { + if (*ar.what == 'm') { /* main? */ + lua_pushliteral(L, " in main chunk"); + + } else if (*ar.what == 'C' || *ar.what == 't') { + lua_pushliteral(L, " ?"); /* C function or tail call */ + + } else { + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + } + } + + if (lua_gettop(L) - base >= 15) { + lua_concat(L, lua_gettop(L) - base); + } + + /* check if the coroutine has a parent coroutine*/ + coctx = coctx->parent_co_ctx; + if (!coctx || coctx->co_status == NGX_STREAM_LUA_CO_DEAD) { + break; + } + + co = coctx->co; + } + + lua_concat(L, lua_gettop(L) - base); + return 1; +} + + +int +ngx_stream_lua_traceback(lua_State *L) +{ + if (!lua_isstring(L, 1)) { /* 'message' not a string? */ + return 1; /* keep it intact */ + } + + lua_getglobal(L, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + + + + +ngx_stream_lua_co_ctx_t * +ngx_stream_lua_get_co_ctx(lua_State *L, ngx_stream_lua_ctx_t *ctx) +{ + ngx_uint_t i; + ngx_list_part_t *part; + + ngx_stream_lua_co_ctx_t *coctx; + + if (L == ctx->entry_co_ctx.co) { + return &ctx->entry_co_ctx; + } + + if (ctx->user_co_ctx == NULL) { + return NULL; + } + + part = &ctx->user_co_ctx->part; + coctx = part->elts; + + /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */ + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + coctx = part->elts; + i = 0; + } + + if (coctx[i].co == L) { + return &coctx[i]; + } + } + + return NULL; +} + + +ngx_stream_lua_co_ctx_t * +ngx_stream_lua_create_co_ctx(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + ngx_stream_lua_co_ctx_t *coctx; + + if (ctx->user_co_ctx == NULL) { + ctx->user_co_ctx = ngx_list_create(r->pool, 4, + sizeof(ngx_stream_lua_co_ctx_t)); + if (ctx->user_co_ctx == NULL) { + return NULL; + } + } + + coctx = ngx_list_push(ctx->user_co_ctx); + if (coctx == NULL) { + return NULL; + } + + ngx_memzero(coctx, sizeof(ngx_stream_lua_co_ctx_t)); + + coctx->co_ref = LUA_NOREF; + + return coctx; +} + + +/* this is for callers other than the content handler */ +ngx_int_t +ngx_stream_lua_run_posted_threads(ngx_connection_t *c, lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, ngx_uint_t nreqs) +{ + ngx_int_t rc; + + ngx_stream_lua_posted_thread_t *pt; + + for ( ;; ) { + if (c->destroyed || c->requests != nreqs) { + return NGX_DONE; + } + + pt = ctx->posted_threads; + if (pt == NULL) { + return NGX_DONE; + } + + ctx->posted_threads = pt->next; + + ngx_stream_lua_probe_run_posted_thread(r, pt->co_ctx->co, + (int) pt->co_ctx->co_status); + + if (pt->co_ctx->co_status != NGX_STREAM_LUA_CO_RUNNING) { + continue; + } + + ctx->cur_co_ctx = pt->co_ctx; + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + continue; + } + + /* rc == NGX_ERROR || rc >= NGX_OK */ + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + } + + return rc; + } + + /* impossible to reach here */ +} + + +ngx_int_t +ngx_stream_lua_post_thread(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx) +{ + ngx_stream_lua_posted_thread_t **p; + ngx_stream_lua_posted_thread_t *pt; + + pt = ngx_palloc(r->pool, sizeof(ngx_stream_lua_posted_thread_t)); + if (pt == NULL) { + return NGX_ERROR; + } + + pt->co_ctx = coctx; + pt->next = NULL; + + for (p = &ctx->posted_threads; *p; p = &(*p)->next) { /* void */ } + + *p = pt; + + return NGX_OK; +} + + +static void +ngx_stream_lua_finalize_threads(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, lua_State *L) +{ +#ifdef NGX_LUA_USE_ASSERT + int top; +#endif + int inited = 0, ref; + ngx_uint_t i; + ngx_list_part_t *part; + + ngx_stream_lua_co_ctx_t *cc, *coctx; + +#ifdef NGX_LUA_USE_ASSERT + top = lua_gettop(L); +#endif + +#if 1 + coctx = ctx->on_abort_co_ctx; + if (coctx && coctx->co_ref != LUA_NOREF) { + if (coctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) { + /* the on_abort thread contributes to the coctx->uthreads + * counter only when it actually starts running */ + ngx_stream_lua_cleanup_pending_operation(coctx); + ctx->uthreads--; + } + + ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx); + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + inited = 1; + + luaL_unref(L, -1, coctx->co_ref); + coctx->co_ref = LUA_NOREF; + + coctx->co_status = NGX_STREAM_LUA_CO_DEAD; + ctx->on_abort_co_ctx = NULL; + } +#endif + + if (ctx->user_co_ctx) { + part = &ctx->user_co_ctx->part; + cc = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + cc = part->elts; + i = 0; + } + + coctx = &cc[i]; + + ref = coctx->co_ref; + + if (ref != LUA_NOREF) { + ngx_stream_lua_cleanup_pending_operation(coctx); + + ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx); + + if (!inited) { + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + inited = 1; + } + + ngx_stream_lua_assert(lua_gettop(L) - top == 1); + + luaL_unref(L, -1, ref); + coctx->co_ref = LUA_NOREF; + + coctx->co_status = NGX_STREAM_LUA_CO_DEAD; + ctx->uthreads--; + } + } + + ctx->user_co_ctx = NULL; + } + + ngx_stream_lua_assert(ctx->uthreads == 0); + + coctx = &ctx->entry_co_ctx; + + ref = coctx->co_ref; + if (ref != LUA_NOREF) { + ngx_stream_lua_cleanup_pending_operation(coctx); + + ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx); + + if (!inited) { + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + inited = 1; + } + + ngx_stream_lua_assert(lua_gettop(L) - top == 1); + + luaL_unref(L, -1, coctx->co_ref); + coctx->co_ref = LUA_NOREF; + coctx->co_status = NGX_STREAM_LUA_CO_DEAD; + } + + if (inited) { + lua_pop(L, 1); + } +} + + +static ngx_int_t +ngx_stream_lua_post_zombie_thread(ngx_stream_lua_request_t *r, + ngx_stream_lua_co_ctx_t *parent, ngx_stream_lua_co_ctx_t *thread) +{ + ngx_stream_lua_posted_thread_t **p; + ngx_stream_lua_posted_thread_t *pt; + + pt = ngx_palloc(r->pool, sizeof(ngx_stream_lua_posted_thread_t)); + if (pt == NULL) { + return NGX_ERROR; + } + + pt->co_ctx = thread; + pt->next = NULL; + + for (p = &parent->zombie_child_threads; *p; p = &(*p)->next) { /* void */ } + + *p = pt; + + return NGX_OK; +} + + +static void +ngx_stream_lua_cleanup_zombie_child_uthreads(ngx_stream_lua_request_t *r, + lua_State *L, ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx) +{ + ngx_stream_lua_posted_thread_t *pt; + + for (pt = coctx->zombie_child_threads; pt; pt = pt->next) { + if (pt->co_ctx->co_ref != LUA_NOREF) { + ngx_stream_lua_del_thread(r, L, ctx, pt->co_ctx); + ctx->uthreads--; + } + } + + coctx->zombie_child_threads = NULL; +} + + +ngx_int_t +ngx_stream_lua_check_broken_connection(ngx_stream_lua_request_t *r, + ngx_event_t *ev) +{ + int n; + char buf[1]; + ngx_err_t err; + ngx_int_t event; + ngx_connection_t *c; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ev->log, 0, + "stream lua check client, write event:%d", ev->write); + + c = r->connection; + + if (c->error) { + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + + event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; + + if (ngx_del_event(ev, event, 0) != NGX_OK) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + } + + return NGX_ERROR; + } + + + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + + if (!ev->pending_eof) { + return NGX_OK; + } + + ev->eof = 1; + + if (ev->kq_errno) { + ev->error = 1; + } + + ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, + "kevent() reported that client prematurely closed " + "connection"); + + return NGX_ERROR; + } + +#endif + + n = recv(c->fd, buf, 1, MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ev->log, err, + "http lua recv(): %d", n); + + if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { + return NGX_OK; + } + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + dd("event is active"); + + event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; + +#if 1 + if (ngx_del_event(ev, event, 0) != NGX_OK) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } +#endif + } + + dd("HERE %d", (int) n); + + if (n > 0) { + return NGX_OK; + } + + if (n == -1) { + if (err == NGX_EAGAIN) { + dd("HERE"); + return NGX_OK; + } + + ev->error = 1; + + } else { /* n == 0 */ + err = 0; + } + + ev->eof = 1; + + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "stream client prematurely closed connection"); + + return NGX_ERROR; +} + + +void +ngx_stream_lua_rd_check_broken_connection(ngx_stream_lua_request_t *r) +{ + ngx_int_t rc; + ngx_event_t *rev; + ngx_stream_lua_ctx_t *ctx; + + + rc = ngx_stream_lua_check_broken_connection(r, r->connection->read); + + if (rc == NGX_OK) { + return; + } + + /* rc == NGX_ERROR || rc > NGX_OK */ + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + if (ctx->on_abort_co_ctx == NULL) { + r->connection->error = 1; + ngx_stream_lua_request_cleanup(ctx, 0); + ngx_stream_lua_finalize_request(r, rc); + return; + } + + if (ctx->on_abort_co_ctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) { + + /* on_abort already run for the current request handler */ + + rev = r->connection->read; + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { + ngx_stream_lua_request_cleanup(ctx, 0); + + ngx_stream_lua_finalize_request(r, + NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + return; + } + + ctx->uthreads++; + ctx->resume_handler = ngx_stream_lua_on_abort_resume; + ctx->on_abort_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + ctx->cur_co_ctx = ctx->on_abort_co_ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua waking up the on_abort callback thread"); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + r->write_event_handler(r); +} + + +static ngx_int_t +ngx_stream_lua_on_abort_resume(ngx_stream_lua_request_t *r) +{ + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_stream_lua_wev_handler; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua resuming the on_abort callback thread"); + +#if 0 + ngx_stream_lua_probe_info("tcp resume"); +#endif + + c = r->connection; + vm = ngx_stream_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_stream_lua_run_thread(vm, r, ctx, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_stream_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + + + +void +ngx_stream_lua_finalize_request(ngx_stream_lua_request_t *r, ngx_int_t rc) +{ + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx && ctx->cur_co_ctx) { + ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); + } + + if (r->connection->fd != (ngx_socket_t) -1) { + + ngx_stream_lua_finalize_real_request(r, rc); + + return; + } + + ngx_stream_lua_finalize_fake_request(r, rc); +} + + +void +ngx_stream_lua_finalize_fake_request(ngx_stream_lua_request_t *r, ngx_int_t rc) +{ + ngx_connection_t *c; + +#if (NGX_STREAM_SSL) + ngx_ssl_conn_t *ssl_conn; + + ngx_stream_lua_ssl_ctx_t *cctx; +#endif + + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua finalize fake request: %d", rc); + + if (rc == NGX_DONE) { + + + return; + } + + if (rc == NGX_ERROR || rc >= NGX_STREAM_BAD_REQUEST) { + +#if (NGX_STREAM_SSL) + + if (r->connection->ssl) { + ssl_conn = r->connection->ssl->connection; + if (ssl_conn) { + c = ngx_ssl_get_connection(ssl_conn); + + if (c && c->ssl) { + cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection); + if (cctx != NULL) { + cctx->exit_code = 0; + } + } + } + } + +#endif + + ngx_stream_lua_close_fake_request(r); + return; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + c->write->delayed = 0; + ngx_del_timer(c->write); + } + + ngx_stream_lua_close_fake_request(r); +} + + +static void +ngx_stream_lua_close_fake_request(ngx_stream_lua_request_t *r) +{ + ngx_connection_t *c; + + + c = r->connection; + + + + ngx_stream_lua_free_fake_request(r); + ngx_stream_lua_close_fake_connection(c); +} + + +void +ngx_stream_lua_free_fake_request(ngx_stream_lua_request_t *r) +{ + ngx_log_t *log; + + ngx_stream_lua_cleanup_t *cln; + + log = r->connection->log; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0, "stream lua close fake " + "request"); + + if (r->pool == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "stream lua fake request " + "already closed"); + return; + } + + cln = r->cleanup; + r->cleanup = NULL; + + while (cln) { + if (cln->handler) { + cln->handler(cln->data); + } + + cln = cln->next; + } + + + r->connection->destroyed = 1; +} + + +void +ngx_stream_lua_close_fake_connection(ngx_connection_t *c) +{ + ngx_pool_t *pool; + ngx_connection_t *saved_c = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream lua close fake stream connection %p", c); + + c->destroyed = 1; + + pool = c->pool; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + c->read->closed = 1; + c->write->closed = 1; + + /* we temporarily use a valid fd (0) to make ngx_free_connection happy */ + + c->fd = 0; + + if (ngx_cycle->files) { + saved_c = ngx_cycle->files[0]; + } + + ngx_free_connection(c); + + c->fd = (ngx_socket_t) -1; + + if (ngx_cycle->files) { + ngx_cycle->files[0] = saved_c; + } + + if (pool) { + ngx_destroy_pool(pool); + } +} + + +ngx_int_t +ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_pool_t *pool, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log, + ngx_pool_cleanup_t **pcln) +{ + int rc; + lua_State *L; + ngx_uint_t i; + ngx_pool_cleanup_t *cln; + + ngx_stream_lua_preload_hook_t *hook; + ngx_stream_lua_vm_state_t *state; + + cln = ngx_pool_cleanup_add(pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + /* create new Lua VM instance */ + L = ngx_stream_lua_new_state(parent_vm, cycle, lmcf, log); + if (L == NULL) { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, "lua initialize the " + "global Lua VM %p", L); + + /* register cleanup handler for Lua VM */ + cln->handler = ngx_stream_lua_cleanup_vm; + + state = ngx_alloc(sizeof(ngx_stream_lua_vm_state_t), log); + if (state == NULL) { + return NGX_ERROR; + } + state->vm = L; + state->count = 1; + + cln->data = state; + + if (lmcf->vm_cleanup == NULL) { + /* this assignment will happen only once, + * and also only for the main Lua VM */ + lmcf->vm_cleanup = cln; + } + + if (pcln) { + *pcln = cln; + } + +#ifdef OPENRESTY_LUAJIT + /* load FFI library first since cdata needs it */ + luaopen_ffi(L); +#endif + + if (lmcf->preload_hooks) { + + /* register the 3rd-party module's preload hooks */ + + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + + hook = lmcf->preload_hooks->elts; + + for (i = 0; i < lmcf->preload_hooks->nelts; i++) { + + ngx_stream_lua_probe_register_preload_package(L, + hook[i].package); + + lua_pushcfunction(L, hook[i].loader); + lua_setfield(L, -2, (char *) hook[i].package); + } + + lua_pop(L, 2); + } + + *new_vm = L; + + lua_getglobal(L, "require"); + lua_pushstring(L, "resty.core"); + + rc = lua_pcall(L, 1, 1, 0); + if (rc != 0) { + return NGX_DECLINED; + } + +#ifdef OPENRESTY_LUAJIT + ngx_stream_lua_inject_global_write_guard(L, log); +#endif + + return NGX_OK; +} + + +void +ngx_stream_lua_cleanup_vm(void *data) +{ + lua_State *L; + ngx_stream_lua_vm_state_t *state = data; + +#if (DDEBUG) + if (state) { + dd("cleanup VM: c:%d, s:%p", (int) state->count, state->vm); + } +#endif + + if (state) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua decrementing the reference count " + "for Lua VM: %i", state->count); + + if (--state->count == 0) { + L = state->vm; + + ngx_stream_lua_cleanup_conn_pools(L); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua close the global Lua VM %p", L); + lua_close(L); + ngx_free(state); + } + } +} + + +ngx_connection_t * +ngx_stream_lua_create_fake_connection(ngx_pool_t *pool) +{ + ngx_log_t *log; + ngx_connection_t *c; + ngx_connection_t *saved_c = NULL; + + /* (we temporarily use a valid fd (0) to make ngx_get_connection happy) */ + if (ngx_cycle->files) { + saved_c = ngx_cycle->files[0]; + } + + c = ngx_get_connection(0, ngx_cycle->log); + + if (ngx_cycle->files) { + ngx_cycle->files[0] = saved_c; + } + + if (c == NULL) { + return NULL; + } + + c->fd = (ngx_socket_t) -1; + c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + + if (pool) { + c->pool = pool; + + } else { + c->pool = ngx_create_pool(128, c->log); + if (c->pool == NULL) { + goto failed; + } + } + + log = ngx_pcalloc(c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + goto failed; + } + + c->log = log; + c->log->connection = c->number; + c->log->action = NULL; + c->log->data = NULL; + + c->log_error = NGX_ERROR_INFO; + +#if 0 + c->buffer = ngx_create_temp_buf(c->pool, 2); + if (c->buffer == NULL) { + goto failed; + } + + c->buffer->start[0] = CR; + c->buffer->start[1] = LF; +#endif + + c->error = 1; + + dd("created fake connection: %p", c); + + return c; + +failed: + + ngx_stream_lua_close_fake_connection(c); + return NULL; +} + + +ngx_stream_session_t * +ngx_stream_lua_create_fake_session(ngx_connection_t *c) +{ + ngx_stream_session_t *s; + + s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); + if (s == NULL) { + return NULL; + } + + s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); + if (s->ctx == NULL) { + return NULL; + } + + s->connection = c; + + c->data = s; + s->signature = NGX_STREAM_MODULE; + + dd("created fake session %p", s); + + return s; +} + + +ngx_stream_lua_request_t * +ngx_stream_lua_create_fake_request(ngx_stream_session_t *s) +{ + ngx_stream_lua_request_t *r; + + r = ngx_pcalloc(s->connection->pool, sizeof(ngx_stream_lua_request_t)); + if (r == NULL) { + return NULL; + } + + r->connection = s->connection; + r->session = s; + r->pool = s->connection->pool; + + return r; +} + + +ngx_int_t +ngx_stream_lua_report(ngx_log_t *log, lua_State *L, int status, + const char *prefix) +{ + const char *msg; + + if (status && !lua_isnil(L, -1)) { + msg = lua_tostring(L, -1); + if (msg == NULL) { + msg = "unknown error"; + } + + ngx_log_error(NGX_LOG_ERR, log, 0, "%s error: %s", prefix, msg); + lua_pop(L, 1); + } + + /* force a full garbage-collection cycle */ + lua_gc(L, LUA_GCCOLLECT, 0); + + return status == 0 ? NGX_OK : NGX_ERROR; +} + + +int +ngx_stream_lua_do_call(ngx_log_t *log, lua_State *L) +{ + int status, base; +#if (NGX_PCRE) + ngx_pool_t *old_pool; +#endif + + base = lua_gettop(L); /* function index */ + lua_pushcfunction(L, ngx_stream_lua_traceback); + /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ + +#if (NGX_PCRE) + old_pool = ngx_stream_lua_pcre_malloc_init(ngx_cycle->pool); +#endif + + status = lua_pcall(L, 0, 0, base); + +#if (NGX_PCRE) + ngx_stream_lua_pcre_malloc_done(old_pool); +#endif + + lua_remove(L, base); + + return status; +} + + +static int +ngx_stream_lua_get_raw_phase_context(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + +#ifdef OPENRESTY_LUAJIT + r = lua_getexdata(L); +#else + r = lua_touserdata(L, 1); +#endif + + if (r == NULL) { + return 0; + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return 0; + } + + lua_pushinteger(L, (int) ctx->context); + return 1; +} + + + + +void +ngx_stream_lua_cleanup_free(ngx_stream_lua_request_t *r, + ngx_stream_lua_cleanup_pt *cleanup) +{ + ngx_stream_lua_cleanup_t **last; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_ctx_t *ctx; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + + cln = (ngx_stream_lua_cleanup_t *) + ((u_char *) cleanup - offsetof(ngx_stream_lua_cleanup_t, handler)); + + dd("cln: %p, cln->handler: %p, &cln->handler: %p", + cln, cln->handler, &cln->handler); + + last = &r->cleanup; + + while (*last) { + if (*last == cln) { + *last = cln->next; + + cln->next = ctx->free_cleanup; + ctx->free_cleanup = cln; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "lua stream cleanup free: %p", cln); + + return; + } + + last = &(*last)->next; + } +} + + +#if (NGX_STREAM_LUA_HAVE_SA_RESTART) +void +ngx_stream_lua_set_sa_restart(ngx_log_t *log) +{ + int *signo; + int sigs[] = NGX_STREAM_LUA_SA_RESTART_SIGS; + struct sigaction act; + + for (signo = sigs; *signo != 0; signo++) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "setting SA_RESTART for signal %d", *signo); + + if (sigaction(*signo, NULL, &act) != 0) { + ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to get " + "sigaction for signal %d", *signo); + } + + act.sa_flags |= SA_RESTART; + + if (sigaction(*signo, &act, NULL) != 0) { + ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to set " + "sigaction for signal %d", *signo); + } + } +} +#endif + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_util.h b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_util.h new file mode 100644 index 000000000..61d0727bc --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_util.h @@ -0,0 +1,528 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_util.h.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_UTIL_H_INCLUDED_ +#define _NGX_STREAM_LUA_UTIL_H_INCLUDED_ + + +#ifdef DDEBUG +#include "ddebug.h" +#endif + + +#include "ngx_stream_lua_common.h" +#include "ngx_stream_lua_ssl.h" +#include "ngx_stream_lua_api.h" + + +#ifndef NGX_UNESCAPE_URI_COMPONENT +#define NGX_UNESCAPE_URI_COMPONENT 0 +#endif + + +typedef struct { + ngx_stream_lua_ffi_str_t key; + ngx_stream_lua_ffi_str_t value; +} ngx_stream_lua_ffi_table_elt_t; + + +/* char whose address we use as the key in Lua vm registry for + * user code cache table */ +extern char ngx_stream_lua_code_cache_key; + + +/* key in Lua vm registry for all the "ngx.ctx" tables */ +#define ngx_stream_lua_ctx_tables_key "ngx_lua_ctx_tables" + + +/* char whose address we use as the key in Lua vm registry for + * regex cache table */ +extern char ngx_stream_lua_regex_cache_key; + +/* char whose address we use as the key in Lua vm registry for + * socket connection pool table */ +extern char ngx_stream_lua_socket_pool_key; + +/* char whose address we use as the key for the coroutine parent relationship */ +extern char ngx_stream_lua_coroutine_parents_key; + +/* coroutine anchoring table key in Lua VM registry */ +extern char ngx_stream_lua_coroutines_key; + +/* key to the metatable for ngx.req.get_headers() and ngx.resp.get_headers() */ +extern char ngx_stream_lua_headers_metatable_key; + + +#ifndef ngx_str_set +#define ngx_str_set(str, text) \ + (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text +#endif + + +#define NGX_STREAM_LUA_CONTEXT_YIELDABLE (NGX_STREAM_LUA_CONTEXT_PREREAD \ + | NGX_STREAM_LUA_CONTEXT_CONTENT \ + | NGX_STREAM_LUA_CONTEXT_TIMER \ + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO \ + | NGX_STREAM_LUA_CONTEXT_SSL_CERT) + + +#define ngx_stream_lua_context_name(c) \ + ((c) == NGX_STREAM_LUA_CONTEXT_CONTENT ? "content_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_LOG ? "log_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_TIMER ? "ngx.timer" \ + : (c) == NGX_STREAM_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_PREREAD ? "preread_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ + "ssl_client_hello_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ + : "(unknown)") + + +#define ngx_stream_lua_check_context(L, ctx, flags) \ + if (!((ctx)->context & (flags))) { \ + return luaL_error(L, "API disabled in the context of %s", \ + ngx_stream_lua_context_name((ctx)->context)); \ + } + + +static ngx_inline ngx_int_t +ngx_stream_lua_ffi_check_context(ngx_stream_lua_ctx_t *ctx, + unsigned flags, u_char *err, size_t *errlen) +{ + if (!(ctx->context & flags)) { + *errlen = ngx_snprintf(err, *errlen, + "API disabled in the context of %s", + ngx_stream_lua_context_name((ctx)->context)) + - err; + + return NGX_DECLINED; + } + + return NGX_OK; +} + + +#define ngx_stream_lua_check_fake_request(L, r) \ + if ((r)->connection->fd == (ngx_socket_t) -1) { \ + return luaL_error(L, "API disabled in the current context"); \ + } + + +#define ngx_stream_lua_check_fake_request2(L, r, ctx) \ + if ((r)->connection->fd == (ngx_socket_t) -1) { \ + return luaL_error(L, "API disabled in the context of %s", \ + ngx_stream_lua_context_name((ctx) \ + ->context)); \ + } + + +#define ngx_stream_lua_ssl_get_ctx(ssl_conn) \ + SSL_get_ex_data(ssl_conn, ngx_stream_lua_ssl_ctx_index) + + +ngx_int_t ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_pool_t *pool, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log, + ngx_pool_cleanup_t **pcln); + +lua_State *ngx_stream_lua_new_thread(ngx_stream_lua_request_t *r, lua_State *l, + int *ref); + +u_char *ngx_stream_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len); + +ngx_int_t ngx_stream_lua_send_header_if_needed(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx); + +ngx_int_t ngx_stream_lua_send_chain_link(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_chain_t *cl); + +void ngx_stream_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in); + +ngx_int_t ngx_stream_lua_add_copy_chain(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in, + ngx_int_t *eof); + +void ngx_stream_lua_reset_ctx(ngx_stream_lua_request_t *r, lua_State *L, + ngx_stream_lua_ctx_t *ctx); + +void ngx_stream_lua_generic_phase_post_read(ngx_stream_lua_request_t *r); + +void ngx_stream_lua_request_cleanup(ngx_stream_lua_ctx_t *ctx, int foricible); + +void ngx_stream_lua_request_cleanup_handler(void *data); + +ngx_int_t ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, volatile int nret); + +ngx_int_t ngx_stream_lua_wev_handler(ngx_stream_lua_request_t *r); + +u_char *ngx_stream_lua_digest_hex(u_char *dest, const u_char *buf, + int buf_len); + +void ngx_stream_lua_set_multi_value_table(lua_State *L, int index); + +void ngx_stream_lua_unescape_uri(u_char **dst, u_char **src, size_t size, + ngx_uint_t type); + +uintptr_t ngx_stream_lua_escape_uri(u_char *dst, u_char *src, + size_t size, ngx_uint_t type); + +void ngx_stream_lua_inject_req_api(ngx_log_t *log, lua_State *L); + +void ngx_stream_lua_process_args_option(ngx_stream_lua_request_t *r, + lua_State *L, int table, ngx_str_t *args); + +ngx_int_t ngx_stream_lua_open_and_stat_file(u_char *name, + ngx_open_file_info_t *of, ngx_log_t *log); + +ngx_chain_t *ngx_stream_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p, + ngx_chain_t **free, size_t len); + +#ifndef OPENRESTY_LUAJIT +void ngx_stream_lua_create_new_globals_table(lua_State *L, int narr, int nrec); +#endif + +int ngx_stream_lua_traceback(lua_State *L); + +ngx_stream_lua_co_ctx_t *ngx_stream_lua_get_co_ctx(lua_State *L, + ngx_stream_lua_ctx_t *ctx); + +ngx_stream_lua_co_ctx_t *ngx_stream_lua_create_co_ctx( + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx); + +ngx_int_t ngx_stream_lua_run_posted_threads(ngx_connection_t *c, lua_State *L, + ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, ngx_uint_t nreqs); + +ngx_int_t ngx_stream_lua_post_thread(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx); + +void ngx_stream_lua_del_thread(ngx_stream_lua_request_t *r, lua_State *L, + ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx); + +void ngx_stream_lua_rd_check_broken_connection(ngx_stream_lua_request_t *r); + +ngx_int_t ngx_stream_lua_test_expect(ngx_stream_lua_request_t *r); + +ngx_int_t ngx_stream_lua_check_broken_connection(ngx_stream_lua_request_t *r, + ngx_event_t *ev); + +void ngx_stream_lua_finalize_request(ngx_stream_lua_request_t *r, ngx_int_t rc); + +void ngx_stream_lua_finalize_fake_request(ngx_stream_lua_request_t *r, + ngx_int_t rc); + +void ngx_stream_lua_close_fake_connection(ngx_connection_t *c); + +void ngx_stream_lua_free_fake_request(ngx_stream_lua_request_t *r); + +void ngx_stream_lua_release_ngx_ctx_table(ngx_log_t *log, lua_State *L, + ngx_stream_lua_ctx_t *ctx); + +void ngx_stream_lua_cleanup_vm(void *data); + +ngx_connection_t *ngx_stream_lua_create_fake_connection(ngx_pool_t *pool); + +ngx_stream_lua_request_t * + ngx_stream_lua_create_fake_request(ngx_stream_session_t *s); + +ngx_stream_session_t *ngx_stream_lua_create_fake_session(ngx_connection_t *c); + +ngx_int_t ngx_stream_lua_report(ngx_log_t *log, lua_State *L, int status, + const char *prefix); + +int ngx_stream_lua_do_call(ngx_log_t *log, lua_State *L); + + + +void ngx_stream_lua_cleanup_free(ngx_stream_lua_request_t *r, + ngx_stream_lua_cleanup_pt *cleanup); + +#if (NGX_STREAM_LUA_HAVE_SA_RESTART) +void ngx_stream_lua_set_sa_restart(ngx_log_t *log); +#endif + +#define ngx_stream_lua_check_if_abortable(L, ctx) \ + if ((ctx)->no_abort) { \ + return luaL_error(L, "attempt to abort with pending subrequests"); \ + } + + +static ngx_inline void +ngx_stream_lua_init_ctx(ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx) +{ + ngx_memzero(ctx, sizeof(ngx_stream_lua_ctx_t)); + ctx->ctx_ref = LUA_NOREF; + ctx->entry_co_ctx.co_ref = LUA_NOREF; + ctx->resume_handler = ngx_stream_lua_wev_handler; + ctx->request = r; +} + + +static ngx_inline ngx_stream_lua_ctx_t * +ngx_stream_lua_create_ctx(ngx_stream_session_t *r) +{ + ngx_int_t rc; + lua_State *L = NULL; + ngx_stream_lua_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_main_conf_t *lmcf; + + ngx_stream_lua_request_t *sreq; + + ctx = ngx_palloc(r->connection->pool, sizeof(ngx_stream_lua_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + sreq = ngx_stream_lua_create_request(r); + + if (sreq == NULL) { + return NULL; + } + + ngx_stream_lua_init_ctx(sreq, ctx); + + ngx_stream_set_ctx(r, ctx, ngx_stream_lua_module); + + llcf = ngx_stream_get_module_srv_conf(r, ngx_stream_lua_module); + + if (!llcf->enable_code_cache && r->connection->fd != (ngx_socket_t) -1) { + lmcf = ngx_stream_get_module_main_conf(r, ngx_stream_lua_module); + +#ifdef DDEBUG + dd("lmcf: %p", lmcf); +#endif + + /* + * caveats: we need to move the vm cleanup hook to the list end + * to ensure it will be executed *after* the request cleanup + * hook registered by ngx_stream_lua_create_request to preserve + * the correct semantics. + */ + + rc = ngx_stream_lua_init_vm(&L, lmcf->lua, lmcf->cycle, sreq->pool, + lmcf, r->connection->log, &cln); + + while (cln->next != NULL) { + cln = cln->next; + } + + cln->next = sreq->pool->cleanup; + + cln = sreq->pool->cleanup; + sreq->pool->cleanup = cln->next; + cln->next = NULL; + + if (rc != NGX_OK) { + if (rc == NGX_DECLINED) { + ngx_stream_lua_assert(L != NULL); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to load the 'resty.core' module " + "(https://github.com/openresty/lua-resty" + "-core); ensure you are using an OpenResty " + "release from https://openresty.org/en/" + "download.html (reason: %s)", + lua_tostring(L, -1)); + + } else { + /* rc == NGX_ERROR */ + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to initialize Lua VM"); + } + + return NULL; + } + + /* rc == NGX_OK */ + + ngx_stream_lua_assert(L != NULL); + + if (lmcf->init_handler) { + if (lmcf->init_handler(r->connection->log, lmcf, L) != NGX_OK) { + /* an error happened */ + return NULL; + } + } + + ctx->vm_state = cln->data; + + } else { + ctx->vm_state = NULL; + } + + return ctx; +} + + +static ngx_inline lua_State * +ngx_stream_lua_get_lua_vm(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + ngx_stream_lua_main_conf_t *lmcf; + + if (ctx == NULL) { + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + } + + if (ctx && ctx->vm_state) { + return ctx->vm_state->vm; + } + + lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); + +#ifdef DDEBUG + dd("lmcf->lua: %p", lmcf->lua); +#endif + + return lmcf->lua; +} + + +#define ngx_stream_lua_req_key "__ngx_req" + + +static ngx_inline ngx_stream_lua_request_t * +ngx_stream_lua_get_req(lua_State *L) +{ +#ifdef OPENRESTY_LUAJIT + return lua_getexdata(L); +#else + ngx_stream_lua_request_t *r; + + lua_getglobal(L, ngx_stream_lua_req_key); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + return r; +#endif +} + + +static ngx_inline void +ngx_stream_lua_set_req(lua_State *L, ngx_stream_lua_request_t *r) +{ +#ifdef OPENRESTY_LUAJIT + lua_setexdata(L, (void *) r); +#else + lua_pushlightuserdata(L, r); + lua_setglobal(L, ngx_stream_lua_req_key); +#endif +} + + +static ngx_inline void +ngx_stream_lua_get_globals_table(lua_State *L) +{ + lua_pushvalue(L, LUA_GLOBALSINDEX); +} + + +static ngx_inline void +ngx_stream_lua_set_globals_table(lua_State *L) +{ + lua_replace(L, LUA_GLOBALSINDEX); +} + + +#define ngx_stream_lua_hash_literal(s) \ + ngx_stream_lua_hash_str((u_char *) s, sizeof(s) - 1) + + +static ngx_inline ngx_uint_t +ngx_stream_lua_hash_str(u_char *src, size_t n) +{ + ngx_uint_t key; + + key = 0; + + while (n--) { + key = ngx_hash(key, *src); + src++; + } + + return key; +} + + + + +static ngx_inline void +ngx_stream_lua_cleanup_pending_operation(ngx_stream_lua_co_ctx_t *coctx) +{ + if (coctx->cleanup) { + coctx->cleanup(coctx); + coctx->cleanup = NULL; + } +} + + +static ngx_inline ngx_chain_t * +ngx_stream_lua_get_flush_chain(ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx) +{ + ngx_chain_t *cl; + + cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_bufs, 0); + if (cl == NULL) { + return NULL; + } + + cl->buf->flush = 1; + + return cl; +} + + +#if defined(nginx_version) && nginx_version < 1011002 +static ngx_inline in_port_t +ngx_inet_get_port(struct sockaddr *sa) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + return ntohs(sin6->sin6_port); +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + return 0; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + return ntohs(sin->sin_port); + } +} +#endif + + +extern ngx_uint_t ngx_stream_lua_location_hash; +extern ngx_uint_t ngx_stream_lua_content_length_hash; + + +#endif /* _NGX_STREAM_LUA_UTIL_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_variable.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_variable.c new file mode 100644 index 000000000..2d3ef8699 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_variable.c @@ -0,0 +1,214 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_variable.c.tt2 + */ + + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_util.h" + + + + +int +ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r, u_char *name_data, + size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value, + size_t *value_len, char **err) +{ + ngx_uint_t hash; + ngx_str_t name; + + ngx_stream_variable_value_t *vv; + + if (r == NULL) { + *err = "no request object found"; + return NGX_ERROR; + } + + if ((r)->connection->fd == (ngx_socket_t) -1) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + + hash = ngx_hash_strlow(lowcase_buf, name_data, name_len); + + name.data = lowcase_buf; + name.len = name_len; + + dd("variable name: %.*s", (int) name_len, lowcase_buf); + + vv = ngx_stream_get_variable(r->session, &name, hash); + + if (vv == NULL || vv->not_found) { + return NGX_DECLINED; + } + + *value = vv->data; + *value_len = vv->len; + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r, u_char *name_data, + size_t name_len, u_char *lowcase_buf, u_char *value, size_t value_len, + u_char *errbuf, size_t *errlen) +{ + u_char *p; + ngx_uint_t hash; + + ngx_stream_variable_t *v; + ngx_stream_variable_value_t *vv; + ngx_stream_core_main_conf_t *cmcf; + + if (r == NULL) { + *errlen = ngx_snprintf(errbuf, *errlen, "no request object found") + - errbuf; + return NGX_ERROR; + } + + if ((r)->connection->fd == (ngx_socket_t) -1) { + *errlen = ngx_snprintf(errbuf, *errlen, + "API disabled in the current context") + - errbuf; + return NGX_ERROR; + } + + hash = ngx_hash_strlow(lowcase_buf, name_data, name_len); + + dd("variable name: %.*s", (int) name_len, lowcase_buf); + + /* we fetch the variable itself */ + + cmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_core_module); + + v = ngx_hash_find(&cmcf->variables_hash, hash, lowcase_buf, name_len); + + if (v) { + if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) { + dd("variable not changeable"); + *errlen = ngx_snprintf(errbuf, *errlen, + "variable \"%*s\" not changeable", + name_len, lowcase_buf) + - errbuf; + return NGX_ERROR; + } + + if (v->set_handler) { + + dd("set variables with set_handler"); + + if (value != NULL && value_len) { + vv = ngx_palloc(r->connection->pool, + sizeof(ngx_stream_variable_value_t) + + value_len); + if (vv == NULL) { + goto nomem; + } + + p = (u_char *) vv + sizeof(ngx_stream_variable_value_t); + ngx_memcpy(p, value, value_len); + value = p; + + } else { + vv = ngx_palloc(r->connection->pool, + sizeof(ngx_stream_variable_value_t)); + if (vv == NULL) { + goto nomem; + } + } + + if (value == NULL) { + vv->valid = 0; + vv->not_found = 1; + vv->no_cacheable = 0; + vv->data = NULL; + vv->len = 0; + + } else { + vv->valid = 1; + vv->not_found = 0; + vv->no_cacheable = 0; + + vv->data = value; + vv->len = value_len; + } + + v->set_handler(r->session, vv, v->data); + return NGX_OK; + } + + if (v->flags & NGX_STREAM_VAR_INDEXED) { + vv = &r->session->variables[v->index]; + + dd("set indexed variable"); + + if (value == NULL) { + vv->valid = 0; + vv->not_found = 1; + vv->no_cacheable = 0; + + vv->data = NULL; + vv->len = 0; + + } else { + p = ngx_palloc(r->connection->pool, value_len); + if (p == NULL) { + goto nomem; + } + + ngx_memcpy(p, value, value_len); + value = p; + + vv->valid = 1; + vv->not_found = 0; + vv->no_cacheable = 0; + + vv->data = value; + vv->len = value_len; + } + + return NGX_OK; + } + + *errlen = ngx_snprintf(errbuf, *errlen, + "variable \"%*s\" cannot be assigned " + "a value", name_len, lowcase_buf) + - errbuf; + return NGX_ERROR; + } + + /* variable not found */ + + *errlen = ngx_snprintf(errbuf, *errlen, + "variable \"%*s\" not found for writing; " + "maybe it is a built-in variable that is not " + "changeable or you forgot to use \"set $%*s '';\" " + "in the config file to define it first", + name_len, lowcase_buf, name_len, lowcase_buf) + - errbuf; + return NGX_ERROR; + +nomem: + + *errlen = ngx_snprintf(errbuf, *errlen, "no memory") - errbuf; + return NGX_ERROR; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_worker.c b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_worker.c new file mode 100644 index 000000000..4fb23314b --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/src/ngx_stream_lua_worker.c @@ -0,0 +1,173 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_worker.c.tt2 + */ + + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#define NGX_PROCESS_PRIVILEGED_AGENT 99 + + +int +ngx_stream_lua_ffi_worker_pid(void) +{ + return (int) ngx_pid; +} + + +int +ngx_stream_lua_ffi_worker_pids(int *pids, size_t *pids_len) +{ + ngx_int_t i, n; + + n = 0; + for (i = 0; i < NGX_MAX_PROCESSES; i++) { + if (i != ngx_process_slot && ngx_processes[i].pid == 0) { + break; + } + + if (i == ngx_process_slot && ngx_processes[i].pid == 0) { + pids[n++] = ngx_pid; + } + + if (ngx_processes[i].pid > 0) { + pids[n++] = ngx_processes[i].pid; + } + } + + if (n == 0) { + return NGX_ERROR; + } + + *pids_len = n; + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_worker_id(void) +{ +#if defined(nginx_version) && nginx_version >= 1009001 + if (ngx_process != NGX_PROCESS_WORKER + && ngx_process != NGX_PROCESS_SINGLE) + { + return -1; + } + + return (int) ngx_worker; +#else + return -1; +#endif +} + + +int +ngx_stream_lua_ffi_worker_exiting(void) +{ + return (int) ngx_exiting; +} + + +int +ngx_stream_lua_ffi_worker_count(void) +{ + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + return (int) ccf->worker_processes; +} + + +int +ngx_stream_lua_ffi_master_pid(void) +{ +#if defined(nginx_version) && nginx_version >= 1013008 + if (ngx_process == NGX_PROCESS_SINGLE) { + return (int) ngx_pid; + } + + return (int) ngx_parent; +#else + return NGX_ERROR; +#endif +} + + +int +ngx_stream_lua_ffi_get_process_type(void) +{ + ngx_core_conf_t *ccf; + +#if defined(HAVE_PRIVILEGED_PROCESS_PATCH) && !NGX_WIN32 + if (ngx_process == NGX_PROCESS_HELPER) { + if (ngx_is_privileged_agent) { + return NGX_PROCESS_PRIVILEGED_AGENT; + } + } +#endif + + if (ngx_process == NGX_PROCESS_SINGLE) { + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + if (ccf->master) { + return NGX_PROCESS_MASTER; + } + } + + return ngx_process; +} + + +#if defined(nginx_version) && nginx_version >= 1019003 +int +ngx_stream_lua_ffi_enable_privileged_agent(char **err, unsigned int connections) +#else +int +ngx_stream_lua_ffi_enable_privileged_agent(char **err) +#endif +{ +#ifdef HAVE_PRIVILEGED_PROCESS_PATCH + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + ccf->privileged_agent = 1; +#if defined(nginx_version) && nginx_version >= 1019003 + ccf->privileged_agent_connections = connections; +#endif + + return NGX_OK; + +#else + *err = "missing privileged agent process patch in the nginx core"; + return NGX_ERROR; +#endif +} + + +void +ngx_stream_lua_ffi_process_signal_graceful_exit(void) +{ + ngx_quit = 1; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/deps/src/stream-lua-nginx-module/t/002-content.t b/src/deps/src/stream-lua-nginx-module/t/002-content.t new file mode 100644 index 000000000..cef8bc5fb --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/002-content.t @@ -0,0 +1,248 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: basic print +--- stream_server_config + content_by_lua_block { + local ok, err = ngx.print("Hello, Lua!\n") + if not ok then + ngx.log(ngx.ERR, "print failed: ", err) + end + } +--- stream_response +Hello, Lua! +--- no_error_log +[error] + + + +=== TEST 2: basic say +--- stream_server_config + content_by_lua_block { + local ok, err = ngx.say("Hello, Lua!") + if not ok then + ngx.log(ngx.ERR, "say failed: ", err) + return + end + local ok, err = ngx.say("Yay! ", 123) + if not ok then + ngx.log(ngx.ERR, "say failed: ", err) + return + end + } +--- stream_response +Hello, Lua! +Yay! 123 +--- no_error_log +[error] + + + +=== TEST 3: no ngx.echo +--- stream_server_config + content_by_lua_block { ngx.echo("Hello, Lua!\n") } +--- stream_response +--- error_log eval +qr/content_by_lua\(nginx\.conf:\d+\):1: attempt to call field 'echo' \(a nil value\)/ + + + +=== TEST 4: calc expression +--- stream_server_config + content_by_lua_file html/calc.lua; +--- user_files +>>> calc.lua +local function uri_unescape(uri) + local function convert(hex) + return string.char(tonumber("0x"..hex)) + end + local s = string.gsub(uri, "%%([0-9a-fA-F][0-9a-fA-F])", convert) + return s +end + +local function eval_exp(str) + return loadstring("return "..str)() +end + +local exp_str = 1+2*math.sin(3)/math.exp(4)-math.sqrt(2) +-- print("exp: '", exp_str, "'\n") +local status, res +status, res = pcall(uri_unescape, exp_str) +if not status then + ngx.print("error: ", res, "\n") + return +end +status, res = pcall(eval_exp, res) +if status then + ngx.print("result: ", res, "\n") +else + ngx.print("error: ", res, "\n") +end + +--- stream_response +result: -0.4090441561579 +--- no_error_log +[error] + + + +=== TEST 5: nil is "nil" +--- stream_server_config + content_by_lua_block { ngx.say(nil) } +--- stream_response +nil +--- no_error_log +[error] + + + +=== TEST 6: write boolean +--- stream_server_config + content_by_lua_block { ngx.say(true, " ", false) } +--- stream_response +true false +--- no_error_log +[error] + + + +=== TEST 7: nginx quote sql string 1 +--- stream_server_config + content_by_lua_block { ngx.say(ngx.quote_sql_str('hello\n\r\'"\\')) } +--- stream_response +'hello\n\r\'\"\\' +--- no_error_log +[error] + + + +=== TEST 8: nginx quote sql string 2 +--- stream_server_config + content_by_lua_block { ngx.say(ngx.quote_sql_str("hello\n\r'\"\\")) } +--- stream_response +'hello\n\r\'\"\\' +--- no_error_log +[error] + + + +=== TEST 9: multiple eof +--- stream_server_config + content_by_lua_block { + ngx.say("Hi") + + local ok, err = ngx.eof() + if not ok then + ngx.log(ngx.WARN, "eof failed: ", err) + return + end + + ok, err = ngx.eof() + if not ok then + ngx.log(ngx.WARN, "eof failed: ", err) + return + end + } +--- stream_response +Hi +--- no_error_log +[error] +--- error_log +lua send eof +eof failed: seen eof + + + +=== TEST 10: ngx.eof before ngx.say +--- stream_server_config + content_by_lua_block { + local ok, err = ngx.eof() + if not ok then + ngx.log(ngx.ERR, "eof failed: ", err) + return + end + + ok, err = ngx.say(ngx.headers_sent) + if not ok then + ngx.log(ngx.WARN, "failed to say: ", err) + return + end + } +--- stream_response +--- no_error_log +[error] +--- error_log +failed to say: seen eof + + + +=== TEST 11: ngx.print table arguments (github issue #54) +--- stream_server_config + content_by_lua_block { ngx.print({10, {0, 5}, 15}, 32) } +--- stream_response chop +10051532 +--- no_error_log +[error] + + + +=== TEST 12: ngx.say table arguments (github issue #54) +--- stream_server_config + content_by_lua_block { ngx.say({10, {0, "5"}, 15}, 32) } +--- stream_response +10051532 +--- no_error_log +[error] + + + +=== TEST 13: Lua file does not exist +--- stream_server_config + content_by_lua_file html/test2.lua; +--- user_files +>>> test.lua +v = ngx.var["request_uri"] +ngx.print("request_uri: ", v, "\n") +--- stream_response +--- error_log eval +qr/failed to load external Lua file ".*?test2\.lua": cannot open .*? No such file or directory/ + + + +=== TEST 14: .lua file with shebang +--- stream_server_config + content_by_lua_file html/test.lua; +--- user_files +>>> test.lua +#!/bin/lua + +ngx.say("line ", debug.getinfo(1).currentline) +--- stream_response +line 3 +--- no_error_log +[error] + + + +=== TEST 15: syntax error in inlined Lua code +--- stream_server_config + content_by_lua_block {for end} +--- stream_response +--- error_log eval +qr/failed to load inlined Lua code: / diff --git a/src/deps/src/stream-lua-nginx-module/t/003-errors.t b/src/deps/src/stream-lua-nginx-module/t/003-errors.t new file mode 100644 index 000000000..381d8175a --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/003-errors.t @@ -0,0 +1,38 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => blocks() * (repeat_each() * 3); + +#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua'; + +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: syntax error in lua code chunk +--- stream_server_config + content_by_lua_block {local a + a = a+; + return a} +--- stream_response +--- error_log eval +qr/failed to load inlined Lua code: content_by_lua\(nginx\.conf:\d+\):2: unexpected symbol near ';'/ + + + +=== TEST 2: syntax error in lua file +--- stream_server_config + content_by_lua_file 'html/test.lua'; +--- user_files +>>> test.lua +local a +a = 3 +; +return a +--- stream_response +--- error_log eval +qr{failed to load external Lua file ".*?html/test\.lua": .*?test\.lua:2: unexpected symbol near ';'} diff --git a/src/deps/src/stream-lua-nginx-module/t/004-require.t b/src/deps/src/stream-lua-nginx-module/t/004-require.t new file mode 100644 index 000000000..0ca4948c4 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/004-require.t @@ -0,0 +1,157 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#log_level('warn'); + +#master_on(); +#repeat_each(120); +repeat_each(2); + +plan tests => blocks() * (repeat_each() * 3); + +our $HtmlDir = html_dir; +#warn $html_dir; + +#$ENV{LUA_PATH} = "$html_dir/?.lua"; + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + # load + content_by_lua_block { + package.loaded.foo = nil; + collectgarbage() + local foo = require "foo"; + foo.hi() + } + +--- stream_server_config2 + # check + content_by_lua_block { + local foo = package.loaded.foo + if foo then + ngx.say("found") + foo.hi() + else + ngx.say("not found") + end + } + +--- stream_server_config3 + # check + content_by_lua_block { + local foo = package.loaded.foo + if foo then + ngx.say("found") + foo.hi() + else + ngx.say("not found") + end + } +--- user_files +>>> foo.lua +local _M = {} + +ngx.say("loading"); + +function _M.hi () + ngx.say("hello, foo") +end + +return _M +--- stream_response +loading +hello, foo +found +hello, foo +found +hello, foo +--- no_error_log +[error] + + + +=== TEST 2: sanity +--- stream_config eval + "lua_package_cpath '$::HtmlDir/?.so';" +--- stream_server_config + content_by_lua_block { + ngx.print(package.cpath); + } +--- stream_response_like: ^[^;]+/servroot(_\d+)?/html/\?\.so$ +--- no_error_log +[error] + + + +=== TEST 3: expand default path (after) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;;';" +--- stream_server_config + content_by_lua_block { + ngx.print(package.path) + } +--- stream_response_like: ^[^;]+/servroot(_\d+)?/html/\?\.lua;(.+\.lua)?;*$ +--- no_error_log +[error] + + + +=== TEST 4: expand default cpath (after) +--- stream_config eval + "lua_package_cpath '$::HtmlDir/?.so;;';" +--- stream_server_config + content_by_lua_block { + ngx.print(package.cpath) + } +--- stream_response_like: ^[^;]+/servroot(_\d+)?/html/\?\.so;(.+\.so)?;*$ +--- no_error_log +[error] + + + +=== TEST 5: expand default path (before) +--- stream_config eval + "lua_package_path ';;$::HtmlDir/?.lua';" +--- stream_server_config + content_by_lua_block { + ngx.print(package.path); + } +--- stream_response_like: ^(.+\.lua)?;*?[^;]+/servroot(_\d+)?/html/\?\.lua$ +--- no_error_log +[error] + + + +=== TEST 6: expand default cpath (before) +--- stream_config eval + "lua_package_cpath ';;$::HtmlDir/?.so';" +--- stream_server_config + content_by_lua_block { + ngx.print(package.cpath); + } +--- stream_response_like: ^(.+\.so)?;*?[^;]+/servroot(_\d+)?/html/\?\.so$ +--- no_error_log +[error] + + + +=== TEST 7: require "ngx" (content_by_lua_block) +--- stream_server_config + content_by_lua_block { + local ngx = require "ngx" + ngx.say("hello, world") + } +--- stream_response +hello, world +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/005-exit.t b/src/deps/src/stream-lua-nginx-module/t/005-exit.t new file mode 100644 index 000000000..b27bed9b0 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/005-exit.t @@ -0,0 +1,114 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#repeat_each(20000); +repeat_each(2); +#master_on(); +#workers(1); +#log_level('debug'); +#log_level('warn'); +#worker_connections(1024); + +plan tests => repeat_each() * (blocks() * 3); + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306; + +our $LuaCpath = $ENV{LUA_CPATH} || + '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;'; + +#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua'; + +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: throw error +--- stream_server_config + content_by_lua_block { ngx.exit(ngx.ERROR);ngx.say('hi') } +--- stream_response +--- no_error_log +[error] + + + +=== TEST 2: throw error after sending the header and partial body +--- stream_server_config + content_by_lua_block { ngx.say('hi');ngx.exit(ngx.ERROR);ngx.say(', you') } +--- no_error_log +[error] +--- stream_response +hi + + + +=== TEST 3: throw 0 +--- stream_server_config + content_by_lua_block { ngx.say('Hi'); ngx.eof(); ngx.exit(0);ngx.say('world') } +--- stream_response +Hi +--- no_error_log +[error] + + + +=== TEST 4: pcall safe +--- stream_server_config + content_by_lua_block { + function f () + ngx.say("hello") + ngx.exit(200) + end + + pcall(f) + ngx.say("world") + } +--- stream_response +hello +--- no_error_log +[error] + + + +=== TEST 5: throw 444 after sending out responses +--- stream_server_config + content_by_lua_block { + ngx.say('ok'); + return ngx.exit(444) + } +--- stream_response +ok +--- log_level: debug +--- no_error_log +[error] + + + +=== TEST 6: throw 499 after sending out responses +--- stream_server_config + content_by_lua_block { + ngx.say('ok'); + return ngx.exit(499) + } +--- stream_response +ok +--- log_level: debug +--- no_error_log +[error] + + + +=== TEST 7: throw 408 after sending out responses +--- stream_server_config + content_by_lua_block { + ngx.say('ok'); + return ngx.exit(408) + } +--- stream_response +ok +--- log_level: debug +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/006-escape.t b/src/deps/src/stream-lua-nginx-module/t/006-escape.t new file mode 100644 index 000000000..5eb4b49ea --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/006-escape.t @@ -0,0 +1,173 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 3); + +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: escape uri in content_by_lua +--- stream_server_config + content_by_lua_block {ngx.say(ngx.escape_uri('a 你'))} +--- stream_response +a%20%E4%BD%A0 +--- no_error_log +[error] + + + +=== TEST 2: unescape uri in content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.unescape_uri('a%20%e4%bd%a0')) } +--- stream_response +a 你 +--- no_error_log +[error] + + + +=== TEST 3: escape uri in content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.escape_uri('a+b')) } +--- stream_response +a%2Bb +--- no_error_log +[error] + + + +=== TEST 4: escape uri in content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.escape_uri('"a/b={}:<>;&[]\\^')) } +--- stream_response +%22a%2Fb%3D%7B%7D%3A%3C%3E%3B%26%5B%5D%5C%5E +--- no_error_log +[error] + + + +=== TEST 5: escape a string that cannot be escaped +--- stream_server_config + content_by_lua_block { ngx.say(ngx.escape_uri('abc')) } +--- stream_response +abc +--- no_error_log +[error] + + + +=== TEST 6: escape an empty string that cannot be escaped +--- stream_server_config + content_by_lua_block { ngx.say(ngx.escape_uri('')) } +--- stream_response eval: "\n" +--- no_error_log +[error] + + + +=== TEST 7: escape nil +--- stream_server_config + content_by_lua_block { ngx.say("[", ngx.escape_uri(nil), "]") } +--- stream_response +[] +--- no_error_log +[error] + + + +=== TEST 8: escape numbers +--- stream_server_config + content_by_lua_block { ngx.say(ngx.escape_uri(32)) } +--- stream_response +32 +--- no_error_log +[error] + + + +=== TEST 9: unescape nil +--- stream_server_config + content_by_lua_block { ngx.say("[", ngx.unescape_uri(nil), "]") } +--- stream_response +[] +--- no_error_log +[error] + + + +=== TEST 10: unescape numbers +--- stream_server_config + content_by_lua_block { ngx.say(ngx.unescape_uri(32)) } +--- stream_response +32 +--- no_error_log +[error] + + + +=== TEST 11: escape type +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 0)) + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 1)) + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 2)) + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 3)) + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 4)) + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 5)) + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 6)) + } +--- stream_response +https://www.google.com/%3Ft=abc@%20: +https://www.google.com/%3Ft=abc@%20: +https%3A%2F%2Fwww.google.com%2F%3Ft%3Dabc%40%20%3A +https://www.google.com/?t=abc@%20: +https://www.google.com/?t=abc@%20: +https://www.google.com/?t=abc@%20: +https://www.google.com/?t=abc@%20: +--- no_error_log +[error] + + + +=== TEST 12: escape type error +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", true)) + } +--- stream_response +--- error_log eval +qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" is not a number/ +--- no_error_log +[alert] + + + +=== TEST 13: escape type out of range +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", -1)) + } +--- stream_response +--- error_log eval +qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" -1 out of range/ +--- no_error_log +[alert] + + + +=== TEST 14: escape type error +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 100)) + } +--- stream_response +--- error_log eval +qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" 100 out of range/ +--- no_error_log +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/007-md5.t b/src/deps/src/stream-lua-nginx-module/t/007-md5.t new file mode 100644 index 000000000..cdaab77f7 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/007-md5.t @@ -0,0 +1,55 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#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: set md5 hello +--- stream_server_config + content_by_lua_block { ngx.say(ngx.md5("hello")) } +--- stream_response +5d41402abc4b2a76b9719d911017c592 +--- no_error_log +[error] + + + +=== TEST 2: nil string to ngx.md5 +--- stream_server_config + content_by_lua_block { ngx.say(ngx.md5(nil)) } +--- stream_response +d41d8cd98f00b204e9800998ecf8427e +--- no_error_log +[error] + + + +=== TEST 3: empty string to ngx.md5 +--- stream_server_config + content_by_lua_block { ngx.say(ngx.md5("")) } +--- stream_response +d41d8cd98f00b204e9800998ecf8427e +--- no_error_log +[error] + + + +=== TEST 4: md5(number) +--- stream_server_config + content_by_lua_block { ngx.say(ngx.md5(45)) } +--- stream_response +6c8349cc7260ae62e3b1396831a8398f +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/008-today.t b/src/deps/src/stream-lua-nginx-module/t/008-today.t new file mode 100644 index 000000000..32073e39c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/008-today.t @@ -0,0 +1,24 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#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: use ngx.today in content_by_lua* +--- stream_server_config + content_by_lua_block { ngx.say(ngx.today()) } +--- stream_response_like: ^\d{4}-\d{2}-\d{2}$ +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/009-log.t b/src/deps/src/stream-lua-nginx-module/t/009-log.t new file mode 100644 index 000000000..12f7d87c5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/009-log.t @@ -0,0 +1,318 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_process_enabled(1); +log_level('debug'); # to ensure any log-level can be outputed + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 2); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: test log-level STDERR +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.STDERR, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 2: test log-level EMERG +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.EMERG, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[emerg\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 3: test log-level ALERT +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.ALERT, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[alert\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 4: test log-level CRIT +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.CRIT, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[crit\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 5: test log-level ERR +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.ERR, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[error\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 6: test log-level WARN +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.WARN, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[warn\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 7: test log-level NOTICE +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.NOTICE, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[notice\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 8: test log-level INFO +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.INFO, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[info\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 9: test log-level DEBUG +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.DEBUG, "hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[debug\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 10: regression test print() +--- stream_server_config + content_by_lua_block { + ngx.say("before log") + print("hello, log", 1234, 3.14159) + ngx.say("after log") + } +--- stream_response +before log +after log +--- error_log eval +qr/\[notice\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):3: hello, log12343.14159/ + + + +=== TEST 11: print(nil) +--- stream_server_config + content_by_lua_block { + print() + print(nil) + print("nil: ", nil) + ngx.say("hi"); + } +--- stream_response +hi +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):2: ,/, +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):3: nil,/, +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):4: nil: nil,/, +] + + + +=== TEST 12: test booleans and nil +--- stream_server_config + content_by_lua_block { + ngx.log(ngx.ERR, true, false, nil) + ngx.say(32) + } +--- stream_response +32 +--- error_log eval +qr/\[error\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):2: truefalsenil/ + + + +=== TEST 13: ngx.log() big data +--- stream_server_config + content_by_lua_block { + ngx.log(ngx.ERR, "a" .. string.rep("h", 1970) .. "b") + ngx.say("hi") + } +--- response_headers +--- error_log eval +[qr/ah{1970}b/] + + + +=== TEST 14: ngx.log in Lua function calls & inlined lua +--- stream_server_config + content_by_lua_block { + function foo() + bar() + end + + function bar() + ngx.log(ngx.ERR, "hello, log", 1234, 3.14159) + end + + foo() + ngx.say("done") + } +--- stream_response +done +--- error_log eval +qr/\[error\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):7: bar\(\): hello, log12343.14159/ + + + +=== TEST 15: ngx.log in Lua function tail-calls & inlined lua +--- stream_server_config + content_by_lua_block { + function foo() + return bar(5) + end + + function bar(n) + if n < 1 then + ngx.log(ngx.ERR, "hello, log", 1234, 3.14159) + return n + end + + return bar(n - 1) + end + + foo() + ngx.say("done") + } +--- stream_response +done +--- error_log eval +qr/\[error\] \S+: \S+ stream \[lua\] content_by_lua\(nginx\.conf:\d+\):8:(?: foo\(\):)? hello, log12343.14159/ + + + +=== TEST 16: ngx.log in Lua files +--- stream_server_config + content_by_lua_file 'html/test.lua'; +--- user_files +>>> test.lua +function foo() + bar() +end + +function bar() + ngx.log(ngx.ERR, "hello, log", 1234, 3.14159) +end + +foo() +ngx.say("done") + +--- stream_response +done +--- error_log eval +qr/\[error\] \S+: \S+ stream \[lua\] test.lua:6: bar\(\): hello, log12343.14159/ + + + +=== TEST 17: ngx.log with bad levels (ngx.ERROR, -1) +--- stream_server_config + content_by_lua_block { + ngx.log(ngx.ERROR, "hello lua") + ngx.say("done") + } +--- stream_response +--- error_log +bad log level: -1 + + + +=== TEST 18: ngx.log with bad levels (9) +--- stream_server_config + content_by_lua_block { + ngx.log(9, "hello lua") + ngx.say("done") + } +--- stream_response +--- error_log +bad log level: 9 + + + +=== TEST 19: \0 in the log message +--- stream_server_config + content_by_lua_block { + ngx.log(ngx.WARN, "hello\0world") + ngx.say("ok") + } +--- stream_response +ok +--- no_error_log +[error] +--- error_log eval +"2: hello\0world" diff --git a/src/deps/src/stream-lua-nginx-module/t/011-md5_bin.t b/src/deps/src/stream-lua-nginx-module/t/011-md5_bin.t new file mode 100644 index 000000000..564ef97b6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/011-md5_bin.t @@ -0,0 +1,107 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#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(); + + +#md5_bin_bin is hard to test, so convert it to hex mode + +__DATA__ + +=== TEST 1: set md5_bin hello ????xxoo +--- stream_server_config + content_by_lua_block { + local a = string.gsub(ngx.md5_bin("hello"), ".", function (c) + return string.format("%02x", string.byte(c)) + end) + ngx.say(a) + } +--- stream_response +5d41402abc4b2a76b9719d911017c592 +--- no_error_log +[error] + + + +=== TEST 2: set md5_bin hello ????xxoo +--- stream_server_config + content_by_lua_block { ngx.say(string.len(ngx.md5_bin("hello"))) } +--- stream_response +16 +--- no_error_log +[error] + + + +=== TEST 3: set md5_bin hello +--- stream_server_config + content_by_lua_block { + local s = ngx.md5_bin("hello") + s = string.gsub(s, ".", function (c) + return string.format("%02x", string.byte(c)) + end) + ngx.say(s) + } +--- stream_response +5d41402abc4b2a76b9719d911017c592 +--- no_error_log +[error] + + + +=== TEST 4: nil string to ngx.md5_bin +--- stream_server_config + content_by_lua_block { + local s = ngx.md5_bin(nil) + s = string.gsub(s, ".", function (c) + return string.format("%02x", string.byte(c)) + end) + ngx.say(s) + } +--- stream_response +d41d8cd98f00b204e9800998ecf8427e +--- no_error_log +[error] + + + +=== TEST 5: null string to ngx.md5_bin +--- stream_server_config + content_by_lua_block { + local s = ngx.md5_bin("") + s = string.gsub(s, ".", function (c) + return string.format("%02x", string.byte(c)) + end) + ngx.say(s) + } +--- stream_response +d41d8cd98f00b204e9800998ecf8427e +--- no_error_log +[error] + + + +=== TEST 6: md5_bin(number) +--- stream_server_config + content_by_lua_block { + s = ngx.md5_bin(45) + s = string.gsub(s, ".", function (c) + return string.format("%02x", string.byte(c)) + end) + ngx.say(s) + } +--- stream_response +6c8349cc7260ae62e3b1396831a8398f +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/012-now.t b/src/deps/src/stream-lua-nginx-module/t/012-now.t new file mode 100644 index 000000000..37613c91b --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/012-now.t @@ -0,0 +1,70 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_process_enabled(1); +log_level('warn'); + +repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: use ngx.localtime in content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.localtime()) } +--- stream_response_like: ^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$ +--- no_error_log +[error] + + + +=== TEST 2: use ngx.time in content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.time()) } +--- stream_response_like: ^\d{10,}$ +--- no_error_log +[error] + + + +=== TEST 3: use ngx.time in content_by_lua +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.time()) + ngx.say(ngx.localtime()) + ngx.say(ngx.utctime()) + } +--- stream_response_like chomp +^\d{10,} +\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} +\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} +--- no_error_log +[error] + + + +=== TEST 4: use ngx.now in content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.now()) } +--- stream_response_like: ^\d{10,}(\.\d{1,3})?$ +--- no_error_log +[error] + + + +=== TEST 5: use ngx.update_time & ngx.now in content_by_lua +--- stream_server_config + content_by_lua_block { + ngx.update_time() + ngx.say(ngx.now()) + } +--- stream_response_like: ^\d{10,}(\.\d{1,3})?$ +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/013-base64.t b/src/deps/src/stream-lua-nginx-module/t/013-base64.t new file mode 100644 index 000000000..01b42c9e6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/013-base64.t @@ -0,0 +1,142 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#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: base64 encode hello +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64("hello")) } +--- stream_response +aGVsbG8= +--- no_error_log +[error] + + + +=== TEST 2: nil string to ngx.encode_base64 +--- stream_server_config + content_by_lua_block { ngx.say("left" .. ngx.encode_base64(nil) .. "right") } +--- stream_response +leftright +--- no_error_log +[error] + + + +=== TEST 3: empty string to ngx.encode_base64 +--- stream_server_config + content_by_lua_block { ngx.say("left" .. ngx.encode_base64("") .. "right") } +--- stream_response +leftright +--- no_error_log +[error] + + + +=== TEST 4: base64 encode hello +--- stream_server_config + content_by_lua_block { ngx.say(ngx.decode_base64("aGVsbG8=")) } +--- stream_response +hello +--- no_error_log +[error] + + + +=== TEST 5: null string to ngx.decode_base64 +--- stream_server_config + content_by_lua_block { ngx.say("left" .. ngx.decode_base64("") .. "right") } +--- stream_response +leftright +--- no_error_log +[error] + + + +=== TEST 6: use ngx.decode_base64 in content_by_lua (nil) +--- stream_server_config + content_by_lua_block { ngx.say("left" .. ngx.decode_base64(nil) .. "right") } +--- stream_response +--- error_log +string argument only + + + +=== TEST 7: base64 encode number +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64(32)) } +--- stream_response +MzI= +--- no_error_log +[error] + + + +=== TEST 8: base64 decode number +--- stream_server_config + content_by_lua_block { ngx.say(ngx.decode_base64(32)) } +--- stream_response +--- error_log +string argument only + + + +=== TEST 9: base64 decode error +--- stream_server_config + content_by_lua_block { ngx.say(ngx.decode_base64("^*~")) } +--- stream_response +nil +--- no_error_log +[error] + + + +=== TEST 10: base64 encode without padding (explicit true to no_padding) +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64("hello", true)) } +--- stream_response +aGVsbG8 +--- no_error_log +[error] + + + +=== TEST 11: base64 encode short string +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64("w")) } +--- stream_response +dw== +--- no_error_log +[error] + + + +=== TEST 12: base64 encode short string with padding (explicit false to no_padding) +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64("w", false)) } +--- stream_response +dw== +--- no_error_log +[error] + + + +=== TEST 13: base64 encode with wrong 2nd parameter +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64("w", 0)) } +--- stream_response +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):\d+: bad no_padding: boolean expected, got number/ diff --git a/src/deps/src/stream-lua-nginx-module/t/014-bugs.t b/src/deps/src/stream-lua-nginx-module/t/014-bugs.t new file mode 100644 index 000000000..e82e37273 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/014-bugs.t @@ -0,0 +1,358 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +log_level('debug'); + +repeat_each(3); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; +#warn $html_dir; + +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; +$ENV{TEST_NGINX_REDIS_PORT} ||= 6379; + +#no_diff(); +#no_long_string(); + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +#no_shuffle(); +no_long_string(); + +sub read_file { + my $infile = shift; + open my $in, $infile + or die "cannot open $infile for reading: $!"; + my $cert = do { local $/; <$in> }; + close $in; + $cert; +} + +our $TestCertificate = read_file("t/cert/test.crt"); +our $TestCertificateKey = read_file("t/cert/test.key"); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + package.loaded.foo = nil; + local foo = require "foo"; + foo.hi() + } +--- user_files +>>> foo.lua +module(..., package.seeall); + +function foo () + return 1 + return 2 +end +--- stream_response +--- error_log +error loading module 'foo' from file + + + +=== TEST 2: print lua empty strings +--- stream_server_config + content_by_lua_block { ngx.print("") ngx.flush() ngx.print("Hi") } +--- stream_response chop +Hi +--- no_error_log +[error] + + + +=== TEST 3: say lua empty strings +--- stream_server_config + content_by_lua_block { ngx.say("") ngx.flush() ngx.print("Hi") } +--- stream_response eval +" +Hi" +--- no_error_log +[error] + + + +=== TEST 4: lua_code_cache off + setkeepalive +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local test = require "test" + local port = $TEST_NGINX_REDIS_PORT + test.go(port) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port) + local sock = ngx.socket.tcp() + local sock2 = ngx.socket.tcp() + + sock:settimeout(1000) + sock2:settimeout(6000000) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local ok, err = sock2:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local ok, err = sock:setkeepalive(100, 100) + if not ok then + ngx.say("failed to set reusable: ", err) + end + + local ok, err = sock2:setkeepalive(200, 100) + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ngx.say("done") +end +--- stap2 +F(ngx_close_connection) { + println("=== close connection") + print_ubacktrace() +} +--- stap_out2 +--- stream_response +done +--- wait: 0.5 +--- no_error_log +[error] + + + +=== TEST 5: .lua file of exactly N*1024 bytes (github issue #385) +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files eval +my $s = "ngx.say('ok')\n"; +">>> a.lua\n" . (" " x (8192 - length($s))) . $s; + +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 6: tcp: nginx crash when resolve an not exist domain in ngx.thread.spawn +https://github.com/openresty/lua-nginx-module/issues/1915 +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local function tcp(host, port) + local sock = ngx.socket.tcp() + local ok,err = sock:connect(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "notexistdomain.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(tcp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + +--- request +GET /t +--- response_body +res: false +--- error_log +notexistdomain.openresty.org could not be resolved + + + +=== TEST 7: domain exists with tcp socket +https://github.com/openresty/lua-nginx-module/issues/1915 +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local function tcp(host, port) + local sock = ngx.socket.tcp() + local ok,err = sock:connect(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "www.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(tcp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + +--- request +GET /t +--- response_body +res: true +--- no_error_log +[error] + + + +=== TEST 8: domain exists with udp socket +https://github.com/openresty/lua-nginx-module/issues/1915 +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local function udp(host, port) + local sock = ngx.socket.udp() + local ok,err = sock:setpeername(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "notexistdomain.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(udp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + +--- request +GET /t +--- response_body +res: false +--- error_log +notexistdomain.openresty.org could not be resolved + + + +=== TEST 9: udp: nginx crash when resolve an not exist domain in ngx.thread.spawn +https://github.com/openresty/lua-nginx-module/issues/1915 +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local function udp(host, port) + local sock = ngx.socket.udp() + local ok,err = sock:setpeername(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "www.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(udp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + +--- request +GET /t +--- response_body +res: true +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/019-const.t b/src/deps/src/stream-lua-nginx-module/t/019-const.t new file mode 100644 index 000000000..59ce3bbb9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/019-const.t @@ -0,0 +1,32 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => blocks() * repeat_each() * 3; + +#no_diff(); +#no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.OK) + ngx.say(ngx.AGAIN) + ngx.say(ngx.DONE) + ngx.say(ngx.ERROR) + ngx.say(ngx.DECLINED) + } +--- stream_response +0 +-2 +-4 +-1 +-5 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/client-abort.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/client-abort.t new file mode 100644 index 000000000..1fd56b13f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/client-abort.t @@ -0,0 +1,450 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +use t::StapThread; + +our $GCScript = <<_EOC_; +$t::StapThread::GCScript + +F(ngx_http_lua_check_broken_connection) { + println("lua check broken conn") +} + +F(ngx_http_lua_request_cleanup) { + println("lua req cleanup") +} +_EOC_ + +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 - 2); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; +$ENV{TEST_NGINX_REDIS_PORT} ||= '6379'; + +#no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sleep + stop +--- stream_server_config + lua_check_client_abort on; + preread_by_lua_block { + ngx.sleep(1) + } + + return here; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 0.1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 2: sleep + stop (log handler still gets called) +--- stream_server_config + lua_check_client_abort on; + preread_by_lua_block { + ngx.sleep(1) + } + + log_by_lua_block { + ngx.log(ngx.NOTICE, "here in log by lua") + } + + return here; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection +here in log by lua + + + +=== TEST 3: sleep + ignore +--- stream_server_config + lua_check_client_abort off; + preread_by_lua_block { + ngx.sleep(1) + } + + content_by_lua return; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +lua req cleanup + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] + + + +=== TEST 4: ngx.req.socket + receive() + sleep + stop +--- stream_server_config + lua_check_client_abort on; + + preread_by_lua_block { + local sock = ngx.req.socket() + sock:receive() + ngx.sleep(1) + } + + return here; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 5: ngx.req.socket + receive(N) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + + preread_by_lua_block { + local sock = ngx.req.socket() + sock:receive(5) + ngx.sleep(1) + } + + return here; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 0.1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 6: ngx.req.socket + receive(n) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + + preread_by_lua_block { + local sock = ngx.req.socket() + sock:receive(2) + ngx.sleep(1) + } + + content_by_lua return; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out_like +^(?:lua check broken conn +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +lua req cleanup|lua check broken conn +lua req cleanup +delete thread 1)$ + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] + + + +=== TEST 7: ngx.req.socket + m * receive(n) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + lua_socket_log_errors off; + preread_by_lua_block { + local sock = ngx.req.socket() + sock:receive(2) + sock:receive(2) + sock:receive(1) + ngx.sleep(1) + } + + content_by_lua return; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 8: ngx.req.socket + receiveuntil + sleep + stop +--- stream_server_config + lua_check_client_abort on; + preread_by_lua_block { + local sock = ngx.req.socket() + local it = sock:receiveuntil("\\n") + it() + ngx.sleep(1) + } + + content_by_lua return; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 9: ngx.req.socket + receiveuntil + it(n) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + preread_by_lua_block { + local sock = ngx.req.socket() + local it = sock:receiveuntil("\\n") + it(2) + it(3) + ngx.sleep(1) + } + + content_by_lua return; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 10: cosocket + stop +--- stream_server_config + lua_check_client_abort on; + + preread_by_lua_block { + local sock, err = ngx.socket.tcp() + if not sock then + ngx.log(ngx.ERR, "failed to get socket: ", err) + return + end + + ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + local bytes, err = sock:send("blpop nonexist 2\\r\\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send query: ", err) + return + end + + -- ngx.log(ngx.ERR, "about to receive") + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive query: ", err) + return + end + + ngx.log(ngx.ERR, "res: ", res) + } + + content_by_lua return; +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +client prematurely closed connection + + + +=== TEST 11: ngx.req.socket + receive n < content-length + stop +--- stream_server_config + lua_check_client_abort on; + + preread_by_lua_block { + local sock = ngx.req.socket() + local res, err = sock:receive("*a") + if not res then + ngx.log(ngx.NOTICE, "failed to receive: ", err) + return + end + error("bad") + } + + content_by_lua return; +--- stream_request eval +"POST /t HTTP/1.0\r +Host: localhost\r +Connection: close\r +Content-Length: 100\r +\r +hello" +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +--- error_log +failed to receive: client aborted + + + +=== TEST 12: ngx.req.socket + receive n == content-length + stop +--- stream_server_config + lua_check_client_abort on; + preread_by_lua_block { + local sock = ngx.req.socket() + local res, err = sock:receive("*a") + if not res then + ngx.log(ngx.NOTICE, "failed to receive: ", err) + return + end + ngx.sleep(1) + error("bad") + } + + content_by_lua return; +--- stream_request eval +"POST /t HTTP/1.0\r +Host: localhost\r +Connection: close\r +Content-Length: 5\r +\r +hello" +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- no_error_log +[error] + + + +=== TEST 13: ngx.req.socket + receive n == content-length + ignore +--- stream_server_config + preread_by_lua_block { + local sock = ngx.req.socket() + local res, err = sock:receive("*a") + if not res then + ngx.log(ngx.NOTICE, "failed to receive: ", err) + return + end + ngx.say("done") + } + + content_by_lua return; +--- stream_request eval +"POST /t HTTP/1.0\r +Host: localhost\r +Connection: close\r +Content-Length: 5\r +\r +hello" +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- no_error_log +[error] +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/exit.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/exit.t new file mode 100644 index 000000000..09cbded86 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/exit.t @@ -0,0 +1,45 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#repeat_each(20000); + +repeat_each(2); + +#master_on(); +#workers(1); +#log_level('debug'); +#log_level('warn'); +#worker_connections(1024); + +plan tests => repeat_each() * (blocks() * 2); + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306; + +our $LuaCpath = $ENV{LUA_CPATH} || + '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;'; + +#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua'; + +no_long_string(); +#no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: throw 500 +--- stream_server_config + preread_by_lua_block { ngx.exit(500);ngx.say('hi') } + content_by_lua_block { ngx.exit(ngx.OK) } +--- error_log +finalize stream request: 500 + + + +=== TEST 2: throw 0 +--- stream_server_config + preread_by_lua_block { ngx.say('Hi'); ngx.eof(); ngx.exit(0);ngx.say('world') } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +Hi diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/req-socket.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/req-socket.t new file mode 100644 index 000000000..329b87727 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/req-socket.t @@ -0,0 +1,570 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 11); + +our $HtmlDir = html_dir; + +#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + preread_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + + for i = 1, 2 do + local data, err, part = sock:receive(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + end + + local data, err, part = sock:receive(1) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + return + end + } + content_by_lua return; +--- stream_request chop +hello world +--- stream_response +got the request socket +received: hello +received: worl +received: d +--- no_error_log +[error] + + + +=== TEST 2: attempt to use the req socket across request boundary +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + preread_by_lua_block { + local test = require "test" + test.go() + ngx.say("done") + } + content_by_lua return; +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock, err + +function go() + if not sock then + sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + else + for i = 1, 3 do + local data, err, part = sock:receive(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + end + end +end +--- stream_response_like +(?:got the request socket +|failed to receive: closed [d] +)?done +--- no_error_log +[alert] + + + +=== TEST 3: receive until on request_body - receiveuntil(1) on the last byte of the body +See https://groups.google.com/group/openresty/browse_thread/thread/43cf01da3c681aba for details +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + preread_by_lua_block { + local test = require "test" + test.go() + ngx.say("done") + } + content_by_lua return; +--- user_files +>>> test.lua +module("test", package.seeall) + +function go() + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + local data, err, part = sock:receive(56) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + + local discard_line = sock:receiveuntil('\r\n') + + local data, err, part = discard_line(8192) + if data then + ngx.say("received len: ", #data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + + local data, err, part = discard_line(1) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end +end +--- stream_request chop +-----------------------------820127721219505131303151179################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################$ +--- stream_response +got the request socket +received: -----------------------------820127721219505131303151179 +received len: 8192 +received: $ +done +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 4: read from preread buffer +--- stream_server_config + ssl_preread on; + + preread_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + for i = 1, 2 do + local data, err, part = sock:receive(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + return + end + end + + local data, err, part = sock:receive(1) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + return + end + } + content_by_lua return; +--- stream_request chop +hello world +--- stream_response +got the request socket +received: hello +received: worl +received: d +--- no_error_log +[error] + + + +=== TEST 5: small preread buffer +--- stream_server_config + ssl_preread on; + preread_buffer_size 5; + + preread_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + for i = 1, 2 do + local data, err, part = sock:receive(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + return + end + end + + local data, err, part = sock:receive(1) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + return + end + } + content_by_lua return; +--- stream_request chop +hello world +--- stream_response +got the request socket +received: hello +received: worl +received: d +--- no_error_log +[error] + + + +=== TEST 6: peeking preread buffer +--- stream_server_config + preread_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + local data, err = sock:peek(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + + data, err = sock:peek(10) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + } + + proxy_pass 127.0.0.1:$TEST_NGINX_RAND_PORT_1; +--- stream_config +server { + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; + + content_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + local data, err = sock:receive(11) + if data then + ngx.log(ngx.DEBUG, "upstream received: ", data) + + else + ngx.say("failed to receive: ", err) + return + end + + ngx.say("done") + } +} +--- stream_request chop +hello world +--- stream_response +got the request socket +received: hello +received: hello worl +got the request socket +done +--- error_log +upstream received: hello world +--- no_error_log +[error] + + + +=== TEST 7: peeking preread buffer, buffer size is small +--- stream_server_config + preread_buffer_size 10; + + preread_by_lua_block { + local sock = assert(ngx.req.socket()) + + local data, err = sock:peek(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + + data, err = sock:peek(11) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + } + + return done; +--- stream_request chop +hello world +--- error_log +preread buffer full while prereading client data +finalize stream session: 400 +--- no_error_log +[warn] + + + +=== TEST 8: peeking preread buffer, timedout +--- stream_server_config + preread_timeout 100ms; + + preread_by_lua_block { + local sock = assert(ngx.req.socket()) + + local data, err = sock:peek(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + + ngx.flush(true) + + data, err = sock:peek(12) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + } + + return done; +--- stream_request chop +hello world +--- stream_response +received: hello +--- error_log +finalize stream session: 200 +--- no_error_log +[warn] +[error] + + + +=== TEST 9: peek in wrong phase +--- stream_server_config + content_by_lua_block { + local sock = assert(ngx.req.socket()) + + local data, err = sock:peek(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + } +--- stream_request chop +hello world +--- error_log +API disabled in the context of content_by_lua* +--- no_error_log +[warn] + + + +=== TEST 10: peek busy reading +--- stream_server_config + preread_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + ngx.thread.spawn(function() + local data, err = sock:peek(15) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + end) + + local data, err = sock:peek(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return ngx.exit(ngx.OK) + end + } + + return done; +--- stream_request +--- stream_response chop +got the request socket +failed to receive: socket busy reading +done +--- no_error_log +[warn] +[error] + + + +=== TEST 11: peek before and after receive +--- stream_server_config + preread_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + local data, err = sock:peek(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + + ngx.flush(true) + + data, err = sock:receive(11) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + + ngx.flush(true) + + data, err = sock:peek(1) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err) + return + end + } + + return done; +--- stream_request chop +hello world +--- stream_response +got the request socket +received: hello +received: hello world +--- error_log +attempt to peek on a consumed socket +--- no_error_log +[warn] + + + +=== TEST 12: peek works with other preread handlers +--- stream_server_config + ssl_preread on; + preread_by_lua_block { + local rsock = assert(ngx.req.socket()) + + local data, err = rsock:peek(2) + if not data then + ngx.log(ngx.ERR, "failed to peek the request socket: ", err) + return + end + + local n = ngx.var.ssl_preread_server_name + if not n or n == '' then + ngx.log(ngx.INFO, "$ssl_preread_server_name is empty") + + else + ngx.log(ngx.INFO, "$ssl_preread_server_name = ", n) + end + + + if n == "my.sni.server.name" then + assert(string.byte(data:sub(1, 1)) == 0x16) + assert(string.byte(data:sub(2, 2)) == 0x03) + ngx.exit(200) + end + + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", tonumber(ngx.var.server_port)) + if not ok then + ngx.say(err) + return ngx.exit(500) + end + + local _, err = sock:sslhandshake(nil, "my.sni.server.name") + if not err then + ngx.say("did not error as expected") + return ngx.exit(500) + end + + sock:close() + } + + return done; +--- stream_request chop +hello +--- stream_response chop +done +--- error_log +$ssl_preread_server_name is empty while prereading client data +$ssl_preread_server_name = my.sni.server.name while prereading client data +--- no_error_log +[crit] +[warn] +assertion failed! +lua entry thread aborted diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/sanity.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/sanity.t new file mode 100644 index 000000000..a3e3b6b8e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/sanity.t @@ -0,0 +1,214 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#no_nginx_manager(); +#log_level('warn'); +#master_on(); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: basic print +--- stream_server_config + preread_by_lua_block { ngx.print("Hello, Lua!\n") } + + content_by_lua return; + #content_by_lua 'ngx.say("Hi")'; +--- stream_response +Hello, Lua! + + + +=== TEST 2: basic say +--- stream_server_config + preread_by_lua_block { + ngx.say("Hello, Lua!") + ngx.say("Yay! ", 123); + } + + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +Hello, Lua! +Yay! 123 + + + +=== TEST 3: no ngx.echo +--- stream_server_config + preread_by_lua_block { ngx.echo("Hello, Lua!\\n") } + content_by_lua_block { ngx.exit(ngx.OK) } +--- error_log +attempt to call field 'echo' (a nil value) + + + +=== TEST 4: variable +--- stream_server_config + # NOTE: the newline escape sequence must be double-escaped, as nginx config + # parser will unescape first! + preread_by_lua_block { v = ngx.var["remote_addr"] ngx.print("remote_addr: ", v, "\n") } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +remote_addr: 127.0.0.1 + + + +=== TEST 5: variable (file) +--- stream_server_config + preread_by_lua_file html/test.lua; + content_by_lua_block { ngx.exit(ngx.OK) } +--- user_files +>>> test.lua +v = ngx.var["remote_addr"] +ngx.print("remote_addr: ", v, "\n") +--- stream_response +remote_addr: 127.0.0.1 + + + +=== TEST 6: nil is "nil" +--- stream_server_config + preread_by_lua_block { ngx.say(nil) } + + content_by_lua return; +--- stream_response +nil + + + +=== TEST 7: write boolean +--- stream_server_config + preread_by_lua_block { ngx.say(true, " ", false) } + + content_by_lua return; +--- stream_response +true false + + + +=== TEST 8: nginx quote sql string 1 +--- stream_server_config + preread_by_lua_block { ngx.say(ngx.quote_sql_str('hello\n\r\'"\\')) } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +'hello\n\r\'\"\\' + + + +=== TEST 9: nginx quote sql string 2 +--- stream_server_config + preread_by_lua_block { ngx.say(ngx.quote_sql_str("hello\n\r'\"\\")) } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +'hello\n\r\'\"\\' + + + +=== TEST 10: use dollar +--- stream_server_config + preread_by_lua_block { + local s = "hello 112"; + ngx.say(string.find(s, "%d+$")); + } + + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +79 + + + +=== TEST 11: short circuit +--- stream_server_config + preread_by_lua_block { + ngx.say("Hi") + ngx.eof() + ngx.exit(ngx.OK) + } + + content_by_lua_block { + print("HERE") + ngx.print("BAD") + } +--- stream_response +Hi + + + +=== TEST 12: nginx vars in script path +--- stream_server_config + preread_by_lua_file html/$remote_addr.lua; + + content_by_lua_block { + print("HERE") + ngx.print("BAD") + } +--- user_files +>>> 127.0.0.1.lua +ngx.say("Hi") +ngx.eof() +ngx.exit(ngx.OK) +--- stream_response +Hi + + + +=== TEST 13: phase postponing works +--- stream_server_config + ssl_preread on; + preread_by_lua_block { + local n = ngx.var.ssl_preread_server_name + + if n then + ngx.log(ngx.INFO, "$ssl_preread_server_name = " .. n) + end + + if n == "my.sni.server.name" then + ngx.exit(200) + end + + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", tonumber(ngx.var.server_port)) + if not ok then + ngx.say(err) + return ngx.exit(500) + end + + local _, err = sock:sslhandshake(nil, "my.sni.server.name") + if not err then + ngx.say("did not error as expected") + return ngx.exit(500) + end + + sock:close() + } + + return done; +--- stream_request +hello +--- stream_response chop +done +--- error_log +$ssl_preread_server_name = my.sni.server.name while prereading client data +--- no_error_log +[crit] +[warn] + + + +=== TEST 14: Lua file does not exist +--- stream_server_config + preread_by_lua_file html/test2.lua; + return here; +--- user_files +>>> test.lua +v = ngx.var["remote_addr"] +ngx.print("remote_addr: ", v, "\n") +--- error_log eval +qr/failed to load external Lua file ".*?\btest2\.lua": cannot open .*? No such file or directory/ diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/sleep.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/sleep.t new file mode 100644 index 000000000..157ca1586 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/sleep.t @@ -0,0 +1,117 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * 21; + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sleep 0.5 +--- stream_server_config + preread_by_lua_block { + ngx.update_time() + local before = ngx.now() + ngx.sleep(0.5) + local now = ngx.now() + ngx.say(now - before) + ngx.exit(200) + } + + return here; +--- stream_response_like chop +^0\.(?:4[5-9]\d*|5[0-9]\d*|5)$ +--- error_log +lua ready to sleep for +stream lua sleep timer expired + + + +=== TEST 2: sleep ag +--- stream_server_config + preread_by_lua_block { + ngx.update_time() + local before = ngx.now() + ngx.sleep("a") + local now = ngx.now() + ngx.say(now - before) + ngx.exit(200) + } + + return here; +--- error_log +bad argument #1 to 'sleep' + + + +=== TEST 3: sleep 0.5 - multi-times +--- stream_server_config + preread_by_lua_block { + ngx.update_time() + local start = ngx.now() + ngx.sleep(0.3) + ngx.sleep(0.3) + ngx.sleep(0.3) + ngx.say(ngx.now() - start) + ngx.exit(200) + } + + return here; +--- stream_response_like chop +^0\.(?:8[5-9]\d*|9[0-9]\d*|9)$ +--- error_log +lua ready to sleep for +stream lua sleep timer expired +--- no_error_log +[error] + + + +=== TEST 4: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep +--- stream_server_config + preread_by_lua_block { + ngx.sleep(1) + ngx.say("blah") + ngx.sleep(1) + ngx.exit(200) + } + + return here; +--- stream_response +blah +--- error_log +lua ready to sleep +stream lua sleep timer expired +--- no_error_log +[error] + + + +=== TEST 5: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep +--- stream_server_config + preread_by_lua_block { + ngx.sleep(0.3) + ngx.say("blah") + ngx.sleep(0.5) + ngx.say("hiya") + ngx.exit(200) + } + + return here; +--- stream_response +blah +hiya +--- error_log +lua ready to sleep for +stream lua sleep timer expired +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/socket-keepalive.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/socket-keepalive.t new file mode 100644 index 000000000..dce458637 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/socket-keepalive.t @@ -0,0 +1,162 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 5); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; +#$ENV{TEST_NGINX_REDIS_PORT} ||= 6379; + +$ENV{LUA_PATH} ||= + '/usr/local/openresty-debug/lualib/?.lua;/usr/local/openresty/lualib/?.lua;;'; + +no_long_string(); +#no_diff(); +#log_level 'warn'; + +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + preread_by_lua_block { + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + test.go(port) + } + content_by_lua return; +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end +end +--- stream_response_like +^connected: 1, reused: \d+ +request sent: 11 +received: OK +connected: 1, reused: [1-9]\d* +request sent: 11 +received: OK +--- no_error_log eval +["[error]", +"lua tcp socket keepalive: free connection pool for "] +--- grep_error_log eval +qr/lua tcp socket get keepalive peer: using connection|lua tcp socket keepalive create connection pool for key "[^"]+"/ + +--- grep_error_log_out eval +[ +qq{lua tcp socket keepalive create connection pool for key "127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" +lua tcp socket get keepalive peer: using connection +}, +"lua tcp socket get keepalive peer: using connection +lua tcp socket get keepalive peer: using connection +"] + + + +=== TEST 2: free up the whole connection pool if no active connections +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + preread_by_lua_block { + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port, true) + test.go(port, false) + } + + content_by_lua return; +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port, keepalive) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + if keepalive then + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + + else + sock:close() + end +end +--- stream_response_like +^connected: 1, reused: \d+ +request sent: 11 +received: OK +connected: 1, reused: [1-9]\d* +request sent: 11 +received: OK +--- no_error_log +[error] +--- error_log eval +["lua tcp socket get keepalive peer: using connection", +"lua tcp socket keepalive: free connection pool for "] diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/tcp-socket-timeout.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/tcp-socket-timeout.t new file mode 100644 index 000000000..59ea21d29 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/tcp-socket-timeout.t @@ -0,0 +1,525 @@ +# 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 ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'get helloworld'; +} + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 + 8); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +no_long_string(); +no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: lua_socket_connect_timeout only +--- stream_server_config + lua_socket_connect_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } + + content_by_lua return; +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 100 +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 2: sock:settimeout() overrides lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 60s; + lua_socket_log_errors off; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(150) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } + + content_by_lua return; +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 150 +--- no_error_log +[error] +[alert] +--- timeout: 10 + + + +=== TEST 3: sock:settimeout(nil) does not override lua_socket_connect_timeout +--- stream_server_config + lua_socket_log_errors off; + lua_socket_connect_timeout 102ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + #resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(nil) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } + + content_by_lua return; +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 102 +--- no_error_log +[error] +[alert] +--- timeout: 5 + + + +=== TEST 4: sock:settimeout(0) does not override lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 102ms; + lua_socket_log_errors off; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(0) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } + + content_by_lua return; +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 102 +--- timeout: 5 +--- no_error_log +[error] +[alert] +--- timeout: 10 + + + +=== TEST 5: -1 is bad timeout value +--- stream_server_config + lua_socket_connect_timeout 102ms; + lua_socket_log_errors off; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(-1) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } + + content_by_lua return; +--- error_log +bad timeout value +finalize stream request: 500 +--- timeout: 10 + + + +=== TEST 6: lua_socket_read_timeout only +--- stream_server_config + lua_socket_read_timeout 100ms; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket read timeout: 100 +lua tcp socket connect timeout: 60000 +lua tcp socket read timed out + + + +=== TEST 7: sock:settimeout() overrides lua_socket_read_timeout +--- stream_server_config + lua_socket_read_timeout 60s; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(150) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket read timeout: 150 +lua tcp socket read timed out + + + +=== TEST 8: sock:settimeout(nil) does not override lua_socket_read_timeout +--- stream_server_config + lua_socket_read_timeout 102ms; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(nil) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket read timeout: 102 +lua tcp socket read timed out + + + +=== TEST 9: sock:settimeout(0) does not override lua_socket_read_timeout +--- stream_server_config + lua_socket_read_timeout 102ms; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(0) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket read timeout: 102 +lua tcp socket read timed out + + + +=== TEST 10: -1 is bad timeout value +--- stream_server_config + lua_socket_read_timeout 102ms; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(-1) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } + + content_by_lua return; +--- error_log +bad timeout value +finalize stream request: 500 + + + +=== TEST 11: lua_socket_send_timeout only +--- stream_server_config + lua_socket_send_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket send timeout: 100 +lua tcp socket connect timeout: 60000 +lua tcp socket write timed out + + + +=== TEST 12: sock:settimeout() overrides lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 60s; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(150) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 150 +lua tcp socket write timed out + + + +=== TEST 13: sock:settimeout(nil) does not override lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 102ms; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(nil) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 102 +lua tcp socket write timed out + + + +=== TEST 14: sock:settimeout(0) does not override lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 102ms; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(0) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } + + content_by_lua return; +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 102 +lua tcp socket write timed out + + + +=== TEST 15: -1 is bad timeout value +--- stream_server_config + lua_socket_send_timeout 102ms; + #resolver $TEST_NGINX_RESOLVER ipv6=off; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(-1) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } + + content_by_lua return; +--- error_log +bad timeout value +finalize stream request: 500 diff --git a/src/deps/src/stream-lua-nginx-module/t/023-preread/tcp-socket.t b/src/deps/src/stream-lua-nginx-module/t/023-preread/tcp-socket.t new file mode 100644 index 000000000..ea7c5072d --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/023-preread/tcp-socket.t @@ -0,0 +1,328 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * 24; + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +#log_level 'warn'; + +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + return testing\npreread\n; +} + +--- stream_server_config + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + + content_by_lua return; +--- stream_response +connected: 1 +request sent: 57 +received: testing +received: preread +failed to receive a line: connection reset by peer [] +close: 1 nil +--- error_log +recv() failed (104: Connection reset by peer + + + +=== TEST 2: no resolver defined +--- stream_server_config + preread_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("agentzh.org", 1234) + if not ok then + ngx.say("failed to connect: ", err) + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + } + + content_by_lua return; +--- stream_response +failed to connect: no resolver defined to resolve "agentzh.org" +connected: nil +failed to send request: closed +--- error_log +attempt to send data on a closed socket: + + + +=== TEST 3: with resolver +--- timeout: 10 +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("agentzh.org", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET / HTTP/1.0\r\nHost: agentzh.org\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local line, err = sock:receive() + if line then + ngx.say("first line received: ", line) + + else + ngx.say("failed to receive the first line: ", err) + end + + line, err = sock:receive() + if line then + ngx.say("second line received: ", line) + + else + ngx.say("failed to receive the second line: ", err) + end + } + + content_by_lua return; +--- stream_response_like +connected: 1 +request sent: 56 +first line received: HTTP\/1\.1 200 OK +second line received: (?:Date|Server): .*? +--- no_error_log +[error] + + + +=== TEST 4: connection refused (tcp) +--- stream_server_config + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", 16787) + ngx.say("connect: ", ok, " ", err) + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + + content_by_lua return; +--- stream_response +connect: nil connection refused +send: nil closed +receive: nil closed +close: nil closed +--- error_log eval +qr/connect\(\) failed \(\d+: Connection refused\)/ + + + +=== TEST 5: connection timeout (tcp) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_socket_connect_timeout 100ms; + lua_socket_send_timeout 100ms; + lua_socket_read_timeout 100ms; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + ngx.say("connect: ", ok, " ", err) + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + + content_by_lua return; +--- stream_response +connect: nil timeout +send: nil closed +receive: nil closed +close: nil closed +--- error_log +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 6: not closed manually +--- stream_server_config + preread_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } + + content_by_lua return; +--- stream_response +connected: 1 +--- no_error_log +[error] + + + +=== TEST 7: resolver error (host not found) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("blah-blah-not-found.agentzh.org", 1234) + print("connected: ", ok, " ", err, " ", not ok) + if not ok then + ngx.say("failed to connect: ", err) + end + + ngx.say("connected: ", ok) + + local req = "GET / HTTP/1.0\\r\\nHost: agentzh.org\\r\\nConnection: close\\r\\n\\r\\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + } + + content_by_lua return; +--- stream_response_like +^failed to connect: blah-blah-not-found\.agentzh\.org could not be resolved(?: \(3: Host not found\))? +connected: nil +failed to send request: closed$ +--- error_log +attempt to send data on a closed socket +--- timeout: 10 + + + +=== TEST 8: resolver error (timeout) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 1ms; + preread_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("blah-blah-not-found.agentzh.org", port) + print("connected: ", ok, " ", err, " ", not ok) + if not ok then + ngx.say("failed to connect: ", err) + end + + ngx.say("connected: ", ok) + + local req = "GET / HTTP/1.0\\r\\nHost: agentzh.org\\r\\nConnection: close\\r\\n\\r\\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + } + + content_by_lua return; +--- stream_response_like +^failed to connect: blah-blah-not-found\.agentzh\.org could not be resolved(?: \(\d+: (?:Operation timed out|Host not found)\))? +connected: nil +failed to send request: closed$ +--- error_log +attempt to send data on a closed socket diff --git a/src/deps/src/stream-lua-nginx-module/t/025-codecache.t b/src/deps/src/stream-lua-nginx-module/t/025-codecache.t new file mode 100644 index 000000000..31a056e7a --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/025-codecache.t @@ -0,0 +1,1119 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * 140; + +#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua'; + +no_long_string(); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +check_accum_error_log(); +run_tests(); + +__DATA__ + +=== TEST 1: code cache on by default +--- stream_server_config + content_by_lua_file html/test.lua; + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/test.lua", "w")) + f:write("ngx.say(101)") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +ngx.say(32) +--- stream_response +32 +updated +32 +--- no_error_log +[alert] +[error] + + + +=== TEST 2: code cache explicitly on +--- stream_config + lua_code_cache on; + +--- stream_server_config + content_by_lua_file html/test.lua; + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/test.lua", "w")) + f:write("ngx.say(101)") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +ngx.say(32) +--- stream_response +32 +updated +32 +--- no_error_log +[alert] +[error] + + + +=== TEST 3: code cache explicitly off +--- stream_server_config + lua_code_cache off; + content_by_lua_file html/test.lua; + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/test.lua", "w")) + f:write("ngx.say(101)") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + lua_code_cache off; + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +ngx.say(32) +--- stream_response +32 +updated +101 +--- no_error_log +[error] +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 4: code cache explicitly off (stream {} level) +--- stream_config + lua_code_cache off; + +--- stream_server_config + content_by_lua_file html/test.lua; + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/test.lua", "w")) + f:write("ngx.say(101)") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +ngx.say(32) + +--- stream_response +32 +updated +101 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 5: code cache explicitly off (server level) but be overridden in the location +--- stream_config + lua_code_cache off; + +--- stream_server_config + lua_code_cache on; + content_by_lua_file html/test.lua; + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/test.lua", "w")) + f:write("ngx.say(101)") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + lua_code_cache on; + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +ngx.say(32) +--- stream_response +32 +updated +32 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 6: code cache explicitly off (affects require) + content_by_lua +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; + lua_code_cache off;" + +--- stream_server_config + content_by_lua_block { + local foo = require "foo"; + } + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/foo.lua", "w")) + f:write("module(..., package.seeall); ngx.say(102);") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + content_by_lua_block { + local foo = require "foo"; + } + +--- user_files +>>> foo.lua +module(..., package.seeall); ngx.say(32); + +--- stream_response +32 +updated +102 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 7: code cache explicitly off (affects require) + content_by_lua_file +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; + lua_code_cache off;" + +--- stream_server_config + content_by_lua_file html/test.lua; + +--- stream_server_config2 + content_by_lua_block { + -- os.execute("(echo HERE; pwd) > /dev/stderr") + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/foo.lua", "w")) + f:write("module(..., package.seeall); ngx.say(102);") + f:close() + ngx.say("updated") + } + +--- stream_server_config3 + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +local foo = require "foo"; +>>> foo.lua +module(..., package.seeall); ngx.say(32); +--- stream_response +32 +updated +102 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 8: no clear builtin lib "string" (file) +--- stream_server_config + lua_code_cache off; + content_by_lua_file html/test.lua; + +--- stream_server_config2 + lua_code_cache off; + content_by_lua_file html/test.lua; + +--- user_files +>>> test.lua +ngx.say(string.len("hello")) +ngx.say(table.concat({1,2,3}, ", ")) +--- stream_response +5 +1, 2, 3 +5 +1, 2, 3 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 9: no clear builtin lib "string" (inline) +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + ngx.say(string.len("hello")) + ngx.say(table.concat({1,2,3}, ", ")) + } + +--- stream_server_config2 + lua_code_cache off; + content_by_lua_block { + ngx.say(string.len("hello")) + ngx.say(table.concat({1,2,3}, ", ")) + } + +--- stream_response +5 +1, 2, 3 +5 +1, 2, 3 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 10: no clear builtin libs (misc) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" + +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local test = require("test") + } + +--- stream_server_config2 + lua_code_cache off; + content_by_lua_block { + local test = require("test") + } + +--- user_files +>>> test.lua +module("test", package.seeall) + +string = require("string") +math = require("math") +io = require("io") +os = require("os") +table = require("table") +coroutine = require("coroutine") +package = require("package") +ngx.say("OK") +--- stream_response +OK +OK +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 11: do not skip luarocks +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; + lua_code_cache off;" + +--- stream_server_config + content_by_lua_block { + package.loaded.luarocks = nil; + local foo = require "luarocks"; + foo.hi() + } + +--- stream_server_config2 + content_by_lua_block { + local foo = package.loaded.luarocks + if foo then + ngx.say("found") + else + ngx.say("not found") + end + } + +--- stream_server_config3 + content_by_lua_block { + local foo = package.loaded.luarocks + if foo then + ngx.say("found") + else + ngx.say("not found") + end + } + +--- user_files +>>> luarocks.lua +module(..., package.seeall); + +ngx.say("loading"); + +function hi () + ngx.say("hello, foo") +end; +--- stream_response +loading +hello, foo +not found +not found +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 12: do not skip luarocks* +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; + lua_code_cache off;" +--- stream_server_config + + content_by_lua_block { + package.loaded.luarocks2 = nil; + local foo = require "luarocks2"; + foo.hi() + } + +--- stream_server_config2 + + content_by_lua_block { + local foo = package.loaded.luarocks2 + if foo then + ngx.say("found") + else + ngx.say("not found") + end + } + +--- stream_server_config3 + + content_by_lua_block { + local foo = package.loaded.luarocks2 + if foo then + ngx.say("found") + else + ngx.say("not found") + end + } + +--- user_files +>>> luarocks2.lua +module(..., package.seeall); + +ngx.say("loading"); + +function hi () + ngx.say("hello, foo") +end; +--- stream_response +loading +hello, foo +not found +not found +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 13: clear _G table +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + if not _G.foo then + _G.foo = 1 + else + _G.foo = _G.foo + 1 + end + ngx.say("_G.foo: ", _G.foo) + } +--- stream_response +_G.foo: 1 +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 14: github #257: globals cleared when code cache off +--- stream_config + lua_code_cache off; + init_by_lua_block { + test = setfenv( + function() + ngx.say(tostring(table)) + end, + setmetatable({}, + { + __index = function(self, key) + return rawget(self, key) or _G[key] + end + })) + } +--- stream_server_config + content_by_lua_block { test() } + +--- stream_response_like chop +^table: 0x\d*?[1-9a-fA-F] +--- no_error_log +[error] +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 15: lua_code_cache off + FFI-based Lua modules +--- stream_config + lua_code_cache off; + lua_package_path "$prefix/html/?.lua;;"; + +--- stream_server_config + content_by_lua_block { + if not jit then + ngx.say("skipped for non-LuaJIT") + else + local test = require("test") + ngx.say("test module loaded: ", test and true or false) + collectgarbage() + end + } +--- user_files +>>> test.lua +local ffi = require "ffi" + +ffi.cdef[[ + int my_test_function_here(void *p); + int my_test_function_here2(void *p); + int my_test_function_here3(void *p); +]] + +return { +} +--- stream_response_like chop +^(?:skipped for non-LuaJIT|test module loaded: true)$ +--- no_error_log +[error] +--- error_log eval +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ + + + +=== TEST 16: ngx.timer.* +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + ngx.timer.at(0, function () + local foo = ngx.unescape_uri("a%20b") + ngx.log(ngx.WARN, "foo = ", foo) + end) + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.2 +--- no_error_log +[error] +--- error_log eval +["foo = a b", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/ +] + + + +=== TEST 17: lua_max_pending_timers - chained timers (non-zero delay) - not exceeding +--- stream_config + lua_max_pending_timers 1; + lua_code_cache off; + +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + local ok, err = ngx.timer.at(0.01, g) + if not ok then + fail("failed to set timer: ", err) + return + end + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +stream lua decrementing the reference count for Lua VM: 3 + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua finalize fake request", +"trace: [m][f][g]", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua close the global Lua VM", +"stream lua decrementing the reference count for Lua VM: 2", +"stream lua decrementing the reference count for Lua VM: 1", +] + + + +=== TEST 18: lua variable sharing via upvalue +--- stream_config + lua_code_cache off; +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local foo + local function f() + foo = 3 + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.06) + ngx.say("foo = ", foo) + } +--- stream_response +registered timer +foo = 3 + +--- wait: 0.1 +--- no_error_log +[error] +decrementing the reference count for Lua VM: 3 + +--- error_log eval +[ +"stream lua ngx.timer expired", +"stream lua finalize fake request", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua close the global Lua VM", +"stream lua decrementing the reference count for Lua VM: 2", +"stream lua decrementing the reference count for Lua VM: 1", +] + + + +=== TEST 19: lua_max_running_timers (just not enough) +--- stream_config + lua_max_running_timers 1; +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local f, g + + g = function () + print("HERE in g") + ngx.sleep(0.01) + -- collectgarbage() + print("HERE out g") + end + + f = function () + print("HERE in f") + ngx.sleep(0.01) + -- collectgarbage() + print("HERE out f") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] + +--- error_log eval +[ +"stream lua: 1 lua_max_running_timers are not enough", +"stream lua ngx.timer expired", +"stream lua finalize fake request", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua decrementing the reference count for Lua VM: 3", +"stream lua decrementing the reference count for Lua VM: 2", +"stream lua decrementing the reference count for Lua VM: 1", +"stream lua close the global Lua VM", +] + + + +=== TEST 20: GC issue with the on_abort thread object +--- stream_server_config + lua_code_cache off; + lua_check_client_abort on; + + content_by_lua_block { + ngx.on_abort(function () end) + collectgarbage() + ngx.sleep(1) + } +--- abort +--- timeout: 0.2 +--- wait: 1 +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +stream lua decrementing the reference count for Lua VM: 2 +stream lua decrementing the reference count for Lua VM: 3 +--- error_log eval +["stream lua decrementing the reference count for Lua VM: 1", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua close the global Lua VM", +] + + + +=== TEST 21: multiple parallel timers +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + fail("failed to set timer: ", err) + return + end + local ok, err = ngx.timer.at(0.01, g) + if not ok then + fail("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +stream lua decrementing the reference count for Lua VM: 4 + +--- error_log eval +[ +"stream lua ngx.timer expired", +"stream lua finalize fake request", +"trace: [m][f][g]", +"stream lua decrementing the reference count for Lua VM: 3", +"stream lua decrementing the reference count for Lua VM: 2", +"stream lua decrementing the reference count for Lua VM: 1", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua close the global Lua VM", +] + + + +=== TEST 22: cosocket connection pool timeout (after Lua VM destroys) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive(1) + if not ok then + ngx.say("failed to set reusable: ", err) + end +end +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +--- no_error_log +[error] +stream lua tcp socket keepalive max idle timeout + +--- error_log eval +[ +qq{stream lua tcp socket keepalive create connection pool for key "127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}"}, +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +qr/\blua tcp socket keepalive: free connection pool [0-9A-F]+ for "127.0.0.1:/, +] + + + +=== TEST 23: cosocket connection pool timeout (before Lua VM destroys) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive(1) + if not ok then + ngx.say("failed to set reusable: ", err) + end + ngx.sleep(0.01) +end +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +--- no_error_log +[error] +--- error_log eval +[ +qq{stream lua tcp socket keepalive create connection pool for key "127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}"}, +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua tcp socket keepalive: free connection pool for ", +"stream lua tcp socket keepalive max idle timeout", +] + + + +=== TEST 24: lua_max_running_timers (just not enough, also low lua_max_pending_timers) +--- stream_config + lua_max_running_timers 1; + lua_max_pending_timers 10; +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local f, g + + g = function () + ngx.sleep(0.01) + collectgarbage() + end + + f = function () + ngx.sleep(0.01) + collectgarbage() + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] + +--- error_log eval +[ +"stream lua: 1 lua_max_running_timers are not enough", +"stream lua ngx.timer expired", +"stream lua finalize fake request", +qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, +"stream lua decrementing the reference count for Lua VM: 3", +"stream lua decrementing the reference count for Lua VM: 2", +"stream lua decrementing the reference count for Lua VM: 1", +"stream lua close the global Lua VM", +] + + + +=== TEST 25: make sure inline code keys are correct +--- stream_config + include ../html/a/proxy.conf; + include ../html/b/proxy.conf; + include ../html/c/proxy.conf; +--- stream_server_config + content_by_lua_block { + for _, n in ipairs({ 1, 2, 1, 3 }) do + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx" .. n .. ".sock") + if not ok then + ngx.log(ngx.ERR, "could not connect: ", err) + return + end + + local res, err = sock:receive(11) + if not res then + ngx.log(ngx.ERR, "could not receive: ", err) + return + end + + ngx.say(res) + + sock:close() + end + } +--- user_files +>>> a/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx1.sock; + content_by_lua_block { ngx.say("1 is called") } +} + +>>> b/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock; + content_by_lua_block { ngx.say("2 is called") } +} + +>>> c/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx3.sock; + content_by_lua_block { ngx.say("2 is called") } +} +--- stream_response +1 is called +2 is called +1 is called +2 is called +--- grep_error_log eval: qr/looking up Lua code cache with key '=content_by_lua\(proxy\.conf:\d+\).*?'/ +--- grep_error_log_out eval +[ +"looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +", +"looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +"] +--- log_level: debug +--- no_error_log +[error] + + + +=== TEST 26: make sure inline code keys are correct +--- stream_config + include ../html/a/proxy.conf; + include ../html/b/proxy.conf; + include ../html/c/proxy.conf; +--- stream_server_config + content_by_lua_block { + for _, n in ipairs({ 1, 2, 1, 3 }) do + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx" .. n .. ".sock") + if not ok then + ngx.log(ngx.ERR, "could not connect: ", err) + return + end + + local res, err = sock:receive(11) + if not res then + ngx.log(ngx.ERR, "could not receive: ", err) + return + end + + ngx.say(res) + + sock:close() + end + } +--- user_files +>>> a.lua +ngx.say("1 is called") + +>>> b.lua +ngx.say("2 is called") + +>>> c.lua +ngx.say("2 is called") + +>>> a/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx1.sock; + content_by_lua_file html/a.lua; +} + +>>> b/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock; + content_by_lua_file html/b.lua; +} + +>>> c/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx3.sock; + content_by_lua_file html/c.lua; +} +--- stream_response +1 is called +2 is called +1 is called +2 is called +--- grep_error_log eval: qr/looking up Lua code cache with key 'nhlf_.*?'/ +--- grep_error_log_out eval +[ +"looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_68f5f4e946c3efd1cc206452b807e8b6' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_042c9b3a136fbacbbd0e4b9ad10896b7' +", +"looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_68f5f4e946c3efd1cc206452b807e8b6' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_042c9b3a136fbacbbd0e4b9ad10896b7' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_68f5f4e946c3efd1cc206452b807e8b6' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_042c9b3a136fbacbbd0e4b9ad10896b7' +"] +--- log_level: debug +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/030-uri-args.t b/src/deps/src/stream-lua-nginx-module/t/030-uri-args.t new file mode 100644 index 000000000..2db36bc94 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/030-uri-args.t @@ -0,0 +1,283 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +no_root_location(); + +#no_shuffle(); +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: ngx.encode_args (sanity) +--- stream_server_config + content_by_lua_block { + local t = {a = "bar", b = "foo"} + ngx.say(ngx.encode_args(t)) + } +--- stream_response eval +qr/a=bar&b=foo|b=foo&a=bar/ +--- no_error_log +[error] + + + +=== TEST 2: ngx.encode_args (empty table) +--- stream_server_config + content_by_lua_block { + local t = {a = nil} + ngx.say("args:" .. ngx.encode_args(t)) + } +--- stream_response +args: +--- no_error_log +[error] + + + +=== TEST 3: ngx.encode_args (value is table) +--- stream_server_config + content_by_lua_block { + local t = {a = {9, 2}, b = 3} + ngx.say("args:" .. ngx.encode_args(t)) + } +--- stream_response_like +(?x) ^args: + (?= .*? \b a=9 \b ) # 3 chars + (?= .*? \b a=2 \b ) # 3 chars + (?= .*? \b b=3 \b ) # 3 chars + (?= (?: [^&]+ & ){2} [^&]+ $ ) # requires exactly 2 &'s + (?= .{11} $ ) # requires for total 11 chars (exactly) in the string +--- no_error_log +[error] + + + +=== TEST 4: ngx.encode_args (boolean values) +--- stream_server_config + content_by_lua_block { + local t = {a = true, foo = 3} + ngx.say("args: " .. ngx.encode_args(t)) + } +--- stream_response_like +^args: (?:a&foo=3|foo=3&a)$ +--- no_error_log +[error] + + + +=== TEST 5: ngx.encode_args (boolean values, false) +--- stream_server_config + content_by_lua_block { + local t = {a = false, foo = 3} + ngx.say("args: " .. ngx.encode_args(t)) + } +--- stream_response +args: foo=3 +--- no_error_log +[error] + + + +=== TEST 6: boolean values in ngx.encode_args +--- stream_server_config + content_by_lua_block { + local t = {bar = {32, true}, foo = 3} + ngx.say(ngx.encode_args(t)) + } +--- stream_response_like +(?x) ^ + (?= .*? \b bar=32 \b ) # 6 chars + (?= .*? \b bar (?!=) \b ) # 3 chars + (?= .*? \b foo=3 \b ) # 5 chars + (?= (?: [^&]+ & ){2} [^&]+ $ ) # requires exactly 2 &'s + (?= .{16} $ ) # requires for total 16 chars (exactly) in the string +--- no_error_log +[error] + + + +=== TEST 7: ngx.encode_args (bad user data value) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local t = {bar = ngx.shared.dogs, foo = 3} + rc, err = pcall(ngx.encode_args, t) + ngx.say("rc: ", rc, ", err: ", err) + } +--- stream_response +rc: false, err: attempt to use userdata as query arg value +--- no_error_log +[error] + + + +=== TEST 8: ngx.encode_args (empty table) +--- stream_server_config + content_by_lua_block { + local t = {} + ngx.say("args: ", ngx.encode_args(t)) + } +--- stream_response +args: +--- no_error_log +[error] + + + +=== TEST 9: ngx.encode_args (bad arg) +--- stream_server_config + content_by_lua_block { + local rc, err = pcall(ngx.encode_args, true) + ngx.say("rc: ", rc, ", err: ", err) + } +--- stream_response +rc: false, err: bad argument #1 to '?' (table expected, got boolean) +--- no_error_log +[error] + + + +=== TEST 10: ngx.decode_args (sanity) +--- stream_server_config + content_by_lua_block { + local args = "a=bar&b=foo" + args = ngx.decode_args(args) + ngx.say("a = ", args.a) + ngx.say("b = ", args.b) + } +--- stream_response +a = bar +b = foo +--- no_error_log +[error] + + + +=== TEST 11: ngx.decode_args (multi-value) +--- stream_server_config + content_by_lua_block { + local args = "a=bar&b=foo&a=baz" + args = ngx.decode_args(args) + ngx.say("a = ", table.concat(args.a, ", ")) + ngx.say("b = ", args.b) + } +--- stream_response +a = bar, baz +b = foo +--- no_error_log +[error] + + + +=== TEST 12: ngx.decode_args (empty string) +--- stream_server_config + content_by_lua_block { + local args = "" + args = ngx.decode_args(args) + ngx.say("n = ", #args) + } +--- stream_response +n = 0 +--- no_error_log +[error] + + + +=== TEST 13: ngx.decode_args (boolean args) +--- stream_server_config + content_by_lua_block { + local args = "a&b" + args = ngx.decode_args(args) + ngx.say("a = ", args.a) + ngx.say("b = ", args.b) + } +--- stream_response +a = true +b = true +--- no_error_log +[error] + + + +=== TEST 14: ngx.decode_args (empty value args) +--- stream_server_config + content_by_lua_block { + local args = "a=&b=" + args = ngx.decode_args(args) + ngx.say("a = ", args.a) + ngx.say("b = ", args.b) + } +--- stream_response +a = +b = +--- no_error_log +[error] + + + +=== TEST 15: ngx.decode_args (max_args = 1) +--- stream_server_config + content_by_lua_block { + local args = "a=bar&b=foo" + args = ngx.decode_args(args, 1) + ngx.say("a = ", args.a) + ngx.say("b = ", args.b) + } +--- stream_response +a = bar +b = nil +--- no_error_log +[error] + + + +=== TEST 16: ngx.decode_args (max_args = -1) +--- stream_server_config + content_by_lua_block { + local args = "a=bar&b=foo" + args = ngx.decode_args(args, -1) + ngx.say("a = ", args.a) + ngx.say("b = ", args.b) + } +--- stream_response +a = bar +b = foo +--- no_error_log +[error] + + + +=== TEST 17: ngx.decode_args should not modify lua strings in place +--- stream_server_config + content_by_lua_block { + local s = "f+f=bar&B=foo" + args = ngx.decode_args(s) + local arr = {} + for k, v in pairs(args) do + table.insert(arr, k) + end + table.sort(arr) + for i, k in ipairs(arr) do + ngx.say("key: ", k) + end + ngx.say("s = ", s) + } +--- stream_response +key: B +key: f f +s = f+f=bar&B=foo +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/032-iolist.t b/src/deps/src/stream-lua-nginx-module/t/032-iolist.t new file mode 100644 index 000000000..5377ece27 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/032-iolist.t @@ -0,0 +1,69 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local table = {"hello", nil, true, false, 32.5, 56} + ngx.say(table) + } +--- stream_response +helloniltruefalse32.556 +--- no_error_log +[error] + + + +=== TEST 2: nested table +--- stream_server_config + content_by_lua_block { + local table = {"hello", nil, true, false, 32.5, 56} + local table2 = {table, "--", table} + ngx.say(table2) + } +--- stream_response +helloniltruefalse32.556--helloniltruefalse32.556 +--- no_error_log +[error] + + + +=== TEST 3: non-array table +--- stream_server_config + content_by_lua_block { + local table = {foo = 3} + ngx.say(table) + } +--- stream_response +--- error_log +bad argument #1 to 'say' (non-array table found) + + + +=== TEST 4: bad data type in table +--- stream_server_config + content_by_lua_block { + local f = function () return end + local table = {1, 3, f} + ngx.say(table) + } +--- stream_response +--- error_log +bad argument #1 to 'say' (bad data type function found) diff --git a/src/deps/src/stream-lua-nginx-module/t/033-ctx.t b/src/deps/src/stream-lua-nginx-module/t/033-ctx.t new file mode 100644 index 000000000..ec21aae7f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/033-ctx.t @@ -0,0 +1,131 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + ngx.ctx.foo = 32; + ngx.say(ngx.ctx.foo) + } +--- stream_response +32 +--- no_error_log +[error] + + + +=== TEST 2: rewrite, access, and content +TODO +--- stream_server_config + rewrite_by_lua_block { + print("foo = ", ngx.ctx.foo) + ngx.ctx.foo = 76 + } + access_by_lua_block { + ngx.ctx.foo = ngx.ctx.foo + 3 + } + content_by_lua_block { + ngx.say(ngx.ctx.foo) + } +--- stream_response +79 +--- no_error_log +[error] +--- grep_error_log eval: qr/foo = [^,]+/ +--- log_level: info +--- grep_error_log_out +foo = nil +--- SKIP + + + +=== TEST 3: different requests have different ngx.ctx +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.ctx.foo) + ngx.ctx.foo = 32 + ngx.say(ngx.ctx.foo) + } +--- stream_server_config2 + content_by_lua_block { + ngx.say(ngx.ctx.foo) + } +--- stream_response +nil +32 +nil +--- no_error_log +[error] + + + +=== TEST 4: overriding ctx +--- stream_server_config + content_by_lua_block { + ngx.ctx = { foo = 32, bar = 54 }; + ngx.say(ngx.ctx.foo) + ngx.say(ngx.ctx.bar) + + ngx.ctx = { baz = 56 }; + ngx.say(ngx.ctx.foo) + ngx.say(ngx.ctx.baz) + } +--- stream_response +32 +54 +nil +56 +--- no_error_log +[error] + + + +=== TEST 5: ngx.ctx + ngx.exit(ngx.ERROR) + log_by_lua +TODO +--- stream_server_config + rewrite_by_lua_block { + ngx.ctx.foo = 32; + ngx.exit(ngx.ERROR) + } + log_by_lua_block { ngx.log(ngx.WARN, "ngx.ctx = ", ngx.ctx.foo) } +--- stream_response +--- no_error_log +[error] +--- error_log +ngx.ctx = 32 +--- SKIP + + + +=== TEST 6: ngx.ctx + ngx.exit(200) + log_by_lua +TODO +--- stream_server_config + rewrite_by_lua_block { + ngx.ctx.foo = 32; + ngx.say(ngx.ctx.foo) + ngx.exit(200) + } + log_by_lua 'ngx.log(ngx.WARN, "ctx.foo = ", ngx.ctx.foo)'; +--- stream_response +32 +--- no_error_log +[error] +--- error_log +ctx.foo = 32 +--- SKIP diff --git a/src/deps/src/stream-lua-nginx-module/t/034-match.t b/src/deps/src/stream-lua-nginx-module/t/034-match.t new file mode 100644 index 000000000..73cbc086f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/034-match.t @@ -0,0 +1,936 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- no_error_log +[error] + + + +=== TEST 2: escaping sequences +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", [[(\d+)]]) + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- no_error_log +[error] + + + +=== TEST 3: single capture +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]{2})[0-9]+") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +12 +--- no_error_log +[error] + + + +=== TEST 4: multiple captures +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([a-z]+).*?([0-9]{2})[0-9]+", "") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +12 +--- no_error_log +[error] + + + +=== TEST 5: multiple captures (with o) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([a-z]+).*?([0-9]{2})[0-9]+", "o") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +12 +--- no_error_log +[error] + + + +=== TEST 6: not matched +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "foo") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +not matched: nil +--- no_error_log +[error] + + + +=== TEST 7: case sensitive by default +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "HELLO") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +not matched: nil +--- no_error_log +[error] + + + +=== TEST 8: case insensitive +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "HELLO", "i") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello +--- no_error_log +[error] + + + +=== TEST 9: UTF-8 mode +--- stream_server_config + content_by_lua_block { + rc, err = pcall(ngx.re.match, "hello章亦春", "HELLO.{2}", "iu") + if not rc then + ngx.say("FAIL: ", err) + return + end + local m = err + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response_like chop +^(?:FAIL: bad argument \#2 to '\?' \(pcre_compile\(\) failed: this version of PCRE is not compiled with PCRE_UTF8 support in "HELLO\.\{2\}" at "HELLO\.\{2\}"\)|hello章亦)$ +--- no_error_log +[error] + + + +=== TEST 10: multi-line mode (^ at line head) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", "^world", "m") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +world +--- no_error_log +[error] + + + +=== TEST 11: multi-line mode (. does not match \n) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", ".*", "m") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello +--- no_error_log +[error] + + + +=== TEST 12: single-line mode (^ as normal) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", "^world", "s") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +not matched: nil +--- no_error_log +[error] + + + +=== TEST 13: single-line mode (dot all) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", ".*", "s") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello +world +--- no_error_log +[error] + + + +=== TEST 14: extended mode (ignore whitespaces) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", [[\w \w]], "x") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +he +--- no_error_log +[error] + + + +=== TEST 15: bad pattern +--- stream_server_config + content_by_lua_block { + local m, err = ngx.re.match("hello\nworld", "(abc") + if m then + ngx.say(m[0]) + + else + if err then + ngx.say("error: ", err) + + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 16: bad option +--- stream_server_config + content_by_lua_block { + rc, m = pcall(ngx.re.match, "hello\nworld", ".*", "Hm") + if rc then + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + else + ngx.say("error: ", m) + end + } +--- stream_response_like chop +error: .*?unknown flag "H" \(flags "Hm"\) + + + +=== TEST 17: extended mode (ignore whitespaces) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, world", "(world)|(hello)", "x") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello +false +hello +--- no_error_log +[error] + + + +=== TEST 18: optional trailing captures +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)(h?)") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response eval +"1234 +1234 + +" +--- no_error_log +[error] + + + +=== TEST 19: anchored match (failed) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)", "a") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +not matched! +--- no_error_log +[error] + + + +=== TEST 20: anchored match (succeeded) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("1234, hello", "([0-9]+)", "a") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- no_error_log +[error] + + + +=== TEST 21: match with ctx but no pos +--- stream_server_config + content_by_lua_block { + local ctx = {} + m = ngx.re.match("1234, hello", "([0-9]+)", "", ctx) + if m then + ngx.say(m[0]) + ngx.say(ctx.pos) + else + ngx.say("not matched!") + ngx.say(ctx.pos) + end + } +--- stream_response +1234 +5 +--- no_error_log +[error] + + + +=== TEST 22: match with ctx and a pos +--- stream_server_config + content_by_lua_block { + local ctx = { pos = 3 } + m = ngx.re.match("1234, hello", "([0-9]+)", "", ctx) + if m then + ngx.say(m[0]) + ngx.say(ctx.pos) + else + ngx.say("not matched!") + ngx.say(ctx.pos) + end + } +--- stream_response +34 +5 +--- no_error_log +[error] + + + +=== TEST 23: match (look-behind assertion) +--- stream_server_config + content_by_lua_block { + local ctx = {} + local m = ngx.re.match("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz", "", ctx) + ngx.say(m and m[0]) + + m = ngx.re.match("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz", "", ctx) + ngx.say(m and m[0]) + } +--- stream_response +bar +baz +--- no_error_log +[error] + + + +=== TEST 24: escaping sequences +--- stream_server_config + content_by_lua_file html/a.lua; +--- user_files +>>> a.lua +local uri = "2" +local regex = '(?:>[\\w\\s]*)'; +ngx.say("regex: ", regex) +m = ngx.re.match(uri, regex, "oi") +if m then + ngx.say("[", m[0], "]") +else + ngx.say("not matched!") +end +--- stream_response +regex: (?:>[\w\s]*) +[>2] +--- no_error_log +[error] + + + +=== TEST 25: long brackets +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", [[\d+]]) + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- no_error_log +[error] + + + +=== TEST 26: bad pattern +--- stream_server_config + content_by_lua_block { + local m, err = ngx.re.match("hello, 1234", "([0-9]+") + if m then + ngx.say(m[0]) + + else + if err then + ngx.say("error: ", err) + + else + ngx.say("not matched!") + end + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "([0-9]+" + +--- no_error_log +[error] + + + +=== TEST 27: long brackets containing [...] +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", [[([0-9]+)]]) + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- no_error_log +[error] + + + +=== TEST 28: bug report (github issue #72) +--- stream_server_config + content_by_lua_block { + local m, err = ngx.re.match("hello", "hello", "j") + ngx.say("done: ", m and "yes" or "no") + } +--- stream_server_config2 + content_by_lua_block { + ngx.re.match("hello", "world", "j") + ngx.say("done: ", m and "yes" or "no") + } +--- stream_response +done: yes +done: no +--- no_error_log +[error] + + + +=== TEST 29: non-empty subject, empty pattern +--- stream_server_config + content_by_lua_block { + local ctx = {} + local m = ngx.re.match("hello, 1234", "", "", ctx) + if m then + ngx.say("pos: ", ctx.pos) + ngx.say("m: ", m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +pos: 1 +m: +--- no_error_log +[error] + + + +=== TEST 30: named subpatterns w/ extraction +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "(?[a-z]+), [0-9]+") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m.first) + ngx.say(m.second) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +hello +nil +--- no_error_log +[error] + + + +=== TEST 31: duplicate named subpatterns w/ extraction +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "(?[a-z]+), (?[0-9]+)", "D") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(table.concat(m.first,"-")) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +1234 +hello-1234 +--- no_error_log +[error] + + + +=== TEST 32: named captures are empty strings +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("1234", "(?[a-z]*)([0-9]+)") + if m then + ngx.say(m[0]) + ngx.say(m.first) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + +1234 +--- no_error_log +[error] + + + +=== TEST 33: named captures are nil +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, world", "(world)|(hello)|(?howdy)") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(m[3]) + ngx.say(m["named"]) + else + ngx.say("not matched!") + end + } +--- stream_response +hello +false +hello +false +false +--- no_error_log +[error] + + + +=== TEST 34: duplicate named subpatterns +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, world", + [[(?\w+), (?\w+)]], + "D") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(table.concat(m.named,"-")) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, world +hello +world +hello-world +--- no_error_log +[error] + + + +=== TEST 35: Javascript compatible mode +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("章", [[\u7AE0]], "uJ") + if m then + ngx.say("matched: ", m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +matched: 章 +--- no_error_log +[error] + + + +=== TEST 36: empty duplicate captures +--- stream_server_config + content_by_lua_block { + local target = 'test' + local regex = '^(?:(?(?:foo))|(?(?:bar))|(?(?:test)))$' + + -- Note the D here + local m = ngx.re.match(target, regex, 'D') + + ngx.say(type(m.group1)) + ngx.say(type(m.group2)) + } +--- stream_response +nil +nil +--- no_error_log +[error] + + + +=== TEST 37: bad UTF-8 +--- stream_server_config + content_by_lua_block { + local target = "你好" + local regex = "你好" + + -- Note the D here + local m, err = ngx.re.match(string.sub(target, 1, 4), regex, "u") + + if err then + ngx.say("error: ", err) + return + end + + if m then + ngx.say("matched: ", m[0]) + else + ngx.say("not matched") + end + } +--- stream_response_like chop +^error: pcre_exec\(\) failed: -10$ + +--- no_error_log +[error] + + + +=== TEST 38: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("你好", ".", "U") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +你 +--- no_error_log +[error] + + + +=== TEST 39: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("你好", ".", "u") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 + +--- stream_response +你 +--- no_error_log +[error] + + + +=== TEST 40: just hit match limit +--- stream_config + lua_regex_match_limit 5000; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local res, err = ngx.re.match(s, re, "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not res then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +error: pcre_exec() failed: -8 + + + +=== TEST 41: just not hit match limit +--- stream_config + lua_regex_match_limit 5700; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local res, err = ngx.re.match(s, re, "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not res then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +failed to match +--- no_error_log +[error] + + + +=== TEST 42: extra table argument +--- stream_server_config + content_by_lua_block { + local res = {} + local s = "hello, 1234" + m = ngx.re.match(s, [[(\d)(\d)]], "o", nil, res) + if m then + ngx.say("1: m size: ", #m) + ngx.say("1: res size: ", #res) + else + ngx.say("1: not matched!") + end + m = ngx.re.match(s, [[(\d)]], "o", nil, res) + if m then + ngx.say("2: m size: ", #m) + ngx.say("2: res size: ", #res) + else + ngx.say("2: not matched!") + end + } +--- stream_response +1: m size: 2 +1: res size: 2 +2: m size: 2 +2: res size: 2 +--- no_error_log +[error] + + + +=== TEST 43: init_by_lua +--- stream_config + init_by_lua_block { + m = ngx.re.match("hello, 1234", [[(\d+)]]) +--- stream_server_config + content_by_lua_block { + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- no_error_log +[error] +--- SKIP diff --git a/src/deps/src/stream-lua-nginx-module/t/035-gmatch.t b/src/deps/src/stream-lua-nginx-module/t/035-gmatch.t new file mode 100644 index 000000000..9b829bba9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/035-gmatch.t @@ -0,0 +1,754 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(5); + +plan tests => repeat_each() * (blocks() * 2 + 7); + +our $HtmlDir = html_dir; + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: gmatch +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello, world", "[a-z]+") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +hello +world + + + +=== TEST 2: fail to match +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[0-9]") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +nil +nil +nil + + + +=== TEST 3: match but iterate more times (not just match at the end) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world!", "[a-z]+") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +hello +world +nil +nil + + + +=== TEST 4: match but iterate more times (just matched at the end) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +hello +world +nil +nil + + + +=== TEST 5: anchored match (failed) +--- stream_server_config + content_by_lua_block { + it = ngx.re.gmatch("hello, 1234", "([0-9]+)", "a") + ngx.say(it()) + } +--- stream_response +nil + + + +=== TEST 6: anchored match (succeeded) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("12 hello 34", "[0-9]", "a") + local m = it() + ngx.say(m[0]) + m = it() + ngx.say(m[0]) + ngx.say(it()) + } +--- stream_response +1 +2 +nil + + + +=== TEST 7: non-anchored gmatch (without regex cache) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("12 hello 34", "[0-9]") + local m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + } +--- stream_response +1 +2 +3 +4 +nil + + + +=== TEST 8: non-anchored gmatch (with regex cache) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("12 hello 34", "[0-9]", "o") + local m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + m = it() + ngx.say(m and m[0]) + } +--- stream_response +1 +2 +3 +4 +nil + + + +=== TEST 9: anchored match (succeeded) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("12 hello 34", "[0-9]", "a") + local m = it() + ngx.say(m[0]) + m = it() + ngx.say(m[0]) + ngx.say(it()) + } +--- stream_response +1 +2 +nil + + + +=== TEST 10: gmatch (look-behind assertion) +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("{foobar}, {foobaz}", "(?<=foo)ba[rz]") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +bar +baz + + + +=== TEST 11: gmatch (look-behind assertion 2) +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +bar +baz + + + +=== TEST 12: with regex cache +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, 1234", "([A-Z]+)", "io") + local m = it() + ngx.say(m and m[0]) + + it = ngx.re.gmatch("1234, okay", "([A-Z]+)", "io") + m = it() + ngx.say(m and m[0]) + + it = ngx.re.gmatch("hi, 1234", "([A-Z]+)", "o") + m = it() + ngx.say(m and m[0]) + } +--- stap2 +F(ngx_http_lua_ngx_re_gmatch_iterator) { println("iterator") } +F(ngx_http_lua_ngx_re_gmatch_gc) { println("gc") } +F(ngx_http_lua_ngx_re_gmatch_cleanup) { println("cleanup") } +--- stream_response +hello +okay +nil + + + +=== TEST 13: exceeding regex cache max entries +--- stream_config + lua_regex_cache_max_entries 2; +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, 1234", "([0-9]+)", "o") + local m = it() + ngx.say(m and m[0]) + + it = ngx.re.gmatch("howdy, 567", "([0-9]+)", "oi") + m = it() + ngx.say(m and m[0]) + + it = ngx.re.gmatch("hiya, 98", "([0-9]+)", "ox") + m = it() + ngx.say(m and m[0]) + } +--- stream_response +1234 +567 +98 + + + +=== TEST 14: disable regex cache completely +--- stream_config + lua_regex_cache_max_entries 0; +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, 1234", "([0-9]+)", "o") + local m = it() + ngx.say(m and m[0]) + + it = ngx.re.gmatch("howdy, 567", "([0-9]+)", "oi") + local m = it() + ngx.say(m and m[0]) + + it = ngx.re.gmatch("hiya, 98", "([0-9]+)", "ox") + local m = it() + ngx.say(m and m[0]) + } +--- stream_response +1234 +567 +98 + + + +=== TEST 15: gmatch matched but no iterate +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+") + ngx.say("done") + } +--- stream_response +done + + + +=== TEST 16: gmatch matched but only iterate once and still matches remain +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched") + end + } +--- stream_response +hello + + + +=== TEST 17: gmatch matched but no iterate and early forced GC +--- stream_server_config + content_by_lua_block { + local a = {} + for i = 1, 3 do + it = ngx.re.gmatch("hello, world", "[a-z]+") + it() + collectgarbage() + table.insert(a, {"hello", "world"}) + end + ngx.say("done") + } +--- stream_response +done + + + +=== TEST 18: gmatch iterator used by another request +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;;';" +--- stream_server_config + content_by_lua_block { + package.loaded.foo = nil + collectgarbage() + local foo = require "foo" + local m = foo.go() + ngx.say(m and "matched" or "no") + } +--- stream_server_config2 + content_by_lua_block { + local foo = require "foo" + local m = foo.go() + ngx.say(m and "matched" or "no") + } +--- user_files +>>> foo.lua +module("foo", package.seeall) + +local it + +function go() + if not it then + it = ngx.re.gmatch("hello, world", "[a-z]+") + end + + return it() +end +--- stream_response +matched +matched +--- no_error_log +[error] + + + +=== TEST 19: gmatch (empty matched string) +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello", "a|") do + if m then + ngx.say("matched: [", m[0], "]") + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +matched: [] +matched: [] +matched: [] +matched: [] +matched: [] +matched: [] + + + +=== TEST 20: gmatch with named pattern +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("1234, 1234", "(?[0-9]+)") + m = it() + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m["first"]) + else + ngx.say("not matched!") + end + + m = it() + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m["first"]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +1234 +1234 +1234 +1234 +1234 + + + +=== TEST 21: gmatch with multiple named pattern +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("1234, abcd, 1234", "(?[0-9]+)|(?[a-z]+)") + + m = it() + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(m["first"]) + ngx.say(m["second"]) + else + ngx.say("not matched!") + end + + m = it() + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(m["first"]) + ngx.say(m["second"]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +1234 +false +1234 +false +abcd +false +abcd +false +abcd + + + +=== TEST 22: gmatch with duplicate named pattern w/ extraction +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, 1234", "(?[a-z]+), (?[0-9]+)", "D") + m = it() + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(table.concat(m.first,"-")) + else + ngx.say("not matched!") + end + + m = it() + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(table.concat(m.first,"-")) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +1234 +hello-1234 +not matched! + + + +=== TEST 23: named captures are empty +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("1234", "(?[a-z]*)([0-9]+)", "") + local m = it() + if m then + ngx.say(m[0]) + ngx.say(m.first) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + +1234 + + + +=== TEST 24: named captures are empty (with regex cache) +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("1234", "(?[a-z]*)([0-9]+)", "o") + local m = it() + if m then + ngx.say(m[0]) + ngx.say(m.first) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + +1234 + + + +=== TEST 25: bad pattern +--- stream_server_config + content_by_lua_block { + local it, err = ngx.re.gmatch("hello\nworld", "(abc") + if not err then + ngx.say("good") + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 26: bad UTF-8 +--- stream_server_config + content_by_lua_block { + local target = "你好" + local regex = "你好" + + -- Note the D here + local it, err = ngx.re.gmatch(string.sub(target, 1, 4), regex, "u") + + if err then + ngx.say("error: ", err) + return + end + + local m, err = it() + if err then + ngx.say("error: ", err) + return + end + + if m then + ngx.say("matched: ", m[0]) + else + ngx.say("not matched") + end + } +--- stream_response_like chop +error: pcre_exec\(\) failed: -10 + +--- no_error_log +[error] + + + +=== TEST 27: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("你好", ".", "U") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +你 +--- no_error_log +[error] + + + +=== TEST 28: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("你好", ".", "u") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 + +--- stream_response +你 +--- no_error_log +[error] + + + +=== TEST 29: just hit match limit +--- stream_config + lua_regex_match_limit 5000; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local it, err = ngx.re.gmatch(s, re, "o") +if not it then + ngx.say("failed to gen iterator: ", err) + return +end + +local res, err = it() + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not res then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +error: pcre_exec() failed: -8 + + + +=== TEST 30: just not hit match limit +--- stream_config + lua_regex_match_limit 5700; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local it, err = ngx.re.gmatch(s, re, "o") +if not it then + ngx.say("failed to gen iterator: ", err) + return +end + +res, err = it() + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not res then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +failed to match diff --git a/src/deps/src/stream-lua-nginx-module/t/036-sub.t b/src/deps/src/stream-lua-nginx-module/t/036-sub.t new file mode 100644 index 000000000..fa426d3a5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/036-sub.t @@ -0,0 +1,593 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 18); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched but w/o variables +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[a-z]+", "howdy") + ngx.say(s) + ngx.say(n) + } +--- stream_response +howdy, world +1 + + + +=== TEST 2: not matched +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[A-Z]+", "howdy") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, world +0 + + + +=== TEST 3: matched and with variables +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("a b c d", "(b) (c)", "[$0] [$1] [$2] [$3] [$134]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] [] [] d +1 + + + +=== TEST 4: matched and with named variables +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("a b c d", + "(b) (c)", "[$0] [$1] [$2] [$3] [$hello]") + if s then + ngx.say(s, ": ", n) + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +attempt to use named capturing variable "hello" (named captures not supported yet) + + + +=== TEST 5: matched and with named variables (bracketed) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("a b c d", + "(b) (c)", "[$0] [$1] [$2] [$3] [${hello}]") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +attempt to use named capturing variable "hello" (named captures not supported yet) + + + +=== TEST 6: matched and with bracketed variables +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${134}]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[b c] [b] [c] [] [] d +1 + + + +=== TEST 7: matched and with bracketed variables (unmatched brackets) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${134]") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +the closing bracket in "134" variable is missing + + + +=== TEST 8: matched and with bracketed variables (unmatched brackets) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${134") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +the closing bracket in "134" variable is missing + + + +=== TEST 9: matched and with bracketed variables (unmatched brackets) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +lua script: invalid capturing variable name found in "[$0] [$1] [${2}] [$3] [${" + + + +=== TEST 10: trailing $ +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [$") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +lua script: invalid capturing variable name found in "[$0] [$1] [${2}] [$3] [$" + + + +=== TEST 11: matched but w/o variables and with literal $ +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[a-z]+", "ho$$wdy") + ngx.say(s) + ngx.say(n) + } +--- stream_response +ho$wdy, world +1 + + + +=== TEST 12: non-anchored match +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "[0-9]", "x") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, x234 +1 + + + +=== TEST 13: anchored match +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "[0-9]", "x", "a") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, 1234 +0 + + + +=== TEST 14: function replace +--- stream_server_config + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "] [" .. m[1] .. "]" + end + + local s, n = ngx.re.sub("hello, 34", "([0-9])", repl) + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, [3] [3]4 +1 + + + +=== TEST 15: function replace (failed) +--- stream_server_config + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "] [" .. m[1] .. "]" + end + + local s, n = ngx.re.sub("hello, 34", "([A-Z])", repl) + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, 34 +0 + + + +=== TEST 16: bad repl arg type +--- stream_server_config + content_by_lua_block { + local rc, s, n = pcall(ngx.re.sub, "hello, 34", "([A-Z])", true) + ngx.say(rc) + ngx.say(s) + ngx.say(n) + } +--- stream_response +false +bad argument #3 to '?' (string, number, or function expected, got boolean) +nil +--- SKIP + + + +=== TEST 17: use number to replace +--- stream_server_config + content_by_lua_block { + local rc, s, n = pcall(ngx.re.sub, "hello, 34", "([0-9])", 72) + ngx.say(rc) + ngx.say(s) + ngx.say(n) + } +--- stream_response +true +hello, 724 +1 + + + +=== TEST 18: bad function return value type +--- SKIP +--- stream_server_config + content_by_lua_block { + local f = function (m) end + local rc, s, n = pcall(ngx.re.sub, "hello, 34", "([0-9])", f) + ngx.say(rc) + ngx.say(s) + ngx.say(n) + } +--- stream_response +false +bad argument #3 to '?' (string or number expected to be returned by the replace function, got nil) +nil + + + +=== TEST 19: matched and with variables w/o using named patterns in sub +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("a b c d", "(?b) (?c)", "[$0] [$1] [$2] [$3] [$134]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] [] [] d +1 + + + +=== TEST 20: matched and with variables using named patterns in func +--- stream_server_config + error_log /tmp/nginx_error debug; + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "] [" .. m["first"] .. "] [" .. m[2] .. "]" + end + + local s, n = ngx.re.sub("a b c d", "(?b) (?c)", repl) + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] d +1 +--- no_error_log +[error] +[alert] +--- timeout: 5 + + + +=== TEST 21: matched and with variables w/ using named patterns in sub +This is still a TODO +--- SKIP +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("a b c d", "(?b) (?c)", "[$0] [${first}] [${second}] [$3] [$134]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] [] [] d +1 +--- no_error_log +[error] + + + +=== TEST 22: $0 without parens +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("a b c d", [[\w]], "[$0]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[a] b c d +1 +--- no_error_log +[error] + + + +=== TEST 23: bad pattern +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("hello\\nworld", "(abc", "") + if s then + ngx.say("subs: ", n) + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 24: bad UTF-8 +--- stream_server_config + content_by_lua_block { + local target = "你好" + local regex = "你好" + + -- Note the D here + local s, n, err = ngx.re.sub(string.sub(target, 1, 4), regex, "", "u") + + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response_like chop +error: pcre_exec\(\) failed: -10 + +--- no_error_log +[error] + + + +=== TEST 25: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("你好", ".", "a", "U") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +s: a好 +--- no_error_log +[error] + + + +=== TEST 26: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("你好", ".", "a", "u") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 + +--- stream_response +s: a好 +--- no_error_log +[error] + + + +=== TEST 27: just hit match limit +--- stream_config + lua_regex_match_limit 5000; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local res, cnt, err = ngx.re.sub(s, re, "", "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if err then + ngx.say("error: ", err) + return +end +ngx.say("sub: ", cnt) + +--- stream_response +error: pcre_exec() failed: -8 + + + +=== TEST 28: just not hit match limit +--- stream_config + lua_regex_match_limit 5700; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +local s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local res, cnt, err = ngx.re.sub(s, re, "", "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if err then + ngx.say("error: ", err) + return +end +ngx.say("sub: ", cnt) + +--- stream_response +sub: 0 + + + +=== TEST 29: bug: sub incorrectly swallowed a character is the first character +Original bad result: estCase +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("TestCase", "^ *", "", "o") + if s then + ngx.say(s) + end + } +--- stream_response +TestCase + + + +=== TEST 30: bug: sub incorrectly swallowed a character is not the first character +Original bad result: .b.d +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("abcd", "(?=c)", ".") + if s then + ngx.say(s) + end + } +--- stream_response +ab.cd + + + +=== TEST 31: ngx.re.gsub: recursive calling (github #445) +--- stream_server_config + + content_by_lua_block { + function test() + local data = [[ + OUTER {FIRST} +]] + + local p1 = "(OUTER)(.+)" + local p2 = "{([A-Z]+)}" + + ngx.print(data) + + local res = ngx.re.gsub(data, p1, function(m) + -- ngx.say("pre: m[1]: [", m[1], "]") + -- ngx.say("pre: m[2]: [", m[2], "]") + + local res = ngx.re.gsub(m[2], p2, function(_) + return "REPLACED" + end, "") + + -- ngx.say("post: m[1]: [", m[1], "]") + -- ngx.say("post m[2]: [", m[2], "]") + return m[1] .. res + end, "") + + ngx.print(res) + end + + test() +} +--- stream_response + OUTER {FIRST} + OUTER REPLACED +--- no_error_log +[error] +bad argument type +NYI diff --git a/src/deps/src/stream-lua-nginx-module/t/037-gsub.t b/src/deps/src/stream-lua-nginx-module/t/037-gsub.t new file mode 100644 index 000000000..d00b6322a --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/037-gsub.t @@ -0,0 +1,489 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 14); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("[hello, world]", "[a-z]+", "howdy") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[howdy, howdy] +2 + + + +=== TEST 2: trimmed +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[a-z]+", "howdy") + ngx.say(s) + ngx.say(n) + } +--- stream_response +howdy, howdy +2 + + + +=== TEST 3: not matched +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[A-Z]+", "howdy") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, world +0 + + + +=== TEST 4: replace by function (trimmed) +--- stream_server_config + content_by_lua_block { + local f = function (m) + return "[" .. m[0] .. "," .. m[1] .. "]" + end + + local s, n = ngx.re.gsub("hello, world", "([a-z])[a-z]+", f) + ngx.say(s) + ngx.say(n) + } +--- stream_response +[hello,h], [world,w] +2 + + + +=== TEST 5: replace by function (not trimmed) +--- stream_server_config + content_by_lua_block { + local f = function (m) + return "[" .. m[0] .. "," .. m[1] .. "]" + end + + local s, n = ngx.re.gsub("{hello, world}", "([a-z])[a-z]+", f) + ngx.say(s) + ngx.say(n) + } +--- stream_response +{[hello,h], [world,w]} +2 + + + +=== TEST 6: replace by script (trimmed) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "([a-z])[a-z]+", "[$0,$1]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[hello,h], [world,w] +2 + + + +=== TEST 7: replace by script (not trimmed) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("{hello, world}", "([a-z])[a-z]+", "[$0,$1]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +{[hello,h], [world,w]} +2 + + + +=== TEST 8: look-behind assertion +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz", "h$0") + ngx.say(s) + ngx.say(n) + } +--- stream_response +{foohbarhbaz} +2 + + + +=== TEST 9: gsub with a patch matching an empty substring (string template) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello", "a|", "b") + ngx.say("s: ", s) + ngx.say("n: ", n) + } +--- stream_response +s: bhbeblblbob +n: 6 +--- no_error_log +[error] + + + +=== TEST 10: gsub with a patch matching an empty substring (string template, empty subj) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("", "a|", "b") + ngx.say("s: ", s) + ngx.say("n: ", n) + } +--- stream_response +s: b +n: 1 +--- no_error_log +[error] + + + +=== TEST 11: gsub with a patch matching an empty substring (func) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello", "a|", function () return "b" end) + ngx.say("s: ", s) + ngx.say("n: ", n) + } +--- stream_response +s: bhbeblblbob +n: 6 +--- no_error_log +[error] + + + +=== TEST 12: gsub with a patch matching an empty substring (func, empty subj) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("", "a|", function () return "b" end) + ngx.say("s: ", s) + ngx.say("n: ", n) + } +--- stream_response +s: b +n: 1 +--- no_error_log +[error] + + + +=== TEST 13: big subject string exceeding the luabuf chunk size (with trailing unmatched data, func repl) +--- stream_server_config + content_by_lua_block { + local subj = string.rep("a", 8000) + .. string.rep("b", 1000) + .. string.rep("a", 8000) + .. string.rep("b", 1000) + .. "aaa" + + local function repl(m) + return string.rep("c", string.len(m[0])) + end + + local s, n = ngx.re.gsub(subj, "b+", repl) + ngx.say(s) + ngx.say(n) + } +--- stream_response eval +("a" x 8000) . ("c" x 1000) . ("a" x 8000) . ("c" x 1000) +. "aaa +2 +" +--- no_error_log +[error] + + + +=== TEST 14: big subject string exceeding the luabuf chunk size (without trailing unmatched data, func repl) +--- stream_server_config + content_by_lua_block { + local subj = string.rep("a", 8000) + .. string.rep("b", 1000) + .. string.rep("a", 8000) + .. string.rep("b", 1000) + + local function repl(m) + return string.rep("c", string.len(m[0])) + end + + local s, n = ngx.re.gsub(subj, "b+", repl) + ngx.say(s) + ngx.say(n) + } +--- stream_response eval +("a" x 8000) . ("c" x 1000) . ("a" x 8000) . ("c" x 1000) +. "\n2\n" +--- no_error_log +[error] + + + +=== TEST 15: big subject string exceeding the luabuf chunk size (with trailing unmatched data, str repl) +--- stream_server_config + content_by_lua_block { + local subj = string.rep("a", 8000) + .. string.rep("b", 1000) + .. string.rep("a", 8000) + .. string.rep("b", 1000) + .. "aaa" + + local s, n = ngx.re.gsub(subj, "b(b+)(b)", "$1 $2") + ngx.say(s) + ngx.say(n) + } +--- stream_response eval +("a" x 8000) . ("b" x 998) . " b" . ("a" x 8000) . ("b" x 998) . " baaa +2 +" +--- no_error_log +[error] + + + +=== TEST 16: big subject string exceeding the luabuf chunk size (without trailing unmatched data, str repl) +--- stream_server_config + content_by_lua_block { + local subj = string.rep("a", 8000) + .. string.rep("b", 1000) + .. string.rep("a", 8000) + .. string.rep("b", 1000) + + local s, n = ngx.re.gsub(subj, "b(b+)(b)", "$1 $2") + ngx.say(s) + ngx.say(n) + } +--- stream_response eval +("a" x 8000) . ("b" x 998) . " b" . ("a" x 8000) . ("b" x 998) . " b\n2\n" +--- no_error_log +[error] + + + +=== TEST 17: named pattern repl w/ callback +--- stream_server_config + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "," .. m["first"] .. "]" + end + + local s, n = ngx.re.gsub("hello, world", "(?[a-z])[a-z]+", repl) + ngx.say(s) + ngx.say(n) + } +--- stream_response +[hello,h], [world,w] +2 + + + +=== TEST 18: $0 without parens +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("a b c d", [[\w]], "[$0]") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[a] [b] [c] [d] +4 +--- no_error_log +[error] + + + +=== TEST 19: bad UTF-8 +--- stream_server_config + content_by_lua_block { + local target = "你好" + local regex = "你好" + + -- Note the D here + local s, n, err = ngx.re.gsub(string.sub(target, 1, 4), regex, "", "u") + + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response_like chop +error: pcre_exec\(\) failed: -10 + +--- no_error_log +[error] + + + +=== TEST 20: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("你好", ".", "a", "U") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 +exec opts: 2000 +exec opts: 2000 + +--- stream_response +s: aa +--- no_error_log +[error] + + + +=== TEST 21: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("你好", ".", "a", "u") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 +exec opts: 0 +exec opts: 0 + +--- stream_response +s: aa +--- no_error_log +[error] + + + +=== TEST 22: just hit match limit +--- stream_config + lua_regex_match_limit 5000; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local res, cnt, err = ngx.re.gsub(s, re, "", "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if err then + ngx.say("error: ", err) + return +end +ngx.say("gsub: ", cnt) + +--- stream_response +error: pcre_exec() failed: -8 + + + +=== TEST 23: just not hit match limit +--- stream_config + lua_regex_match_limit 5700; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +local s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local res, cnt, err = ngx.re.gsub(s, re, "", "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if err then + ngx.say("error: ", err) + return +end +ngx.say("gsub: ", cnt) + +--- stream_response +gsub: 0 +--- timeout: 10 + + + +=== TEST 24: bug: gsub incorrectly swallowed a character is the first character +Original bad result: estCase +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("TestCase", "^ *", "", "o") + if s then + ngx.say(s) + end + } +--- stream_response +TestCase + + + +=== TEST 25: bug: gsub incorrectly swallowed a character is not the first character +Original bad result: .b.d +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("abcd", "a|(?=c)", ".") + if s then + ngx.say(s) + end + } +--- stream_response +.b.cd diff --git a/src/deps/src/stream-lua-nginx-module/t/038-match-o.t b/src/deps/src/stream-lua-nginx-module/t/038-match-o.t new file mode 100644 index 000000000..e5b567ebb --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/038-match-o.t @@ -0,0 +1,560 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 1); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)", "o") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + + +=== TEST 2: escaping sequences +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "(\\d+)", "o") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + + +=== TEST 3: single capture +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]{2})[0-9]+", "o") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +12 + + + +=== TEST 4: multiple captures +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([a-z]+).*?([0-9]{2})[0-9]+", "o") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +12 + + + +=== TEST 5: not matched +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "foo", "o") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +not matched: nil + + + +=== TEST 6: case sensitive by default +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "HELLO", "o") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +not matched: nil + + + +=== TEST 7: case sensitive by default +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "HELLO", "oi") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello + + + +=== TEST 8: UTF-8 mode +--- stream_server_config + content_by_lua_block { + local rc, m = pcall(ngx.re.match, "hello章亦春", "HELLO.{2}", "iou") + if not rc then + ngx.say("error: ", m) + return + end + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response_like chop +this version of PCRE is not compiled with PCRE_UTF8 support|^hello章亦$ + + + +=== TEST 9: multi-line mode (^ at line head) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", "^world", "mo") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +world + + + +=== TEST 10: multi-line mode (. does not match \n) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", ".*", "om") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello + + + +=== TEST 11: single-line mode (^ as normal) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", "^world", "so") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +not matched: nil + + + +=== TEST 12: single-line mode (dot all) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", ".*", "os") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello +world + + + +=== TEST 13: extended mode (ignore whitespaces) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello\nworld", [[\w \w]], "xo") + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +he + + + +=== TEST 14: bad pattern +--- stream_server_config + content_by_lua_block { + local m, err = ngx.re.match("hello\\nworld", "(abc", "o") + if m then + ngx.say(m[0]) + + else + if err then + ngx.say("error: ", err) + + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 15: bad option +--- stream_server_config + content_by_lua_block { + rc, m = pcall(ngx.re.match, "hello\\nworld", ".*", "Ho") + if rc then + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + else + ngx.say("error: ", m) + end + } +--- stream_response_like chop +^error: .*?unknown flag "H" + + + +=== TEST 16: extended mode (ignore whitespaces) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, world", "(world)|(hello)", "xo") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched: ", m) + end + } +--- stream_response +hello +false +hello + + + +=== TEST 17: optional trailing captures +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)(h?)", "o") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response eval +"1234 +1234 + +" + + + +=== TEST 18: anchored match (failed) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)", "oa") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +not matched! + + + +=== TEST 19: anchored match (succeeded) +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("1234, hello", "([0-9]+)", "ao") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + + +=== TEST 20: match with ctx but no pos +--- stream_server_config + content_by_lua_block { + local ctx = {} + m = ngx.re.match("1234, hello", "([0-9]+)", "o", ctx) + if m then + ngx.say(m[0]) + ngx.say(ctx.pos) + else + ngx.say("not matched!") + ngx.say(ctx.pos) + end + } +--- stream_response +1234 +5 + + + +=== TEST 21: match with ctx and a pos +--- stream_server_config + content_by_lua_block { + local ctx = { pos = 3 } + m = ngx.re.match("1234, hello", "([0-9]+)", "o", ctx) + if m then + ngx.say(m[0]) + ngx.say(ctx.pos) + else + ngx.say("not matched!") + ngx.say(ctx.pos) + end + } +--- stream_response +34 +5 + + + +=== TEST 22: match (look-behind assertion) +--- stream_server_config + content_by_lua_block { + local ctx = {} + local m = ngx.re.match("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz", "o", ctx) + ngx.say(m and m[0]) + + m = ngx.re.match("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz", "o", ctx) + ngx.say(m and m[0]) + } +--- stream_response +bar +baz + + + +=== TEST 23: match (with regex cache) +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "([A-Z]+)", "io") + ngx.say(m and m[0]) + + m = ngx.re.match("1234, okay", "([A-Z]+)", "io") + ngx.say(m and m[0]) + + m = ngx.re.match("hello, 1234", "([A-Z]+)", "o") + ngx.say(m and m[0]) + } +--- stream_response +hello +okay +nil + + + +=== TEST 24: match (with regex cache and ctx) +--- stream_server_config + content_by_lua_block { + local ctx = {} + local m = ngx.re.match("hello, 1234", "([A-Z]+)", "io", ctx) + ngx.say(m and m[0]) + ngx.say(ctx.pos) + + m = ngx.re.match("1234, okay", "([A-Z]+)", "io", ctx) + ngx.say(m and m[0]) + ngx.say(ctx.pos) + + ctx.pos = 1 + m = ngx.re.match("hi, 1234", "([A-Z]+)", "o", ctx) + ngx.say(m and m[0]) + ngx.say(ctx.pos) + } +--- stream_response +hello +6 +okay +11 +nil +1 + + + +=== TEST 25: exceeding regex cache max entries +--- stream_config + lua_regex_cache_max_entries 2; +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "([0-9]+)", "o") + ngx.say(m and m[0]) + + m = ngx.re.match("howdy, 567", "([0-9]+)", "oi") + ngx.say(m and m[0]) + + m = ngx.re.match("hiya, 98", "([0-9]+)", "ox") + ngx.say(m and m[0]) + } +--- stream_response +1234 +567 +98 + + + +=== TEST 26: disable regex cache completely +--- stream_config + lua_regex_cache_max_entries 0; +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "([0-9]+)", "o") + ngx.say(m and m[0]) + + m = ngx.re.match("howdy, 567", "([0-9]+)", "oi") + ngx.say(m and m[0]) + + m = ngx.re.match("hiya, 98", "([0-9]+)", "ox") + ngx.say(m and m[0]) + } +--- stream_response +1234 +567 +98 + + + +=== TEST 27: named subpatterns w/ extraction +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "(?[a-z]+), [0-9]+", "o") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m.first) + ngx.say(m.second) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +hello +nil + + + +=== TEST 28: duplicate named subpatterns w/ extraction +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, 1234", "(?[a-z]+), (?[0-9]+)", "Do") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(table.concat(m.first,"-")) + else + ngx.say("not matched!") + end + } +--- stream_response +hello, 1234 +hello +1234 +hello-1234 + + + +=== TEST 29: named captures are empty strings +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("1234", "(?[a-z]*)([0-9]+)", "o") + if m then + ngx.say(m[0]) + ngx.say(m.first) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + + +1234 + + + +=== TEST 30: named captures are nil +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("hello, world", "(world)|(hello)|(?howdy)", "o") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(m[3]) + ngx.say(m["named"]) + else + ngx.say("not matched!") + end + } +--- stream_response +hello +false +hello +false +false diff --git a/src/deps/src/stream-lua-nginx-module/t/039-sub-o.t b/src/deps/src/stream-lua-nginx-module/t/039-sub-o.t new file mode 100644 index 000000000..69003f1f0 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/039-sub-o.t @@ -0,0 +1,464 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 6); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched but w/o variables +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[a-z]+", "howdy", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +howdy, world +1 + + + +=== TEST 2: not matched +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[A-Z]+", "howdy", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, world +0 + + + +=== TEST 3: matched and with variables +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("a b c d", "(b) (c)", "[$0] [$1] [$2] [$3] [$134]", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] [] [] d +1 + + + +=== TEST 4: matched and with named variables (bad template) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("a b c d", + "(b) (c)", + "[$0] [$1] [$2] [$3] [$hello]", + "o") + if s then + ngx.say(s, ": ", n) + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +attempt to use named capturing variable "hello" (named captures not supported yet) + + + +=== TEST 5: matched and with named variables (bracketed) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("a b c d", + "(b) (c)", + "[$0] [$1] [$2] [$3] [${hello}]", + "o") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +attempt to use named capturing variable "hello" (named captures not supported yet) + + + +=== TEST 6: matched and with bracketed variables +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${134}]", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[b c] [b] [c] [] [] d +1 + + + +=== TEST 7: matched and with bracketed variables (unmatched brackets) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${134]", "o") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +the closing bracket in "134" variable is missing + + + +=== TEST 8: matched and with bracketed variables (unmatched brackets) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${134", "o") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +the closing bracket in "134" variable is missing + + + +=== TEST 9: matched and with bracketed variables (unmatched brackets) +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [${", "o") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +lua script: invalid capturing variable name found in "[$0] [$1] [${2}] [$3] [${" + + + +=== TEST 10: trailing $ +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("b c d", "(b) (c)", "[$0] [$1] [${2}] [$3] [$", "o") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: failed to compile the replacement template +--- error_log +lua script: invalid capturing variable name found in "[$0] [$1] [${2}] [$3] [$" + + + +=== TEST 11: matched but w/o variables and with literal $ +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[a-z]+", "ho$$wdy", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +ho$wdy, world +1 + + + +=== TEST 12: non-anchored match +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", " [0-9] ", "x", "xo") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, x234 +1 + + + +=== TEST 13: anchored match +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "[0-9]", "x", "ao") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, 1234 +0 + + + +=== TEST 14: function replace +--- stream_server_config + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "] [" .. m[1] .. "]" + end + + local s, n = ngx.re.sub("hello, 34", "([0-9])", repl, "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, [3] [3]4 +1 + + + +=== TEST 15: function replace (failed) +--- stream_server_config + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "] [" .. m[1] .. "]" + end + + local s, n = ngx.re.sub("hello, 34", "([A-Z])", repl, "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, 34 +0 + + + +=== TEST 16: bad repl arg type +--- SKIP +--- stream_server_config + content_by_lua_block { + local rc, s, n = pcall(ngx.re.sub, "hello, 34", "([A-Z])", true, "o") + ngx.say(rc) + ngx.say(s) + ngx.say(n) + } +--- stream_response +false +bad argument #3 to '?' (string, number, or function expected, got boolean) +nil + + + +=== TEST 17: use number to replace +--- stream_server_config + content_by_lua_block { + local rc, s, n = pcall(ngx.re.sub, "hello, 34", "([0-9])", 72, "o") + ngx.say(rc) + ngx.say(s) + ngx.say(n) + } +--- stream_response +true +hello, 724 +1 + + + +=== TEST 18: bad function return value type +--- SKIP +--- stream_server_config + content_by_lua_block { + local f = function (m) end + local rc, s, n = pcall(ngx.re.sub, "hello, 34", "([0-9])", f, "o") + ngx.say(rc) + ngx.say(s) + ngx.say(n) + } +--- stream_response +false +bad argument #3 to '?' (string or number expected to be returned by the replace function, got nil) +nil + + + +=== TEST 19: with regex cache (with text replace) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "([A-Z]+)", "baz", "io") + ngx.say(s) + ngx.say(n) + + local s, n = ngx.re.sub("howdy, 1234", "([A-Z]+)", "baz", "io") + ngx.say(s) + ngx.say(n) + + + s, n = ngx.re.sub("1234, okay", "([A-Z]+)", "blah", "io") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("hi, 1234", "([A-Z]+)", "hello", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +baz, 1234 +1 +baz, 1234 +1 +1234, blah +1 +hi, 1234 +0 + + + +=== TEST 20: with regex cache (with func replace) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "([A-Z]+)", "baz", "io") + ngx.say(s) + ngx.say(n) + + local s, n = ngx.re.sub("howdy, 1234", "([A-Z]+)", function () return "bah" end, "io") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("1234, okay", "([A-Z]+)", function () return "blah" end, "io") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("hi, 1234", "([A-Z]+)", "hello", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +baz, 1234 +1 +bah, 1234 +1 +1234, blah +1 +hi, 1234 +0 + + + +=== TEST 21: exceeding regex cache max entries +--- stream_config + lua_regex_cache_max_entries 2; +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "([0-9]+)", "hello", "o") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("howdy, 567", "([0-9]+)", "hello", "oi") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("hiya, 98", "([0-9]+)", "hello", "ox") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, hello +1 +howdy, hello +1 +hiya, hello +1 + + + +=== TEST 22: disable regex cache completely +--- stream_config + lua_regex_cache_max_entries 0; +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "([0-9]+)", "hello", "o") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("howdy, 567", "([0-9]+)", "hello", "oi") + ngx.say(s) + ngx.say(n) + + s, n = ngx.re.sub("hiya, 98", "([0-9]+)", "hello", "ox") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, hello +1 +howdy, hello +1 +hiya, hello +1 + + + +=== TEST 23: empty replace +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234", "([0-9]+)", "", "o") + ngx.say(s) + ngx.say(n) + + local s, n = ngx.re.sub("hi, 5432", "([0-9]+)", "", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, +1 +hi, +1 + + + +=== TEST 24: matched and with variables w/o using named patterns in sub +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("a b c d", "(?b) (?c)", "[$0] [$1] [$2] [$3] [$134]", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] [] [] d +1 + + + +=== TEST 25: matched and with variables using named patterns in func +--- stream_server_config + error_log /tmp/nginx_error debug; + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "] [" .. m["first"] .. "] [" .. m[2] .. "]" + end + + local s, n = ngx.re.sub("a b c d", "(?b) (?c)", repl, "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +a [b c] [b] [c] d +1 diff --git a/src/deps/src/stream-lua-nginx-module/t/040-gsub-o.t b/src/deps/src/stream-lua-nginx-module/t/040-gsub-o.t new file mode 100644 index 000000000..cd20727b7 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/040-gsub-o.t @@ -0,0 +1,144 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("[hello, world]", "[a-z]+", "howdy", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[howdy, howdy] +2 + + + +=== TEST 2: trimmed +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[a-z]+", "howdy", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +howdy, howdy +2 + + + +=== TEST 3: not matched +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[A-Z]+", "howdy", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +hello, world +0 + + + +=== TEST 4: replace by function (trimmed) +--- stream_server_config + content_by_lua_block { + local f = function (m) + return "[" .. m[0] .. "," .. m[1] .. "]" + end + + local s, n = ngx.re.gsub("hello, world", "([a-z])[a-z]+", f, "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[hello,h], [world,w] +2 + + + +=== TEST 5: replace by function (not trimmed) +--- stream_server_config + content_by_lua_block { + local f = function (m) + return "[" .. m[0] .. "," .. m[1] .. "]" + end + + local s, n = ngx.re.gsub("{hello, world}", "([a-z])[a-z]+", f, "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +{[hello,h], [world,w]} +2 + + + +=== TEST 6: replace by script (trimmed) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "([a-z])[a-z]+", "[$0,$1]", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[hello,h], [world,w] +2 + + + +=== TEST 7: replace by script (not trimmed) +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("{hello, world}", "([a-z])[a-z]+", "[$0,$1]", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +{[hello,h], [world,w]} +2 + + + +=== TEST 8: look-behind assertion +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz", "h$0", "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +{foohbarhbaz} +2 + + + +=== TEST 9: named pattern repl w/ callback +--- stream_server_config + content_by_lua_block { + local repl = function (m) + return "[" .. m[0] .. "," .. m["first"] .. "]" + end + + local s, n = ngx.re.gsub("hello, world", "(?[a-z])[a-z]+", repl, "o") + ngx.say(s) + ngx.say(n) + } +--- stream_response +[hello,h], [world,w] +2 diff --git a/src/deps/src/stream-lua-nginx-module/t/042-crc32.t b/src/deps/src/stream-lua-nginx-module/t/042-crc32.t new file mode 100644 index 000000000..24ac59a91 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/042-crc32.t @@ -0,0 +1,44 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_process_enabled(1); +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: short sanity +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.crc32_short("hello, world")) + } +--- stream_response +4289425978 + + + +=== TEST 2: long sanity +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.crc32_long("hello, world")) + } +--- stream_response +4289425978 + + + +=== TEST 3: long sanity (empty) +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.crc32_long("")) + } +--- stream_response +0 diff --git a/src/deps/src/stream-lua-nginx-module/t/043-shdict.t b/src/deps/src/stream-lua-nginx-module/t/043-shdict.t new file mode 100644 index 000000000..beac136db --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/043-shdict.t @@ -0,0 +1,2038 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +#repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 6); + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +run_tests(); + +__DATA__ + +=== TEST 1: string key, int value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + dogs:set("bah", 10502) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + val = dogs:get("bah") + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +10502 number +--- no_error_log +[error] + + + +=== TEST 2: string key, floating-point value +--- stream_config + lua_shared_dict cats 1m; +--- stream_server_config + content_by_lua_block { + local cats = ngx.shared.cats + cats:set("foo", 3.14159) + cats:set("baz", 1.28) + cats:set("baz", 3.96) + local val = cats:get("foo") + ngx.say(val, " ", type(val)) + val = cats:get("baz") + ngx.say(val, " ", type(val)) + } +--- stream_response +3.14159 number +3.96 number +--- no_error_log +[error] + + + +=== TEST 3: string key, boolean value +--- stream_config + lua_shared_dict cats 1m; +--- stream_server_config + content_by_lua_block { + local cats = ngx.shared.cats + cats:set("foo", true) + cats:set("bar", false) + local val = cats:get("foo") + ngx.say(val, " ", type(val)) + val = cats:get("bar") + ngx.say(val, " ", type(val)) + } +--- stream_response +true boolean +false boolean +--- no_error_log +[error] + + + +=== TEST 4: number keys, string values +--- stream_config + lua_shared_dict cats 1m; +--- stream_server_config + content_by_lua_block { + local cats = ngx.shared.cats + ngx.say(cats:set(1234, "cat")) + ngx.say(cats:set("1234", "dog")) + ngx.say(cats:set(256, "bird")) + ngx.say(cats:get(1234)) + ngx.say(cats:get("1234")) + local val = cats:get("256") + ngx.say(val, " ", type(val)) + } +--- stream_response +truenilfalse +truenilfalse +truenilfalse +dog +dog +bird string +--- no_error_log +[error] + + + +=== TEST 5: different-size values set to the same key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "hello") + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello") + ngx.say(dogs:get("foo")) + } +--- stream_response +hello +hello, world +hello +--- no_error_log +[error] + + + +=== TEST 6: expired entries (can be auto-removed by get) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0.01) + ngx.sleep(0.01) + ngx.say(dogs:get("foo")) + } +--- stream_response +nil +--- no_error_log +[error] + + + +=== TEST 7: expired entries (can NOT be auto-removed by get) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bar", 56, 0.001) + dogs:set("baz", 78, 0.001) + dogs:set("foo", 32, 0.01) + ngx.sleep(0.012) + ngx.say(dogs:get("foo")) + } +--- stream_response +nil +--- no_error_log +[error] + + + +=== TEST 8: not yet expired entries +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0.5) + ngx.sleep(0.01) + ngx.say(dogs:get("foo")) + } +--- stream_response +32 +--- no_error_log +[error] + + + +=== TEST 9: forcibly override other valid entries +--- stream_config + lua_shared_dict dogs 100k; +--- stream_server_config + content_by_lua_file html/a.lua; +--- stream_server_config2 + content_by_lua_file html/a.lua; +--- user_files +>>> a.lua +local dogs = ngx.shared.dogs +local i = 0 +while i < 1000 do + i = i + 1 + local val = string.rep(" hello", 10) .. i + local res, err, forcible = dogs:set("key_" .. i, val) + if not res or forcible then + ngx.say(res, " ", err, " ", forcible) + break + end +end +ngx.say("abort at ", i) +ngx.say("cur value: ", dogs:get("key_" .. i)) +if i > 1 then + ngx.say("1st value: ", dogs:get("key_1")) +end +if i > 2 then + ngx.say("2nd value: ", dogs:get("key_2")) +end + +--- stream_response eval +my $a = "true nil true\nabort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: nil\n2nd value: " . (" hello" x 10) . "2\n"; +[qr/$a/, +"true nil true\nabort at 1\ncur value: " . (" hello" x 10) . "1\n" +] +--- no_error_log +[error] + + + +=== TEST 10: forcibly override other valid entries and test LRU +--- stream_config + lua_shared_dict dogs 100k; +--- stream_server_config + content_by_lua_file html/a.lua; +--- stream_server_config2 + content_by_lua_file html/a.lua; +--- user_files +>>> a.lua +local dogs = ngx.shared.dogs +local i = 0 +while i < 1000 do + i = i + 1 + local val = string.rep(" hello", 10) .. i + if i == 10 then + dogs:get("key_1") + end + local res, err, forcible = dogs:set("key_" .. i, val) + if not res or forcible then + ngx.say(res, " ", err, " ", forcible) + break + end +end +ngx.say("abort at ", i) +ngx.say("cur value: ", dogs:get("key_" .. i)) +if i > 1 then + ngx.say("1st value: ", dogs:get("key_1")) +end +if i > 2 then + ngx.say("2nd value: ", dogs:get("key_2")) +end +--- stream_response eval +my $a = "true nil true\nabort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: " . (" hello" x 10) . "1\n2nd value: nil\n"; +[qr/$a/, +"true nil true\nabort at 2\ncur value: " . (" hello" x 10) . "2\n1st value: " . (" hello" x 10) . "1\n" +] +--- no_error_log +[error] + + + +=== TEST 11: dogs and cats dicts +--- stream_config + lua_shared_dict dogs 1m; + lua_shared_dict cats 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local cats = ngx.shared.cats + dogs:set("foo", 32) + cats:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + ngx.say(cats:get("foo")) + dogs:set("foo", 56) + ngx.say(dogs:get("foo")) + ngx.say(cats:get("foo")) + } +--- stream_response +32 +hello, world +56 +hello, world +--- no_error_log +[error] + + + +=== TEST 12: get non-existent keys +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + ngx.say(dogs:get("foo")) + ngx.say(dogs:get("foo")) + } +--- stream_response +nil +nil +--- no_error_log +[error] + + + +=== TEST 13: not feed the object into the call +--- SKIP +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local rc, err = pcall(dogs.set, "foo", 3, 0.01) + ngx.say(rc, " ", err) + rc, err = pcall(dogs.set, "foo", 3) + ngx.say(rc, " ", err) + rc, err = pcall(dogs.get, "foo") + ngx.say(rc, " ", err) + } +--- stream_response +false bad argument #1 to '?' (userdata expected, got string) +false expecting 3, 4 or 5 arguments, but only seen 2 +false expecting exactly two arguments, but only seen 1 +--- no_error_log +[error] + + + +=== TEST 14: too big value +--- stream_config + lua_shared_dict dogs 50k; +--- stream_server_config + content_by_lua_block { + collectgarbage("collect") + local dogs = ngx.shared.dogs + local res, err, forcible = dogs:set("foo", string.rep("helloworld", 10000)) + ngx.say(res, " ", err, " ", forcible) + } +--- stream_response +false no memory false +--- log_level: info +--- no_error_log +[error] +[crit] +ngx_slab_alloc() failed: no memory in lua_shared_dict zone + + + +=== TEST 15: set too large key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local key = string.rep("a", 65535) + local rc, err = dogs:set(key, "hello") + ngx.say(rc, " ", err) + ngx.say(dogs:get(key)) + + key = string.rep("a", 65536) + ok, err = dogs:set(key, "world") + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + + } +--- stream_response +true nil +hello +not ok: key too long +--- no_error_log +[error] + + + +=== TEST 16: bad value type +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("foo", dogs) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: bad value type +--- no_error_log +[error] + + + +=== TEST 17: delete after setting values +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + ngx.say(dogs:get("foo")) + dogs:delete("foo") + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + } +--- stream_response +32 +nil +hello, world +--- no_error_log +[error] + + + +=== TEST 18: delete at first +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:delete("foo") + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + } +--- stream_response +nil +hello, world +--- no_error_log +[error] + + + +=== TEST 19: set nil after setting values +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + ngx.say(dogs:get("foo")) + dogs:set("foo", nil) + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + } +--- stream_response +32 +nil +hello, world +--- no_error_log +[error] + + + +=== TEST 20: set nil at first +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", nil) + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + } +--- stream_response +nil +hello, world +--- no_error_log +[error] + + + +=== TEST 21: fail to allocate memory +--- stream_config + lua_shared_dict dogs 100k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local i = 0 + while i < 1000 do + i = i + 1 + local val = string.rep("hello", i ) + local res, err, forcible = dogs:set("key_" .. i, val) + if not res or forcible then + ngx.say(res, " ", err, " ", forcible) + break + end + end + ngx.say("abort at ", i) + } +--- stream_response_like +^true nil true\nabort at (?:139|140|141)$ +--- no_error_log +[error] + + + +=== TEST 22: too big value (forcible) +--- stream_config + lua_shared_dict dogs 50k; +--- stream_server_config + content_by_lua_block { + collectgarbage("collect") + local dogs = ngx.shared.dogs + dogs:set("bah", "hello") + local res, err, forcible = dogs:set("foo", string.rep("helloworld", 10000)) + ngx.say(res, " ", err, " ", forcible) + } +--- stream_response +false no memory true +--- log_level: info +--- no_error_log +[error] +[crit] +ngx_slab_alloc() failed: no memory in lua_shared_dict zone + + + +=== TEST 23: add key (key exists) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err, forcible = dogs:add("foo", 10502) + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +add: false exists false +foo = 32 +--- no_error_log +[error] + + + +=== TEST 24: add key (key not exists) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bah", 32) + local res, err, forcible = dogs:add("foo", 10502) + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +add: true nil false +foo = 10502 +--- no_error_log +[error] + + + +=== TEST 25: add key (key expired) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bar", 32, 0.001) + dogs:set("baz", 32, 0.001) + dogs:set("foo", 32, 0.001) + ngx.sleep(0.002) + local res, err, forcible = dogs:add("foo", 10502) + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +add: true nil false +foo = 10502 +--- no_error_log +[error] + + + +=== TEST 26: add key (key expired and value size unmatched) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bar", 32, 0.001) + dogs:set("baz", 32, 0.001) + dogs:set("foo", "hi", 0.001) + ngx.sleep(0.002) + local res, err, forcible = dogs:add("foo", "hello") + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +add: true nil false +foo = hello +--- no_error_log +[error] + + + +=== TEST 27: incr key (key exists) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + + local res, err, forcible = dogs:replace("foo", "hello") + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + + } +--- stream_response +replace: true nil false +foo = 10502 +replace: true nil false +foo = hello +--- no_error_log +[error] + + + +=== TEST 28: replace key (key not exists) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bah", 32) + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +replace: false not found false +foo = nil +--- no_error_log +[error] + + + +=== TEST 29: replace key (key expired) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bar", 3, 0.001) + dogs:set("baz", 2, 0.001) + dogs:set("foo", 32, 0.001) + ngx.sleep(0.002) + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +replace: false not found false +foo = nil +--- no_error_log +[error] + + + +=== TEST 30: replace key (key expired and value size unmatched) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bar", 32, 0.001) + dogs:set("baz", 32, 0.001) + dogs:set("foo", "hi", 0.001) + ngx.sleep(0.002) + local rc, err, forcible = dogs:replace("foo", "hello") + ngx.say("replace: ", rc, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +replace: false not found false +foo = nil +--- no_error_log +[error] + + + +=== TEST 31: incr key (key exists) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", 10502) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: 10534 nil +foo = 10534 +--- no_error_log +[error] + + + +=== TEST 32: replace key (key not exists) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bah", 32) + local res, err = dogs:incr("foo", 2) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: nil not found +foo = nil +--- no_error_log +[error] + + + +=== TEST 33: replace key (key expired) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("bar", 3, 0.001) + dogs:set("baz", 2, 0.001) + dogs:set("foo", 32, 0.001) + ngx.sleep(0.002) + local res, err = dogs:incr("foo", 10502) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: nil not found +foo = nil +--- no_error_log +[error] + + + +=== TEST 34: incr key (incr by 0) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", 0) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: 32 nil +foo = 32 +--- no_error_log +[error] + + + +=== TEST 35: incr key (incr by floating point number) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", 0.14) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: 32.14 nil +foo = 32.14 +--- no_error_log +[error] + + + +=== TEST 36: incr key (incr by negative numbers) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", -0.14) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: 31.86 nil +foo = 31.86 +--- no_error_log +[error] + + + +=== TEST 37: incr key (original value is not number) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", true) + local res, err = dogs:incr("foo", -0.14) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +incr: nil not a number +foo = true +--- no_error_log +[error] + + + +=== TEST 38: get and set with flags +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 199) + dogs:set("bah", 10502, 202) + local val, flags = dogs:get("foo") + ngx.say(val, " ", type(val)) + ngx.say(flags, " ", type(flags)) + val, flags = dogs:get("bah") + ngx.say(val, " ", type(val)) + ngx.say(flags, " ", type(flags)) + } +--- stream_response +32 number +199 number +10502 number +nil nil +--- no_error_log +[error] + + + +=== TEST 39: expired entries (can be auto-removed by get), with flags set +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0.01, 255) + ngx.sleep(0.01) + local res, flags = dogs:get("foo") + ngx.say("res = ", res, ", flags = ", flags) + } +--- stream_response +res = nil, flags = nil +--- no_error_log +[error] + + + +=== TEST 40: flush_all +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + dogs:set("bah", 10502) + + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + val = dogs:get("bah") + ngx.say(val, " ", type(val)) + + dogs:flush_all() + + val = dogs:get("foo") + ngx.say(val, " ", type(val)) + val = dogs:get("bah") + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +10502 number +nil nil +nil nil +--- no_error_log +[error] + + + +=== TEST 41: flush_expires +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "x", 1) + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 100) + + ngx.sleep(1.5) + + local num = dogs:flush_expired() + ngx.say(num) + } +--- stream_response +1 +--- no_error_log +[error] + + + +=== TEST 42: flush_expires with number +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + for i=1,100 do + dogs:set(tostring(i), "x", 1) + end + + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 100) + + ngx.sleep(1.5) + + local num = dogs:flush_expired(42) + ngx.say(num) + } +--- stream_response +42 +--- no_error_log +[error] + + + +=== TEST 43: flush_expires an empty dict +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local num = dogs:flush_expired() + ngx.say(num) + } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 44: flush_expires a dict without expired items +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 100) + + local num = dogs:flush_expired() + ngx.say(num) + } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 45: list all keys in a shdict +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 0) + local keys = dogs:get_keys() + ngx.say(#keys) + table.sort(keys) + for _,k in ipairs(keys) do + ngx.say(k) + end + } +--- stream_response +2 +bah +bar +--- no_error_log +[error] + + + +=== TEST 46: list keys in a shdict with limit +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 0) + local keys = dogs:get_keys(1) + ngx.say(#keys) + } +--- stream_response +1 +--- no_error_log +[error] + + + +=== TEST 47: list all keys in a shdict with expires +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "x", 1) + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 100) + + ngx.sleep(1.5) + + local keys = dogs:get_keys() + ngx.say(#keys) + } +--- stream_response +2 +--- no_error_log +[error] + + + +=== TEST 48: list keys in a shdict with limit larger than number of keys +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + dogs:set("bah", "y", 0) + dogs:set("bar", "z", 0) + local keys = dogs:get_keys(3) + ngx.say(#keys) + } +--- stream_response +2 +--- no_error_log +[error] + + + +=== TEST 49: list keys in an empty shdict +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local keys = dogs:get_keys() + ngx.say(#keys) + } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 50: list keys in an empty shdict with a limit +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local keys = dogs:get_keys(4) + ngx.say(#keys) + } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 51: list all keys in a shdict with all keys expired +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "x", 1) + dogs:set("bah", "y", 1) + dogs:set("bar", "z", 1) + + ngx.sleep(1.5) + + local keys = dogs:get_keys() + ngx.say(#keys) + } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 52: list all keys in a shdict with more than 1024 keys with no limit set +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + for i=1,2048 do + dogs:set(tostring(i), i) + end + local keys = dogs:get_keys() + ngx.say(#keys) + } +--- stream_response +1024 +--- no_error_log +[error] + + + +=== TEST 53: list all keys in a shdict with more than 1024 keys with 0 limit set +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + for i=1,2048 do + dogs:set(tostring(i), i) + end + local keys = dogs:get_keys(0) + ngx.say(#keys) + } +--- stream_response +2048 +--- no_error_log +[error] + + + +=== TEST 54: safe_set +--- stream_config + lua_shared_dict dogs 100k; +--- stream_server_config + content_by_lua_file html/a.lua; +--- stream_server_config2 + content_by_lua_file html/a.lua; +--- user_files +>>> a.lua +local dogs = ngx.shared.dogs +local i = 0 +while i < 1000 do + i = i + 1 + local val = string.rep(" hello", 10) .. i + local res, err = dogs:safe_set("key_" .. i, val) + if not res then + ngx.say(res, " ", err) + break + end +end +ngx.say("abort at ", i) +ngx.say("cur value: ", dogs:get("key_" .. i)) +if i > 1 then + ngx.say("1st value: ", dogs:get("key_1")) +end +if i > 2 then + ngx.say("2nd value: ", dogs:get("key_2")) +end +--- stream_response eval +my $a = "false no memory\nabort at (353|705)\ncur value: nil\n1st value: " . (" hello" x 10) . "1\n2nd value: " . (" hello" x 10) . "2\n"; +[qr/$a/, qr/$a/] +--- no_error_log +[error] + + + +=== TEST 55: safe_add +--- stream_config + lua_shared_dict dogs 100k; +--- stream_server_config + content_by_lua_file html/a.lua; +--- stream_server_config2 + content_by_lua_file html/a.lua; +--- user_files +>>> a.lua +local dogs = ngx.shared.dogs +local i = 0 +while i < 1000 do + i = i + 1 + local val = string.rep(" hello", 10) .. i + local res, err = dogs:safe_add("key_" .. i, val) + if not res then + ngx.say(res, " ", err) + break + end +end +ngx.say("abort at ", i) +ngx.say("cur value: ", dogs:get("key_" .. i)) +if i > 1 then + ngx.say("1st value: ", dogs:get("key_1")) +end +if i > 2 then + ngx.say("2nd value: ", dogs:get("key_2")) +end +--- stream_response eval +my $a = "false no memory\nabort at (353|705)\ncur value: nil\n1st value: " . (" hello" x 10) . "1\n2nd value: " . (" hello" x 10) . "2\n"; +[qr/$a/, +q{false exists +abort at 1 +cur value: hello hello hello hello hello hello hello hello hello hello1 +} +] +--- no_error_log +[error] + + + +=== TEST 56: get_stale: expired entries can still be fetched +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0.01) + dogs:set("blah", 33, 0.1) + ngx.sleep(0.02) + local val, flags, stale = dogs:get_stale("foo") + ngx.say(val, ", ", flags, ", ", stale) + local val, flags, stale = dogs:get_stale("blah") + ngx.say(val, ", ", flags, ", ", stale) + } +--- stream_response +32, nil, true +33, nil, false +--- no_error_log +[error] + + + +=== TEST 57: set nil key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set(nil, 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: nil key +--- no_error_log +[error] + + + +=== TEST 58: set bad zone argument +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs.set(nil, "foo", 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +--- error_log +bad "zone" argument + + + +=== TEST 59: set empty string keys +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("", 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: empty key +--- no_error_log +[error] + + + +=== TEST 60: get bad zone argument +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs.get(nil, "foo") + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +--- error_log +bad "zone" argument + + + +=== TEST 61: get nil key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:get(nil) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: nil key +--- no_error_log +[error] + + + +=== TEST 62: get empty key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:get("") + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: empty key +--- no_error_log +[error] + + + +=== TEST 63: get a too-long key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:get(string.rep("a", 65536)) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: key too long +--- no_error_log +[error] + + + +=== TEST 64: set & get large values +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("foo", string.rep("helloworld", 1024)) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + + local data, err = dogs:get("foo") + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + ngx.say("get ok: ", #data) + + } +--- stream_response +set ok +get ok: 10240 +--- no_error_log +[error] + + + +=== TEST 65: get_stale nil key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:get_stale(nil) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: nil key +--- no_error_log +[error] + + + +=== TEST 66: get_stale empty key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:get_stale("") + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: empty key +--- no_error_log +[error] + + + +=== TEST 67: get_stale number key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set(1024, "hello") + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + local data, err = dogs:get_stale(1024) + if not ok then + ngx.say("get_stale not ok: ", err) + return + end + ngx.say("get_stale: ", data) + } +--- stream_response +set ok +get_stale: hello +--- no_error_log +[error] + + + +=== TEST 68: get_stale a too-long key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:get_stale(string.rep("a", 65536)) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: key too long +--- no_error_log +[error] + + + +=== TEST 69: get_stale a non-existent key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local data, err = dogs:get_stale("not_found") + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + ngx.say("get ok: ", data) + } +--- stream_response +get ok: nil +--- no_error_log +[error] + + + +=== TEST 70: set & get_stale large values +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("foo", string.rep("helloworld", 1024)) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + + local data, err, stale = dogs:get_stale("foo") + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + ngx.say("get_stale ok: ", #data, ", stale: ", stale) + + } +--- stream_response +set ok +get_stale ok: 10240, stale: false +--- no_error_log +[error] + + + +=== TEST 71: set & get_stale boolean values (true) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("foo", true) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + + local data, err, stale = dogs:get_stale("foo") + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + ngx.say("get_stale ok: ", data, ", stale: ", stale) + + } +--- stream_response +set ok +get_stale ok: true, stale: false +--- no_error_log +[error] + + + +=== TEST 72: set & get_stale boolean values (false) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("foo", false) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + + local data, err, stale = dogs:get_stale("foo") + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + ngx.say("get_stale ok: ", data, ", stale: ", stale) + + } +--- stream_response +set ok +get_stale ok: false, stale: false +--- no_error_log +[error] + + + +=== TEST 73: set & get_stale with a flag +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:set("foo", false, 0, 325) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + + local data, err, stale = dogs:get_stale("foo") + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + flags = err + ngx.say("get_stale ok: ", data, ", flags: ", flags, + ", stale: ", stale) + + } +--- stream_response +set ok +get_stale ok: false, flags: 325, stale: false +--- no_error_log +[error] + + + +=== TEST 74: incr nil key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:incr(nil, 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: nil key +--- no_error_log +[error] + + + +=== TEST 75: incr bad zone argument +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs.incr(nil, "foo", 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +--- error_log +bad "zone" argument + + + +=== TEST 76: incr empty string keys +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:incr("", 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: empty key +--- no_error_log +[error] + + + +=== TEST 77: incr too long key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local key = string.rep("a", 65536) + local ok, err = dogs:incr(key, 32) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + + } +--- stream_response +not ok: key too long +--- no_error_log +[error] + + + +=== TEST 78: incr number key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local key = 56 + local ok, err = dogs:set(key, 1) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + ok, err = dogs:incr(key, 32) + if not ok then + ngx.say("incr not ok: ", err) + return + end + ngx.say("incr ok") + local data, err = dogs:get(key) + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + local flags = err + ngx.say("get ok: ", data, ", flags: ", flags) + + } +--- stream_response +set ok +incr ok +get ok: 33, flags: nil +--- no_error_log +[error] + + + +=== TEST 79: incr a number-like string key +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local key = 56 + local ok, err = dogs:set(key, 1) + if not ok then + ngx.say("set not ok: ", err) + return + end + ngx.say("set ok") + ok, err = dogs:incr(key, "32") + if not ok then + ngx.say("incr not ok: ", err) + return + end + ngx.say("incr ok") + local data, err = dogs:get(key) + if data == nil and err then + ngx.say("get not ok: ", err) + return + end + local flags = err + ngx.say("get ok: ", data, ", flags: ", flags) + + } +--- stream_response +set ok +incr ok +get ok: 33, flags: nil +--- no_error_log +[error] + + + +=== TEST 80: add nil values +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local ok, err = dogs:add("foo", nil) + if not ok then + ngx.say("not ok: ", err) + return + end + ngx.say("ok") + } +--- stream_response +not ok: attempt to add or replace nil values +--- no_error_log +[error] + + + +=== TEST 81: replace key with exptime +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 2, 0) + dogs:replace("foo", 32, 0.01) + local data = dogs:get("foo") + ngx.say("get foo: ", data) + ngx.sleep(0.02) + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + } +--- stream_response +get foo: 32 +replace: false not found false +foo = nil +--- no_error_log +[error] + + + +=== TEST 82: the lightuserdata ngx.null has no methods of shared dicts. +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local lightuserdata = ngx.null + lightuserdata:set("foo", 1) + } +--- stream_response +--- grep_error_log chop +attempt to index local 'lightuserdata' (a userdata value) +--- grep_error_log_out +attempt to index local 'lightuserdata' (a userdata value) +--- error_log +[error] +--- no_error_log +bad "zone" argument + + + +=== TEST 83: set bad zone table +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs.set({1}, "foo", 1) + } +--- stream_response +--- error_log +bad "zone" argument + + + +=== TEST 84: get bad zone table +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs.get({1}, "foo") + } +--- stream_response +--- error_log +bad "zone" argument + + + +=== TEST 85: incr bad zone table +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs.incr({1}, "foo", 32) + } +--- stream_response +--- error_log +bad "zone" argument + + + +=== TEST 86: check the type of the shdict object +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + ngx.say("type: ", type(ngx.shared.dogs)) + } +--- stream_response +type: table +--- no_error_log +[error] + + + +=== TEST 87: dogs, cat mixing +--- stream_config + lua_shared_dict dogs 1m; + lua_shared_dict cats 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + dogs:set("bah", 10502) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + val = dogs:get("bah") + ngx.say(val, " ", type(val)) + + local cats = ngx.shared.cats + val = cats:get("foo") + ngx.say(val or "nil") + val = cats:get("bah") + ngx.say(val or "nil") + } +--- stream_response +32 number +10502 number +nil +nil +--- no_error_log +[error] + + + +=== TEST 88: push to an expired list +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local len, err = dogs:lpush("cc", "1") --add another list to avoid key"aa" be cleaned (run ‘ngx_http_lua_shdict_expire(ctx, 1)’ may clean key ,ensure key'aa' not clean ,just expired)) + if not len then + ngx.say("push cc err: ", err) + end + local len, err = dogs:lpush("aa", "1") + if not len then + ngx.say("push1 err: ", err) + end + local succ, err = dogs:expire("aa", 0.2) + if not succ then + ngx.say("expire err: ",err) + end + ngx.sleep(0.3) -- list aa expired + local len, err = dogs:lpush("aa", "2") --push to an expired list may set as a new list + if not len then + ngx.say("push2 err: ", err) + end + local len, err = dogs:llen("aa") -- new list len is 1 + if not len then + ngx.say("llen err: ", err) + else + ngx.say("aa:len :", dogs:llen("aa")) + end + } +--- stream_response +aa:len :1 +--- no_error_log +[error] + + + +=== TEST 89: push to an expired list then pop many time (more then list len ) +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local len, err = dogs:lpush("cc", "1") --add another list to avoid key"aa" be cleaned (run ‘ngx_http_lua_shdict_expire(ctx, 1)’ may clean key ,ensure key'aa' not clean ,just expired)) + if not len then + ngx.say("push cc err: ", err) + end + local len, err = dogs:lpush("aa", "1") + if not len then + ngx.say("push1 err: ", err) + end + local succ, err = dogs:expire("aa", 0.2) + if not succ then + ngx.say("expire err: ",err) + end + ngx.sleep(0.3) -- list aa expired + local len, err = dogs:lpush("aa", "2") --push to an expired list may set as a new list + if not len then + ngx.say("push2 err: ", err) + end + local val, err = dogs:lpop("aa") + if not val then + ngx.say("llen err: ", err) + end + local val, err = dogs:lpop("aa") -- val == nil + ngx.say("aa list value: ", val) + } +--- stream_response +aa list value: nil +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/045-ngx-var.t b/src/deps/src/stream-lua-nginx-module/t/045-ngx-var.t new file mode 100644 index 000000000..131cfcf18 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/045-ngx-var.t @@ -0,0 +1,133 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 3); + +#no_diff(); +#no_long_string(); +#master_on(); +#workers(2); +run_tests(); + +__DATA__ + +=== TEST 1: pid +--- stream_server_config + content_by_lua_block { + local pid = ngx.var.pid + ngx.say("variable pid: ", pid) + if pid ~= tostring(ngx.worker.pid()) then + ngx.say("variable pid is wrong.") + else + ngx.say("variable pid is correct.") + end + } +--- stream_response_like +variable pid: \d+ +variable pid is correct\. +--- no_error_log +[error] + + + +=== TEST 2: remote_addr +--- stream_server_config + content_by_lua_block { + ngx.say("remote_addr: ", ngx.var.remote_addr) + ngx.say("type: ", type(ngx.var.remote_addr)) + } +--- stream_response +remote_addr: 127.0.0.1 +type: string + + + +=== TEST 3: binary_remote_addr +--- stream_server_config + content_by_lua_block { + ngx.say("binary_remote_addr len: ", #ngx.var.binary_remote_addr) + ngx.say("type: ", type(ngx.var.binary_remote_addr)) + } +--- stream_response +binary_remote_addr len: 4 +type: string + + + +=== TEST 4: server_addr & server_port +--- stream_server_config + content_by_lua_block { + ngx.say("server_addr: ", ngx.var.server_addr) + ngx.say("server_port: ", ngx.var.server_port) + ngx.say(type(ngx.var.server_addr)) + ngx.say(type(ngx.var.server_port)) + } +--- stream_response_like eval +qr/^server_addr: 127\.0\.0\.1 +server_port: \d{4,} +string +string +$/ + + + +=== TEST 5: connection & nginx_version +--- stream_server_config + content_by_lua_block { + ngx.say("connection: ", ngx.var.connection) + ngx.say("nginx_version: ", ngx.var.nginx_version) + ngx.say(type(ngx.var.connection)) + ngx.say(type(ngx.var.nginx_version)) + } +--- stream_response_like eval +qr/^connection: \d+ +nginx_version: \d+\.\d+\.\d+.* +string +string$/ + + + +=== TEST 6: reference nonexistent variable +--- stream_server_config + content_by_lua_block { + ngx.say("value: ", ngx.var.notfound) + } +--- stream_response +value: nil + + + +=== TEST 7: variable name is caseless +--- stream_server_config + content_by_lua_block { + ngx.say("value: ", ngx.var.REMOTE_ADDR) + } +--- stream_response +value: 127.0.0.1 + + + +=== TEST 8: get a bad variable name +--- stream_server_config + content_by_lua_block { + ngx.say("value: ", ngx.var[true]) + } +--- stream_response +--- error_log +bad variable name + + + +=== TEST 9: can not set variable +--- stream_server_config + content_by_lua_block { + ngx.var.foo = 56 + } +--- stream_response +--- error_log +variable "foo" not found for writing diff --git a/src/deps/src/stream-lua-nginx-module/t/046-hmac.t b/src/deps/src/stream-lua-nginx-module/t/046-hmac.t new file mode 100644 index 000000000..eea0d2c22 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/046-hmac.t @@ -0,0 +1,27 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local digest = ngx.hmac_sha1("thisisverysecretstuff", "some string we want to sign") + ngx.say(ngx.encode_base64(digest)) + } +--- stream_response +R/pvxzHC4NLtj7S+kXFg/NePTmk= diff --git a/src/deps/src/stream-lua-nginx-module/t/047-match-jit.t b/src/deps/src/stream-lua-nginx-module/t/047-match-jit.t new file mode 100644 index 000000000..ebf446615 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/047-match-jit.t @@ -0,0 +1,186 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 5); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched with j +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)", "j") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 2: not matched with j +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, world", "([0-9]+)", "j") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +not matched! +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 3: matched with jo +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, 1234", "([0-9]+)", "jo") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +1234 + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 4: not matched with jo +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello, world", "([0-9]+)", "jo") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +not matched! + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 5: bad pattern +--- stream_server_config + content_by_lua_block { + local m, err = ngx.re.match("hello\\nworld", "(abc", "j") + if m then + ngx.say(m[0]) + + else + if err then + ngx.say("error: ", err) + + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 6: just hit match limit +--- stream_config + lua_regex_match_limit 2940; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 21) + +local start = ngx.now() + +local res, err = ngx.re.match(s, re, "jo") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not res then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +error: pcre_exec() failed: -8 + + + +=== TEST 7: just not hit match limit +--- stream_config + lua_regex_match_limit 2950; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 21) + +local start = ngx.now() + +local res, err = ngx.re.match(s, re, "jo") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not res then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +failed to match diff --git a/src/deps/src/stream-lua-nginx-module/t/048-match-dfa.t b/src/deps/src/stream-lua-nginx-module/t/048-match-dfa.t new file mode 100644 index 000000000..0544ae3c8 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/048-match-dfa.t @@ -0,0 +1,154 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 4); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched with d +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello", "(he|hell)", "d") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +hell + + + +=== TEST 2: matched with d + j +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello", "(he|hell)", "jd") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +hell + + + +=== TEST 3: not matched with j +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("world", "(he|hell)", "d") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +not matched! + + + +=== TEST 4: matched with do +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("hello", "he|hell", "do") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + else + ngx.say("not matched!") + end + } +--- stream_response +hell +nil +nil + + + +=== TEST 5: not matched with do +--- stream_server_config + content_by_lua_block { + m = ngx.re.match("world", "([0-9]+)", "do") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stream_response +not matched! + + + +=== TEST 6: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("你好", ".", "Ud") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +你 +--- no_error_log +[error] + + + +=== TEST 7: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local m = ngx.re.match("你好", ".", "ud") + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 + +--- stream_response +你 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/049-gmatch-jit.t b/src/deps/src/stream-lua-nginx-module/t/049-gmatch-jit.t new file mode 100644 index 000000000..b5a2d6ecb --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/049-gmatch-jit.t @@ -0,0 +1,192 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 9); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: gmatch matched +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello, world", "[a-z]+", "j") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +hello +world +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 2: fail to match +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[0-9]", "j") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +nil +nil +nil +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 3: gmatch matched but no iterate +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "j") + ngx.say("done") + } +--- stream_response +done +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 4: gmatch matched but only iterate once and still matches remain +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "j") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched") + end + } +--- stream_response +hello +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 5: gmatch matched + o +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello, world", "[a-z]+", "jo") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +hello +world + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 6: fail to match + o +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[0-9]", "jo") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +nil +nil +nil + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 7: gmatch matched but no iterate + o +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "jo") + ngx.say("done") + } +--- stream_response +done + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 8: gmatch matched but only iterate once and still matches remain + o +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "jo") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched") + end + } +--- stream_response +hello + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 9: bad pattern +--- stream_server_config + content_by_lua_block { + local m, err = ngx.re.gmatch("hello\\nworld", "(abc", "j") + if not m then + ngx.say("error: ", err) + return + end + ngx.say("success") + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/050-gmatch-dfa.t b/src/deps/src/stream-lua-nginx-module/t/050-gmatch-dfa.t new file mode 100644 index 000000000..3a0b78e63 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/050-gmatch-dfa.t @@ -0,0 +1,239 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 5); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: gmatch matched +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello, halo", "h[a-z]|h[a-z][a-z]", "d") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +hel +hal + + + +=== TEST 2: d + j +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello, halo", "h[a-z]|h[a-z][a-z]", "dj") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +hel +hal + + + +=== TEST 3: fail to match +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[0-9]", "d") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +nil +nil +nil + + + +=== TEST 4: gmatch matched but no iterate +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "d") + ngx.say("done") + } +--- stream_response +done + + + +=== TEST 5: gmatch matched but only iterate once and still matches remain +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "d") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched") + end + } +--- stream_response +hello + + + +=== TEST 6: gmatch matched + o +--- stream_server_config + content_by_lua_block { + for m in ngx.re.gmatch("hello, world", "[a-z]+", "do") do + if m then + ngx.say(m[0]) + else + ngx.say("not matched: ", m) + end + end + } +--- stream_response +hello +world + + + +=== TEST 7: fail to match + o +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[0-9]", "do") + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + + local m = it() + if m then ngx.say(m[0]) else ngx.say(m) end + } +--- stream_response +nil +nil +nil + + + +=== TEST 8: gmatch matched but no iterate + o +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "do") + ngx.say("done") + } +--- stream_response +done + + + +=== TEST 9: gmatch matched but only iterate once and still matches remain + o +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("hello, world", "[a-z]+", "do") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched") + end + } +--- stream_response +hello + + + +=== TEST 10: bad pattern +--- stream_server_config + content_by_lua_block { + local it, err = ngx.re.gmatch("hello\\nworld", "(abc", "d") + if not it then + ngx.say("error: ", err) + return + end + ngx.say("success") + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 11: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("你好", ".", "Ud") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +你 +--- no_error_log +[error] + + + +=== TEST 12: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local it = ngx.re.gmatch("你好", ".", "ud") + local m = it() + if m then + ngx.say(m[0]) + else + ngx.say("not matched!") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 + +--- stream_response +你 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/051-sub-jit.t b/src/deps/src/stream-lua-nginx-module/t/051-sub-jit.t new file mode 100644 index 000000000..2334a0ca6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/051-sub-jit.t @@ -0,0 +1,125 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 6); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched with j +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234 5678", "([0-9]+)", "world", "j") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world 5678: 1 +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 2: not matched with j +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[0-9]+", "hiya", "j") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 3: matched with jo +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234 5678", "([0-9]+)", "world", "jo") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world 5678: 1 + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 4: not matched with jo +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[0-9]+", "hiya", "jo") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 5: bad pattern +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("hello\\nworld", "(abc", "world", "j") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 6: bad pattern + o +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub( "hello\\nworld", "(abc", "world", "jo") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/052-sub-dfa.t b/src/deps/src/stream-lua-nginx-module/t/052-sub-dfa.t new file mode 100644 index 000000000..b7423dfde --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/052-sub-dfa.t @@ -0,0 +1,167 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 6); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched with d +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234 5678", "[0-9]|[0-9][0-9]", "world", "d") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world34 5678: 1 + + + +=== TEST 2: not matched with d +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[0-9]+", "hiya", "d") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 + + + +=== TEST 3: matched with do +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, 1234 5678", "[0-9]|[0-9][0-9]", "world", "do") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world34 5678: 1 + + + +=== TEST 4: not matched with do +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.sub("hello, world", "[0-9]+", "hiya", "do") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 + + + +=== TEST 5: bad pattern +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("hello\\nworld", "(abc", "world", "j") + if s then + ngx.say(s, ": ", n) + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 6: bad pattern + o +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("hello\\nworld", "(abc", "world", "jo") + if s then + ngx.say(s, ": ", n) + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 7: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("你好", ".", "a", "Ud") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +s: a好 +--- no_error_log +[error] + + + +=== TEST 8: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.sub("你好", ".", "a", "ud") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 + +--- stream_response +s: a好 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/053-gsub-jit.t b/src/deps/src/stream-lua-nginx-module/t/053-gsub-jit.t new file mode 100644 index 000000000..a8efdd4c6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/053-gsub-jit.t @@ -0,0 +1,125 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 6); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched with j +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, 1234 5678", "([0-9]+)", "world", "j") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world world: 2 +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 2: not matched with j +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[0-9]+", "hiya", "j") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 +--- error_log +pcre JIT compiling result: 1 + + + +=== TEST 3: matched with jo +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, 1234 5678", "([0-9]+)", "world", "jo") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world world: 2 + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 4: not matched with jo +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[0-9]+", "hiya", "jo") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 + +--- grep_error_log eval +qr/pcre JIT compiling result: \d+/ + +--- grep_error_log_out eval +["pcre JIT compiling result: 1\n", ""] + + + +=== TEST 5: bad pattern +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("hello\\nworld", "(abc", "world", "j") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 6: bad pattern + o +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("hello\\nworld", "(abc", "world", "jo") + if s then + ngx.say(s, ": ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/054-gsub-dfa.t b/src/deps/src/stream-lua-nginx-module/t/054-gsub-dfa.t new file mode 100644 index 000000000..c707c75fc --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/054-gsub-dfa.t @@ -0,0 +1,168 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 5); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: matched with d +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, 1234 5678", "[0-9]|[0-9][0-9]", "world", "d") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, worldworld worldworld: 4 + + + +=== TEST 2: not matched with d +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[0-9]+", "hiya", "d") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 + + + +=== TEST 3: matched with do +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, 1234 5678", "[0-9]|[0-9][0-9]", "world", "do") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, worldworld worldworld: 4 + + + +=== TEST 4: not matched with do +--- stream_server_config + content_by_lua_block { + local s, n = ngx.re.gsub("hello, world", "[0-9]+", "hiya", "do") + if n then + ngx.say(s, ": ", n) + else + ngx.say(s) + end + } +--- stream_response +hello, world: 0 + + + +=== TEST 5: bad pattern +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("hello\\nworld", "(abc", "world", "j") + if s then + ngx.say("gsub: ", n) + + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" + + + +=== TEST 6: bad pattern + o +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("hello\\nworld", "(abc", "world", "jo") + if s then + ngx.say("gsub: ", n) + else + ngx.say("error: ", err) + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 7: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("你好", ".", "a", "Ud") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 +exec opts: 2000 +exec opts: 2000 + +--- stream_response +s: aa +--- no_error_log +[error] + + + +=== TEST 8: UTF-8 mode with UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s, n, err = ngx.re.gsub("你好", ".", "a", "ud") + if s then + ngx.say("s: ", s) + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_dfa_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 0 +exec opts: 0 +exec opts: 0 + +--- stream_response +s: aa +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/056-flush.t b/src/deps/src/stream-lua-nginx-module/t/056-flush.t new file mode 100644 index 000000000..fa59bd711 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/056-flush.t @@ -0,0 +1,210 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +BEGIN { + $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1; +} + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * 12; + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: flush wait - content +--- stream_server_config + content_by_lua_block { + ngx.say("hello, world") + local ok, err = ngx.flush(true) + if not ok then + ngx.log(ngx.ERR, "flush failed: ", err) + return + end + ngx.say("hiya") + } +--- stream_response +hello, world +hiya +--- no_error_log +[error] +--- error_log +lua reuse free buf memory 13 >= 5 + + + +=== TEST 2: flush no wait - content +--- stream_server_config + lua_socket_send_timeout 500ms; + content_by_lua_block { + ngx.say("hello, world") + local ok, err = ngx.flush(false) + if not ok then + ngx.log(ngx.ERR, "flush failed: ", err) + return + end + ngx.say("hiya") + } +--- stream_response +hello, world +hiya + + + +=== TEST 3: flush wait - big data +--- stream_server_config + content_by_lua_block { + ngx.say(string.rep("a", 1024 * 64)) + ngx.flush(true) + ngx.say("hiya") + } +--- stream_response +hello, world +hiya +--- SKIP + + + +=== TEST 4: flush wait in a user coroutine +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello, world") + ngx.flush(true) + coroutine.yield() + ngx.say("hiya") + end + local c = coroutine.create(f) + ngx.say(coroutine.resume(c)) + ngx.say(coroutine.resume(c)) + } +--- stap2 +F(ngx_http_lua_wev_handler) { + printf("wev handler: wev:%d\n", $r->connection->write->ready) +} + +global ids, cur + +function gen_id(k) { + if (ids[k]) return ids[k] + ids[k] = ++cur + return cur +} + +F(ngx_http_handler) { + delete ids + cur = 0 +} + +/* +F(ngx_http_lua_run_thread) { + id = gen_id($ctx->cur_co) + printf("run thread %d\n", id) +} + +probe process("/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2").function("lua_resume") { + id = gen_id($L) + printf("lua resume %d\n", id) +} +*/ + +M(http-lua-user-coroutine-resume) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("resume %x in %x\n", c, p) +} + +M(http-lua-entry-coroutine-yield) { + println("entry coroutine yield") +} + +/* +F(ngx_http_lua_coroutine_yield) { + printf("yield %x\n", gen_id($L)) +} +*/ + +M(http-lua-user-coroutine-yield) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("yield %x in %x\n", c, p) +} + +F(ngx_http_lua_atpanic) { + printf("lua atpanic(%d):", gen_id($L)) + print_ubacktrace(); +} + +M(http-lua-user-coroutine-create) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("create %x in %x\n", c, p) +} + +F(ngx_http_lua_ngx_exec) { println("exec") } + +F(ngx_http_lua_ngx_exit) { println("exit") } + +F(ngx_http_writer) { println("http writer") } + +--- stream_response +hello, world +true +hiya +true +--- error_log +lua reuse free buf memory 13 >= 5 + + + +=== TEST 5: flush before sending out the header +--- stream_server_config + content_by_lua_block { + ngx.flush() + ngx.status = 404 + ngx.say("not found") + } +--- stream_response +not found +--- no_error_log +[error] + + + +=== TEST 6: limit_rate +TODO +--- SKIP +--- stream_server_config + limit_rate 150; + content_by_lua_block { + local begin = ngx.now() + for i = 1, 2 do + ngx.print(string.rep("a", 100)) + local ok, err = ngx.flush(true) + if not ok then + ngx.log(ngx.ERR, "failed to flush: ", err) + end + end + local elapsed = ngx.now() - begin + ngx.log(ngx.WARN, "lua writes elapsed ", elapsed, " sec") + } +--- stream_response eval +"a" x 200 +--- error_log eval +[ +qr/lua writes elapsed [12](?:\.\d+)? sec/, +qr/lua flush requires waiting: buffered 0x[0-9a-f]+, delayed:1/, +] + +--- no_error_log +[error] +--- timeout: 4 diff --git a/src/deps/src/stream-lua-nginx-module/t/057-flush-timeout.t b/src/deps/src/stream-lua-nginx-module/t/057-flush-timeout.t new file mode 100644 index 000000000..801eebec6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/057-flush-timeout.t @@ -0,0 +1,313 @@ +# 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 ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'hello, world'; + $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1; +} + +use Test::Nginx::Socket::Lua::Stream; +use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * 25; + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: flush wait - timeout +--- stream_server_config + lua_socket_send_timeout 100ms; + content_by_lua_block { + ngx.say("hello, world") + ngx.flush(true) + ngx.say("hiya") + } +--- stream_response +received 12 bytes of response data. +--- log_stream_response +--- error_log eval +[ +qr/client timed out \(\d+: .*?timed out\)/, +qr/stream response: hello, world, client/, +] + +--- no_error_log +[error] + + + +=== TEST 2: send timeout timer got removed in time +--- stream_server_config + lua_socket_send_timeout 1234ms; + content_by_lua_block { + ngx.say(string.rep("blah blah blah", 10)) + -- ngx.flush(true) + ngx.eof() + for i = 1, 20 do + ngx.sleep(0.1) + end + } +--- stap +global evtime + +F(ngx_http_handler) { + delete evtime +} + +M(timer-add) { + if ($arg2 == 1234) { + printf("add timer %d\n", $arg2) + evtime[$arg1] = $arg2 + } +} + +M(timer-del) { + time = evtime[$arg1] + if (time == 1234) { + printf("del timer %d\n", time) + } +} + +M(timer-expire) { + time = evtime[$arg1] + if (time == 1234) { + printf("expire timer %d\n", time) + #print_ubacktrace() + } +} +/* +probe syscall.writev.return { + if (pid() == target()) { + printf("writev: %s\n", retstr) + } +} +*/ +--- stap_out +add timer 1234 +del timer 1234 + +--- log_stream_response +--- stream_response +received 141 bytes of response data. +--- no_error_log +[error] +--- timeout: 3 + + + +=== TEST 3: exit in user thread (entry thread is still pending on ngx.flush) +--- stream_server_config + lua_socket_send_timeout 1s; + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.2) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + + ngx.say("hello, world!") + ngx.flush(true) + + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 200 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 200 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 200 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_coctx_cleanup) { + println("lua tcp socket cleanup") +} + +/* +F(ngx_http_finalize_request) { + printf("finalize request: c:%d, a:%d, cb:%d, rb:%d\n", $r->main->count, + $r == $r->connection->data, $r->connection->buffered, $r->buffered) +} + +F(ngx_http_set_write_handler) { + println("set write handler") +} +*/ + +F(ngx_http_lua_flush_cleanup) { + println("lua flush cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 200 +expire timer 100 +terminate 2: ok +delete thread 2 +lua flush cleanup +delete timer 200 +delete thread 1 +add timer 200 +expire timer 200 +free request + +--- log_stream_response +--- stream_response +received 41 bytes of response data. +--- no_error_log +[error] +--- timeout: 7 + + + +=== TEST 4: flush wait - return "timeout" error +--- stream_server_config + lua_socket_send_timeout 100ms; + content_by_lua_block { + ngx.say("hello, world") + local ok, err = ngx.flush(true) + if not ok then + ngx.log(ngx.ERR, "failed to flush: ", err) + return + end + ngx.say("hiya") + } +--- log_stream_response +--- stream_response +received 12 bytes of response data. +--- error_log eval +[ +qr/client timed out \(\d+: .*?timed out\)/, +'failed to flush: timeout', +] +--- no_error_log +[alert] + + + +=== TEST 5: flush wait in multiple user threads - return "timeout" error +--- stream_server_config + lua_socket_send_timeout 100ms; + content_by_lua_block { + ngx.say("hello, world") + + local function run(tag) + local ok, err = ngx.flush(true) + if not ok then + ngx.log(ngx.ERR, "thread ", tag, ": failed to flush: ", err) + return + end + ngx.say("hiya") + end + + local function new_thread(tag) + local ok, err = ngx.thread.spawn(run, tag) + if not ok then + return error("failed to spawn thread: ", err) + end + end + + new_thread("A") + new_thread("B") + run("main") + } +--- log_stream_response +--- stream_response +received 12 bytes of response data. +--- error_log eval +[ +qr/client timed out \(\d+: .*?timed out\)/, +'thread main: failed to flush: timeout', +'thread A: failed to flush: timeout', +'thread B: failed to flush: timeout', +] +--- no_error_log +[alert] + + + +=== TEST 6: flush wait - client abort connection prematurely +TODO +--- SKIP +--- stream_server_config + #lua_socket_send_timeout 100ms; + limit_rate 2; + content_by_lua_block { + ngx.say("hello, world") + if not ok then + ngx.log(ngx.ERR, "failed to flush: ", err) + return + end + ngx.say("hiya") + } +--- stream_response +--- error_log eval +[ +qr/writev\(\) failed .*? Broken pipe/i, +qr/failed to flush: client aborted/, +] +--- no_error_log +[alert] + +--- timeout: 0.2 +--- abort +--- wait: 1 diff --git a/src/deps/src/stream-lua-nginx-module/t/058-tcp-socket.t b/src/deps/src/stream-lua-nginx-module/t/058-tcp-socket.t new file mode 100644 index 000000000..c4657a802 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/058-tcp-socket.t @@ -0,0 +1,3528 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * 219; + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 2: no trailing newline +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + sock:close() + ngx.say("closed") + } + +--- config + server_tokens off; + + location /foo { + content_by_lua_block { ngx.print("foo") } + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 3 +received: Connection: close +received: +failed to receive a line: closed [foo] +closed +--- no_error_log +[error] + + + +=== TEST 3: no resolver defined +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("agentzh.org", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + } +--- stream_response +failed to connect: no resolver defined to resolve "agentzh.org" +connected: nil +failed to send request: closed +--- error_log +attempt to send data on a closed socket: + + + +=== TEST 4: with resolver +--- timeout: 10 +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("agentzh.org", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET / HTTP/1.0\r\nHost: agentzh.org\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local line, err = sock:receive() + if line then + ngx.say("first line received: ", line) + + else + ngx.say("failed to receive the first line: ", err) + end + + line, err = sock:receive() + if line then + ngx.say("second line received: ", line) + + else + ngx.say("failed to receive the second line: ", err) + end + } + +--- stream_response_like +connected: 1 +request sent: 56 +first line received: HTTP\/1\.1 200 OK +second line received: (?:Date|Server): .*? +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 5: connection refused (tcp) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", 16787) + ngx.say("connect: ", ok, " ", err) + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connect: nil connection refused +send: nil closed +receive: nil closed +close: nil closed +--- error_log eval +qr/connect\(\) failed \(\d+: Connection refused\)/ + + + +=== TEST 6: connection timeout (tcp) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + #lua_socket_connect_timeout 100ms; + #lua_socket_send_timeout 100ms; + #lua_socket_read_timeout 100ms; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(100) -- ms + + local ok, err = sock:connect("127.0.0.2", 12345) + ngx.say("connect: ", ok, " ", err) + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connect: nil timeout +send: nil closed +receive: nil closed +close: nil closed +--- error_log +lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 7: not closed manually +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- stream_response +connected: 1 +--- no_error_log +[error] + + + +=== TEST 8: resolver error (host not found) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("blah-blah-not-found.agentzh.org", port) + print("connected: ", ok, " ", err, " ", not ok) + if not ok then + ngx.say("failed to connect: ", err) + end + + ngx.say("connected: ", ok) + + local req = "GET / HTTP/1.0\r\nHost: agentzh.org\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + } +--- stream_response_like +^failed to connect: blah-blah-not-found\.agentzh\.org could not be resolved(?: \(3: Host not found\))? +connected: nil +failed to send request: closed$ +--- error_log +attempt to send data on a closed socket +--- timeout: 10 + + + +=== TEST 9: resolver error (timeout) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 1ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("blah-blah-not-found.agentzh.org", port) + print("connected: ", ok, " ", err, " ", not ok) + if not ok then + ngx.say("failed to connect: ", err) + end + + ngx.say("connected: ", ok) + + local req = "GET / HTTP/1.0\r\nHost: agentzh.org\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + } +--- stream_response_like +^failed to connect: blah-blah-not-found\.agentzh\.org could not be resolved(?: \(\d+: (?:Operation timed out|Host not found)\))? +connected: nil +failed to send request: closed$ +--- error_log +attempt to send data on a closed socket + + + +=== TEST 10: explicit *l pattern for receive +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err = sock:receive("*l") + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err) + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + location = /foo { + server_tokens off; + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 11: *a pattern for receive +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local data, err = sock:receive("*a") + if data then + ngx.say("receive: ", data) + ngx.say("err: ", err) + + else + ngx.say("failed to receive: ", err) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +"connected: 1 +request sent: 57 +receive: HTTP/1.1 200 OK\r +Server: nginx\r +Content-Type: text/plain\r +Content-Length: 4\r +Connection: close\r +\r +foo + +err: nil +close: 1 nil +" +--- no_error_log +[error] + + + +=== TEST 12: mixing *a and *l patterns for receive +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local line, err = sock:receive("*l") + if line then + ngx.say("receive: ", line) + ngx.say("err: ", err) + + else + ngx.say("failed to receive: ", err) + end + + local data + data, err = sock:receive("*a") + if data then + ngx.say("receive: ", data) + ngx.say("err: ", err) + + else + ngx.say("failed to receive: ", err) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +"connected: 1 +request sent: 57 +receive: HTTP/1.1 200 OK +err: nil +receive: Server: nginx\r +Content-Type: text/plain\r +Content-Length: 4\r +Connection: close\r +\r +foo + +err: nil +close: 1 nil +" +--- no_error_log +[error] + + + +=== TEST 13: receive by chunks +--- timeout: 5 +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local data, err, partial = sock:receive(10) + if data then + local len = string.len(data) + if len == 10 then + ngx.print("[", data, "]") + else + ngx.say("ERROR: returned invalid length of data: ", len) + end + + else + ngx.say("failed to receive a line: ", err, " [", partial, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +"connected: 1 +request sent: 57 +[HTTP/1.1 2][00 OK\r +Ser][ver: nginx][\r +Content-][Type: text][/plain\r +Co][ntent-Leng][th: 4\r +Con][nection: c][lose\r +\r +fo]failed to receive a line: closed [o +] +close: 1 nil +" +--- no_error_log +[error] + + + +=== TEST 14: receive by chunks (very small buffer) +--- timeout: 5 +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local data, err, partial = sock:receive(10) + if data then + local len = string.len(data) + if len == 10 then + ngx.print("[", data, "]") + else + ngx.say("ERROR: returned invalid length of data: ", len) + end + + else + ngx.say("failed to receive a line: ", err, " [", partial, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +"connected: 1 +request sent: 57 +[HTTP/1.1 2][00 OK\r +Ser][ver: nginx][\r +Content-][Type: text][/plain\r +Co][ntent-Leng][th: 4\r +Con][nection: c][lose\r +\r +fo]failed to receive a line: closed [o +] +close: 1 nil +" +--- no_error_log +[error] + + + +=== TEST 15: line reading (very small buffer) +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 16: ngx.socket.connect (working) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not sock then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected.") + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response +connected. +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 17: ngx.socket.connect() shortcut (connection refused) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local sock, err = sock:connect("127.0.0.1", 16787) + if not sock then + ngx.say("failed to connect: ", err) + return + end + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stap2 +M(http-lua-info) { + printf("tcp resume: %p\n", $coctx) + print_ubacktrace() +} + +--- stream_response +failed to connect: connection refused +--- error_log eval +qr/connect\(\) failed \(\d+: Connection refused\)/ + + + +=== TEST 18: receive by chunks (stringified size) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local data, err, partial = sock:receive("10") + if data then + local len = string.len(data) + if len == 10 then + ngx.print("[", data, "]") + else + ngx.say("ERROR: returned invalid length of data: ", len) + end + + else + ngx.say("failed to receive a line: ", err, " [", partial, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +"connected: 1 +request sent: 57 +[HTTP/1.1 2][00 OK\r +Ser][ver: nginx][\r +Content-][Type: text][/plain\r +Co][ntent-Leng][th: 4\r +Con][nection: c][lose\r +\r +fo]failed to receive a line: closed [o +] +close: 1 nil +" +--- no_error_log +[error] + + + +=== TEST 19: cannot survive across request boundary (send) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go($TEST_NGINX_MEMCACHED_PORT) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function go(port) + if not sock then + sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + end + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end +end +--- stream_response_like eval +"^(?:connected: 1 +request sent: 11 +received: OK|failed to send request: closed)\$" + + + +=== TEST 20: cannot survive across request boundary (receive) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go($TEST_NGINX_MEMCACHED_PORT) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function go(port) + if not sock then + sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + else + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + return + end + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end +end + +--- stap2 +M(http-lua-info) { + printf("tcp resume\n") + print_ubacktrace() +} +--- stream_response_like eval +qr/^(?:connected: 1 +request sent: 11 +received: OK|failed to receive a line: closed \[nil\])$/ + + + +=== TEST 21: cannot survive across request boundary (close) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go($TEST_NGINX_MEMCACHED_PORT) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function go(port) + if not sock then + sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + else + local ok, err = sock:close() + if ok then + ngx.say("successfully closed") + + else + ngx.say("failed to close: ", err) + end + return + end + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end +end +--- stream_response_like eval +qr/^(?:connected: 1 +request sent: 11 +received: OK|failed to close: closed)$/ + + + +=== TEST 22: cannot survive across request boundary (connect) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go($TEST_NGINX_MEMCACHED_PORT) + test.go($TEST_NGINX_MEMCACHED_PORT) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function go(port) + if not sock then + sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + else + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect again: ", err) + return + end + + ngx.say("connected again: ", ok) + end + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end +end +--- stream_response_like eval +qr/^(?:connected(?: again)?: 1 +request sent: 11 +received: OK +){2}$/ +--- error_log +lua reuse socket upstream ctx +--- no_error_log +[error] +--- SKIP + + + +=== TEST 23: connect again immediately +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected again: ", ok) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connected: 1 +connected again: 1 +request sent: 11 +received: OK +close: 1 nil +--- no_error_log +[error] +--- error_log eval +["lua reuse socket upstream", "lua tcp socket reconnect without shutting down"] + + + +=== TEST 24: two sockets mix together +--- stream_server_config + content_by_lua_block { + local sock1 = ngx.socket.tcp() + local sock2 = ngx.socket.tcp() + + local port1 = $TEST_NGINX_MEMCACHED_PORT + local port2 = $TEST_NGINX_SERVER_PORT + + local ok, err = sock1:connect("127.0.0.1", port1) + if not ok then + ngx.say("1: failed to connect: ", err) + return + end + + ngx.say("1: connected: ", ok) + + ok, err = sock2:connect("127.0.0.1", port2) + if not ok then + ngx.say("2: failed to connect: ", err) + return + end + + ngx.say("2: connected: ", ok) + + local req1 = "flush_all\r\n" + local bytes, err = sock1:send(req1) + if not bytes then + ngx.say("1: failed to send request: ", err) + return + end + ngx.say("1: request sent: ", bytes) + + local req2 = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + local bytes, err = sock2:send(req2) + if not bytes then + ngx.say("2: failed to send request: ", err) + return + end + ngx.say("2: request sent: ", bytes) + + local line, err, part = sock1:receive() + if line then + ngx.say("1: received: ", line) + + else + ngx.say("1: failed to receive a line: ", err, " [", part, "]") + end + + line, err, part = sock2:receive() + if line then + ngx.say("2: received: ", line) + + else + ngx.say("2: failed to receive a line: ", err, " [", part, "]") + end + + ok, err = sock1:close() + ngx.say("1: close: ", ok, " ", err) + + ok, err = sock2:close() + ngx.say("2: close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo foo; + more_clear_headers Date; + } +--- stream_response +1: connected: 1 +2: connected: 1 +1: request sent: 11 +2: request sent: 57 +1: received: OK +2: received: HTTP/1.1 200 OK +1: close: 1 nil +2: close: 1 nil +--- no_error_log +[error] + + + +=== TEST 25: send tables of string fragments +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = {"GET", " ", "/foo", " HTTP/", 1, ".", 0, "\r\n", + "Host: localhost\r\n", "Connection: close\r\n", + "\r\n"} + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 26: send tables of string fragments (bad type "nil") +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = {"GET", " ", "/foo", " HTTP/", nil, 1, ".", 0, "\r\n", + "Host: localhost\r\n", "Connection: close\r\n", + "\r\n"} + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo foo; + more_clear_headers Date; + } +--- stream_response +connected: 1 +--- error_log +bad argument #1 to 'send' (bad data type nil found) + + + +=== TEST 27: send tables of string fragments (bad type "boolean") +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = {"GET", " ", "/foo", " HTTP/", true, 1, ".", 0, "\r\n", + "Host: localhost\r\n", "Connection: close\r\n", + "\r\n"} + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo foo; + more_clear_headers Date; + } +--- stream_response +connected: 1 +--- error_log +bad argument #1 to 'send' (bad data type boolean found) + + + +=== TEST 28: send tables of string fragments (bad type ngx.null) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = {"GET", " ", "/foo", " HTTP/", ngx.null, 1, ".", 0, "\r\n", + "Host: localhost\r\n", "Connection: close\r\n", + "\r\n"} + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo foo; + more_clear_headers Date; + } +--- stream_response +connected: 1 +--- error_log +bad argument #1 to 'send' (bad data type userdata found) + + + +=== TEST 29: CR in a line +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo "foo\r\rbar\rbaz"; + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 13 +received: Connection: close +received: +received: foobarbaz +failed to receive a line: closed [] +close: nil closed +--- no_error_log +[error] +--- SKIP + + + +=== TEST 30: receive(0) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local data, err, part = sock:receive(0) + if not data then + ngx.say("failed to receive(0): ", err) + return + end + + ngx.say("receive(0): [", data, "]") + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo foo; + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +receive(0): [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 31: send("") +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local bytes, err = sock:send("") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("send(\"\"): ", bytes) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo foo; + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +send(""): 0 +close: 1 nil +--- no_error_log +[error] +[alert] + + + +=== TEST 32: connection refused (tcp) - lua_socket_log_errors off +--- stream_server_config + lua_socket_log_errors off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", 16787) + ngx.say("connect: ", ok, " ", err) + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connect: nil connection refused +send: nil closed +receive: nil closed +close: nil closed +--- no_error_log eval +[qr/connect\(\) failed \(\d+: Connection refused\)/] + + + +=== TEST 33: reread after a read time out happen (receive -> receive) +--- stream_server_config + lua_socket_read_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + + line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + end + end + } +--- stream_response +connected: 1 +failed to receive: timeout +failed to receive: timeout +--- error_log +lua tcp socket read timeout: 100 +lua tcp socket connect timeout: 60000 +lua tcp socket read timed out + + + +=== TEST 34: successful reread after a read time out happen (receive -> receive) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("GET /back HTTP/1.1\r\nHost: localhost\r\n\r\n") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to read the response header: ", err) + return + end + + sock:settimeout(100) + + local data, err, partial = sock:receive(100) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, ", partial: ", partial) + + sock:settimeout(123) + ngx.sleep(0.1) + local line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + location = /back { + content_by_lua_block { + ngx.print("hi") + ngx.flush(true) + ngx.sleep(0.2) + ngx.print("world") + } + } +--- stream_response eval +"failed to receive: timeout, partial: 2\r +hi\r + +received: 5 +received: world +" +--- error_log +lua tcp socket read timed out +--- no_error_log +[alert] + + + +=== TEST 35: successful reread after a read time out happen (receive -> receiveuntil) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("GET /back HTTP/1.1\r\nHost: localhost\r\n\r\n") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to read the response header: ", err) + return + end + + sock:settimeout(100) + + local data, err, partial = sock:receive(100) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, ", partial: ", partial) + + ngx.sleep(0.1) + + sock:settimeout(123) + local reader = sock:receiveuntil("\r\n") + + local line, err = reader() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + + local line, err = reader() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + server_tokens off; + location = /back { + content_by_lua_block { + ngx.print("hi") + ngx.flush(true) + ngx.sleep(0.2) + ngx.print("world") + } + } +--- stream_response eval +"failed to receive: timeout, partial: 2\r +hi\r + +received: 5 +received: world +" +--- error_log +lua tcp socket read timed out +--- no_error_log +[alert] + + + +=== TEST 36: successful reread after a read time out happen (receiveuntil -> receiveuntil) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("GET /back HTTP/1.1\r\nHost: localhost\r\n\r\n") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to read the response header: ", err) + return + end + + sock:settimeout(100) + + local reader = sock:receiveuntil("no-such-terminator") + local data, err, partial = reader() + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, ", partial: ", partial) + + ngx.sleep(0.1) + + sock:settimeout(123) + + local reader = sock:receiveuntil("\r\n") + + local line, err = reader() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + + local line, err = reader() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + server_tokens off; + location = /back { + content_by_lua_block { + ngx.print("hi") + ngx.flush(true) + ngx.sleep(0.2) + ngx.print("world") + } + } +--- stream_response eval +"failed to receive: timeout, partial: 2\r +hi\r + +received: 5 +received: world +" +--- error_log +lua tcp socket read timed out +--- no_error_log +[alert] + + + +=== TEST 37: successful reread after a read time out happen (receiveuntil -> receive) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("GET /back HTTP/1.1\r\nHost: localhost\r\n\r\n") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to read the response header: ", err) + return + end + + sock:settimeout(100) + + local reader = sock:receiveuntil("no-such-terminator") + local data, err, partial = reader() + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, ", partial: ", partial) + + ngx.sleep(0.1) + + sock:settimeout(123) + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + server_tokens off; + location = /back { + content_by_lua_block { + ngx.print("hi") + ngx.flush(true) + ngx.sleep(0.2) + ngx.print("world") + } + } +--- stream_response eval +"failed to receive: timeout, partial: 2\r +hi\r + +received: 5 +received: world +" +--- error_log +lua tcp socket read timed out +--- no_error_log +[alert] + + + +=== TEST 38: receive(0) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local data, err = sock:receive(0) + if not data then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("received: ", data) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /back { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +received: +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 39: empty options table +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port, {}) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connected: 1 +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 40: u->coctx left over bug +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local ready = false + local fatal = false + + function f() + local line, err, part = sock:receive() + if not line then + ngx.say("failed to receive the 1st line: ", err, " [", part, "]") + fatal = true + return + end + ready = true + ngx.sleep(1) + end + + local st = ngx.thread.spawn(f) + while true do + if fatal then + return + end + + if not ready then + ngx.sleep(0.01) + else + break + end + end + + while true do + local line, err, part = sock:receive() + if line then + -- ngx.say("received: ", line) + + else + -- ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + ngx.exit(0) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.sleep(0.1) ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +request sent: 57 +close: 1 nil +--- no_error_log +[error] +--- error_log +lua clean up the timer for pending ngx.sleep + + + +=== TEST 41: bad request tries to connect +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.new_sock() + local sock = test.get_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + else + ngx.say("connected") + end + + local function f() + local sock = test.get_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + end + end + + ngx.timer.at(0, f) + ngx.sleep(0.001) + } + +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.tcp() +end + +function get_sock() + return sock +end +--- stream_response +connected + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):14: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 42: bad request tries to receive +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + else + ngx.say("connected") + end + local function f() + local test = require "test" + local sock = test.get_sock() + sock:receive() + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.tcp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +connected + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):13: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 43: bad request tries to send +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + else + ngx.say("connected") + end + local function f() + local sock = test.get_sock() + sock:send("a") + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.tcp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +connected + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 44: bad request tries to close +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + else + ngx.say("connected") + end + local function f() + local sock = test.get_sock() + sock:close() + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.tcp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +connected + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 45: bad request tries to set keepalive +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + else + ngx.say("connected") + end + local function f() + local sock = test.get_sock() + sock:setkeepalive() + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.tcp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +connected + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 46: bad request tries to receiveuntil +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + else + ngx.say("connected") + end + local function f() + local sock = test.get_sock() + local it, err = sock:receiveuntil("abc") + if it then + it() + end + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.tcp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +connected + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):14: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 47: cosocket resolving aborted by coroutine yielding failures (require) +--- stream_config + lua_package_path "$prefix/html/?.lua;;"; + resolver $TEST_NGINX_RESOLVER ipv6=off; + +--- stream_server_config + content_by_lua_block { + package.loaded.myfoo = nil + require "myfoo" + } +--- user_files +>>> myfoo.lua +local sock = ngx.socket.tcp() +local ok, err = sock:connect("agentzh.org") +if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return +end + +--- stream_response +--- wait: 0.3 +--- error_log +resolve name done +runtime error: attempt to yield across C-call boundary +--- no_error_log +[alert] + + + +=== TEST 48: cosocket resolving aborted by coroutine yielding failures (xpcall err) +--- stream_config + lua_package_path "$prefix/html/?.lua;;"; + resolver $TEST_NGINX_RESOLVER ipv6=off; + +--- stream_server_config + content_by_lua_block { + local function f() + return error(1) + end + local function err() + local sock = ngx.socket.tcp() + local ok, err = sock:connect("agentzh.org") + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + end + xpcall(f, err) + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.3 +--- error_log +resolve name done +--- no_error_log +[error] +[alert] +could not cancel + + + +=== TEST 49: tcp_nodelay on +--- stream_server_config + tcp_nodelay on; + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- error_log +stream lua socket tcp_nodelay +--- no_error_log +[error] + + + +=== TEST 50: tcp_nodelay off +--- stream_server_config + tcp_nodelay off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +stream lua socket tcp_nodelay +[error] + + + +=== TEST 51: IPv6 +--- http_config + server_tokens off; + + server { + listen [::1]:$TEST_NGINX_SERVER_PORT; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("[::1]", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] +--- skip_eval: 3: system("ping6 -c 1 ::1 >/dev/null 2>&1") ne 0 + + + +=== TEST 52: kill a thread with a connecting socket +--- stream_server_config + lua_socket_connect_timeout 1s; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + content_by_lua_block { + local sock + + local thr = ngx.thread.spawn(function () + sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + end) + + ngx.sleep(0.002) + ngx.thread.kill(thr) + ngx.sleep(0.001) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to setkeepalive: ", err) + else + ngx.say("setkeepalive: ", ok) + end + } + +--- stream_response +failed to setkeepalive: closed +--- error_log +stream lua tcp socket connect timeout: 100 +--- timeout: 10 + + + +=== TEST 53: reuse cleanup +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + for i = 1, 2 do + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if not line then + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +request sent: 57 +failed to receive a line: closed [] +close: 1 nil +connected: 1 +request sent: 57 +failed to receive a line: closed [] +close: 1 nil +--- error_log +lua stream cleanup reuse + + + +=== TEST 54: reuse cleanup in ngx.timer (fake_request) +--- stream_server_config + content_by_lua_block { + local total_send_bytes = 0 + local port = $TEST_NGINX_SERVER_PORT + + local function network() + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.log(ngx.ERR, "failed to send request: ", err) + return + end + + total_send_bytes = total_send_bytes + bytes + + while true do + local line, err, part = sock:receive() + if not line then + break + end + end + + ok, err = sock:close() + end + + local done = false + + local function double_network() + network() + network() + done = true + end + + local ok, err = ngx.timer.at(0, double_network) + if not ok then + ngx.say("failed to create timer: ", err) + end + + i = 1 + while not done do + local time = 0.005 * i + if time > 0.1 then + time = 0.1 + end + ngx.sleep(time) + i = i + 1 + end + + collectgarbage("collect") + + ngx.say("total_send_bytes: ", total_send_bytes) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +total_send_bytes: 114 +--- error_log +lua stream cleanup reuse + + + +=== TEST 55: free cleanup in ngx.timer (without sock:close) +--- stream_server_config + content_by_lua_block { + local total_send_bytes = 0 + + local function network() + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.log(ngx.ERR, "failed to send request: ", err) + return + end + + total_send_bytes = total_send_bytes + bytes + + while true do + local line, err, part = sock:receive() + if not line then + break + end + end + end + + local done = false + + local function double_network() + network() + network() + done = true + end + + local ok, err = ngx.timer.at(0, double_network) + if not ok then + ngx.say("failed to create timer: ", err) + end + + i = 1 + while not done do + local time = 0.005 * i + if time > 0.1 then + time = 0.1 + end + ngx.sleep(time) + i = i + 1 + end + + collectgarbage("collect") + + ngx.say("total_send_bytes: ", total_send_bytes) + } + +--- config + server_tokens off; + + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +total_send_bytes: 114 +--- no_error_log +[error] + + + +=== TEST 56: setkeepalive on socket already shutdown +--- stream_server_config + lua_socket_connect_timeout 1s; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local ok, err = sock:shutdown('send') + if not ok then + ngx.log(ngx.ERR, 'failed to shutdown socket: ', err) + return + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.log(ngx.ERR, "failed to setkeepalive: ", err) + end + } + +--- stream_response +connected: 1 +--- error_log +stream lua shutdown socket write direction +failed to setkeepalive: closed + + + +=== TEST 57: options_table is nil +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port, nil) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connected: 1 +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 58: resolver send query failing immediately in connect() +this case did not clear coctx->cleanup properly and would lead to memory invalid accesses. + +this test case requires the following iptables rule to work properly: + +sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT + +--- stream_server_config + resolver 127.0.0.1:10086 ipv6=off; + resolver_timeout 10ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + + for i = 1, 3 do -- retry + local ok, err = sock:connect("www.google.com", 80) + if not ok then + ngx.say("failed to connect: ", err) + end + end + + ngx.say("hello!") + } +--- stream_response_body_like +failed to connect: www.google.com could not be resolved(?: \(\d+: Operation timed out\))? +failed to connect: www.google.com could not be resolved(?: \(\d+: Operation timed out\))? +failed to connect: www.google.com could not be resolved(?: \(\d+: Operation timed out\))? +hello! +--- error_log eval +qr{\[alert\] .*? send\(\) failed \(\d+: Operation not permitted\) while resolving} + + + +=== TEST 59: the upper bound of port range should be 2^16 - 1 +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.connect("127.0.0.1", 65536) + if not sock then + ngx.say("failed to connect: ", err) + end + } + +--- stream_response +failed to connect: bad port number: 65536 +--- no_error_log +[error] + + + +=== TEST 60: send boolean and nil +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local function send(data) + local bytes, err = sock:send(data) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + end + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\nTest: " + send(req) + send(true) + send(false) + send(nil) + send("\r\n\r\n") + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + else + break + end + end + + ok, err = sock:close() + } +--- config + location /foo { + server_tokens off; + more_clear_headers Date; + echo $http_test; + } +--- stream_response +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Connection: close +received: +received: truefalsenil +--- no_error_log +[error] + + + +=== TEST 61: TCP socket GC'ed in preread phase without Lua content phase +--- stream_server_config + lua_socket_connect_timeout 1s; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + preread_by_lua_block { + do + local sock = ngx.socket.tcp() + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + end + + ngx.timer.at(0, function() + collectgarbage() + ngx.log(ngx.WARN, "GC cycle done") + end) + } + + return 1; + +--- stream_response chomp +connected: 1 +1 +--- no_error_log +[error] +--- error_log +cleanup lua tcp socket request +GC cycle done + + + +=== TEST 62: receiveany method in cosocket +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = { + '1', + '22', + 'hello world', + } + + local length = 0 + for _, v in ipairs(resp) do + length = length + #v + end + + -- flush http header + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + -- send http body + for _, v in ipairs(resp) do + ngx.print(v) + ngx.flush(true) + ngx.sleep(0.01) + end + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + -- skip http header + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ', err) + return + end + + if #data == 0 then -- read last line of head + break + end + end + + -- receive http body + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + end + ngx.say(data) + end + + sock:close() + } +--- stream_response +1 +22 +hello world +--- no_error_log +[error] +--- error_log +lua tcp socket read any + + + +=== TEST 63: receiveany send data after read side closed +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + assert(sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1)) + + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + break + end + + local data = "send data after read side closed" + local bytes, err = sock:send(data) + if not bytes then + ngx.say(err) + end + + break + end + ngx.say(data) + end + + sock:close() + } +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 +--- tcp_shutdown: 1 +--- tcp_query eval: "send data after read side closed" +--- tcp_query_len: 32 +--- stream_response +--- no_error_log +[error] + + + +=== TEST 64: receiveany with limited, max <= 0 +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + + local function receiveany_say_err(...) + local ok, err = pcall(sock.receiveany, sock, ...) + if not ok then + ngx.say(err) + end + end + + + receiveany_say_err(0) + receiveany_say_err(-1) + receiveany_say_err() + receiveany_say_err(nil) + } +--- stream_response +bad argument #2 to '?' (bad max argument) +bad argument #2 to '?' (bad max argument) +expecting 2 arguments (including the object), but got 1 +bad argument #2 to '?' (bad max argument) +--- no_error_log +[error] + + + +=== TEST 65: receiveany with limited, max is larger than data +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = 'hello world' + local length = #resp + + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + ngx.print(resp) + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ', err) + return + end + + if #data == 0 then -- read last line of head + break + end + end + + local data, err = sock:receiveany(128) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + else + ngx.say(data) + end + + sock:close() + } +--- stream_response +hello world +--- no_error_log +[error] +--- error_log +lua tcp socket calling receiveany() method to read at most 128 bytes + + + +=== TEST 66: receiveany with limited, max is smaller than data +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = 'hello world' + local length = #resp + + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + ngx.print(resp) + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ', err) + return + end + + if #data == 0 then -- read last line of head + break + end + end + + while true do + local data, err = sock:receiveany(7) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + + else + ngx.say(data) + end + end + + sock:close() + } +--- stream_response +hello w +orld +--- no_error_log +[error] +--- error_log +lua tcp socket calling receiveany() method to read at most 7 bytes diff --git a/src/deps/src/stream-lua-nginx-module/t/059-unix-socket.t b/src/deps/src/stream-lua-nginx-module/t/059-unix-socket.t new file mode 100644 index 000000000..8df7c0607 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/059-unix-socket.t @@ -0,0 +1,123 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 1); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +no_long_string(); +#no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: connection refused (unix domain socket) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("unix:/tmp/nosuchfile.sock") + ngx.say("connect: ", ok, " ", err) + + local bytes + bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local line + line, err = sock:receive() + ngx.say("receive: ", line, " ", err) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connect: nil no such file or directory +send: nil closed +receive: nil closed +close: nil closed +--- error_log eval +qr{\[crit\] .*? connect\(\) to unix:/tmp/nosuchfile\.sock failed} + + + +=== TEST 2: invalid host argument +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("/tmp/test-nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- stream_response +failed to connect: failed to parse host name "/tmp/test-nginx.sock": invalid host + + + +=== TEST 3: sanity +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + + server_tokens off; + location /foo { + content_by_lua 'ngx.say("foo")'; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + print("calling receive") + local line, err = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err) + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed +close: 1 nil diff --git a/src/deps/src/stream-lua-nginx-module/t/060-lua-memcached.t b/src/deps/src/stream-lua-nginx-module/t/060-lua-memcached.t new file mode 100644 index 000000000..a1d70c366 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/060-lua-memcached.t @@ -0,0 +1,160 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 1); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +my $pwd = `pwd`; +chomp $pwd; +$ENV{TEST_NGINX_PWD} ||= $pwd; + +#master_on(); +workers(1); +#log_level('warn'); +#worker_connections(1014); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config + lua_package_path '$TEST_NGINX_PWD/t/lib/?.lua;;'; +--- stream_server_config + content_by_lua_block { + package.loaded["socket"] = ngx.socket + local Memcached = require "Memcached" + Memcached.socket = ngx.socket + + local memc = Memcached.Connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + + memc:set("some_key", "hello 1234") + local data = memc:get("some_key") + ngx.say("some_key: ", data) + } +--- stream_response +some_key: hello 1234 +--- no_error_log +[error] + + + +=== TEST 2: raw memcached +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;;';" +--- stream_server_config + content_by_lua_block { + local memcached = require "resty.memcached" + local memc, err = memcached.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + + local ok, err = memc:set("some_key", "hello 1234") + if not ok then + ngx.log(ngx.ERR, "failed to set some_key: ", err) + ngx.exit(500) + end + + local data, err = memc:get("some_key") + if not data and err then + ngx.log(ngx.ERR, "failed to get some_key: ", err) + ngx.exit(500) + end + + ngx.say("some_key: ", data) + + local res, err = memc:set_keepalive() + if not res then + ngx.say("failed to set keepalive: ", err) + return + end + } +--- user_files +>>> resty/memcached.lua +module("resty.memcached", package.seeall) + +local mt = { __index = resty.memcached } +local sub = string.sub +local escape_uri = ngx.escape_uri +local socket_connect = ngx.socket.connect +local match = string.match + +function connect(...) + local sock, err = socket_connect(...) + return setmetatable({ sock = sock }, mt) +end + +function get(self, key) + local cmd = "get " .. escape_uri(key) .. "\r\n" + local bytes, err = self.sock:send(cmd) + if not bytes then + return nil, err + end + + local line, err = self.sock:receive() + if line == 'END' then + return nil, nil + end + + local flags, len = match(line, [[^VALUE %S+ (%d+) (%d+)]]) + if not flags then + return nil, "bad response: " .. line + end + + print("size: ", size, ", flags: ", len) + + local data, err = self.sock:receive(len) + if not data then + return nil, err + end + + line, err = self.sock:receive(2) -- discard the trailing CRLF + if not line then + return nil, nil, "failed to receive CRLF: " .. (err or "") + end + + line, err = self.sock:receive() -- discard "END\r\n" + if not line then + return nil, nil, "failed to receive END CRLF: " .. (err or "") + end + + return data +end + +function set(self, key, value, exptime, flags) + if not exptime then + exptime = 0 + end + + if not flags then + flags = 0 + end + + local cmd = table.concat({"set ", escape_uri(key), " ", flags, " ", exptime, " ", #value, "\r\n", value, "\r\n"}, "") + + local bytes, err = self.sock:send(cmd) + if not bytes then + return nil, err + end + + local data, err = self.sock:receive() + if sub(data, 1, 6) == "STORED" then + return true + end + + return false, err +end + +function set_keepalive(self) + return self.sock:setkeepalive(0, 100) +end +--- stream_response +some_key: hello 1234 +--- no_error_log +[error] +--- error_log +lua reuse free buf memory diff --git a/src/deps/src/stream-lua-nginx-module/t/061-lua-redis.t b/src/deps/src/stream-lua-nginx-module/t/061-lua-redis.t new file mode 100644 index 000000000..15a2554c3 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/061-lua-redis.t @@ -0,0 +1,176 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +$ENV{TEST_NGINX_REDIS_PORT} ||= 6379; + +#log_level "warn"; +#worker_connections(1024); +#master_on(); + +my $pwd = `pwd`; +chomp $pwd; +$ENV{TEST_NGINX_PWD} ||= $pwd; + +our $LuaCpath = $ENV{LUA_CPATH} || + '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;'; + +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config + lua_package_path '$TEST_NGINX_PWD/t/lib/?.lua;;'; +--- stream_server_config + content_by_lua_block { + package.loaded["socket"] = ngx.socket + local Redis = require "Redis" + + local redis = Redis.connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + + redis:set("some_key", "hello 1234") + local data = redis:get("some_key") + ngx.say("some_key: ", data) + } +--- stream_response +some_key: hello 1234 +--- no_error_log +[error] + + + +=== TEST 2: coroutine-based pub/sub +--- stream_config eval +qq{ + lua_package_path '\$TEST_NGINX_PWD/t/lib/?.lua;;'; + lua_package_cpath '$::LuaCpath'; +} +--- stream_server_config + content_by_lua_block { + package.loaded["socket"] = ngx.socket + local Redis = require "Redis" + + local ljson = require "ljson" + + local r1 = Redis.connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + + local r2 = Redis.connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + + local loop = r2:pubsub({ subscribe = "foo" }) + local msg, abort = loop() + ngx.say("msg type: ", type(msg)) + ngx.say("abort: ", type(abort)) + + if msg then + ngx.say("msg: ", ljson.encode(msg)) + end + + for i = 1, 3 do + r1:publish("foo", "test " .. i) + msg, abort = loop() + if msg then + ngx.say("msg: ", ljson.encode(msg)) + end + ngx.say("abort: ", type(abort)) + end + + abort() + + msg, abort = loop() + ngx.say("msg type: ", type(msg)) + } +--- stap2 +global ids, cur + +function gen_id(k) { + if (ids[k]) return ids[k] + ids[k] = ++cur + return cur +} + +F(ngx_http_handler) { + delete ids + cur = 0 +} + +/* +probe process("/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2").function("lua_yield") { + id = gen_id($L) + printf("raw lua yield %d\n", id) + #print_ubacktrace() +} + +probe process("/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2").function("lua_resume") { + id = gen_id($L) + printf("raw lua resume %d\n", id) +} +*/ + +/* +F(ngx_http_lua_run_thread) { + id = gen_id($ctx->cur_co) + printf("run thread %d\n", id) +} +*/ + +M(http-lua-user-coroutine-resume) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("resume %x in %x\n", c, p) +} + +M(http-lua-entry-coroutine-yield) { + println("entry coroutine yield") +} + +F(ngx_http_lua_coroutine_yield) { + printf("yield %x\n", gen_id($L)) +} + +/* +F(ngx_http_lua_coroutine_resume) { + printf("resume %x\n", gen_id($L)) +} +*/ + +M(http-lua-user-coroutine-yield) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("yield %x in %x\n", c, p) +} + +F(ngx_http_lua_atpanic) { + printf("lua atpanic(%d):", gen_id($L)) + print_ubacktrace(); +} + +M(http-lua-user-coroutine-create) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("create %x in %x\n", c, p) +} + +F(ngx_http_lua_ngx_exec) { println("exec") } + +F(ngx_http_lua_ngx_exit) { println("exit") } + +--- stream_response +msg type: table +abort: function +msg: {"channel":"foo","kind":"subscribe","payload":1} +msg: {"channel":"foo","kind":"message","payload":"test 1"} +abort: function +msg: {"channel":"foo","kind":"message","payload":"test 2"} +abort: function +msg: {"channel":"foo","kind":"message","payload":"test 3"} +abort: function +msg type: nil +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/062-count.t b/src/deps/src/stream-lua-nginx-module/t/062-count.t new file mode 100644 index 000000000..255ad4f1a --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/062-count.t @@ -0,0 +1,300 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(4); +#log_level('warn'); +no_root_location(); + +#repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +#$ENV{LUA_CPATH} = "/usr/local/openresty/lualib/?.so;" . $ENV{LUA_CPATH}; + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: entries under ngx. (content by lua) +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx) do + n = n + 1 + end + ngx.say("ngx: ", n) + } +--- stream_response +ngx: 53 +--- no_error_log +[error] + + + +=== TEST 2: entries under ngx.req (content by lua) +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.req) do + n = n + 1 + end + -- ngx.req.socket + -- ngx.req.start_time + ngx.say("n = ", n) + } +--- stream_response +n = 2 +--- no_error_log +[error] + + + +=== TEST 3: entries under ngx.socket +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.socket) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 4 +--- no_error_log +[error] + + + +=== TEST 4: entries under ngx._tcp_meta +--- SKIP +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx._tcp_meta) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 10 +--- no_error_log +[error] + + + +=== TEST 5: entries under the metatable of req sockets +--- stream_server_config + content_by_lua_block { + local n = 0 + local sock, err = ngx.req.socket() + if not sock then + ngx.say("failed to get the request socket: ", err) + end + + for k, v in pairs(getmetatable(sock)) do + print("key: ", k) + n = n + 1 + end + assert(ngx.say("n = ", n)) + } +--- stream_response +n = 9 +--- no_error_log +[error] + + + +=== TEST 6: shdict metatable +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local mt = dogs.__index + local n = 0 + for k, v in pairs(mt) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 22 +--- no_error_log +[error] + + + +=== TEST 7: entries under ngx.timer +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.timer) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 4 +--- no_error_log +[error] + + + +=== TEST 8: entries under ngx.config +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.config) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 6 +--- no_error_log +[error] + + + +=== TEST 9: entries under ngx.re +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.re) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 5 +--- no_error_log +[error] + + + +=== TEST 10: entries under coroutine. (content by lua) +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(coroutine) do + n = n + 1 + end + ngx.say("coroutine: ", n) + } +--- stap2 +global c +probe process("$LIBLUA_PATH").function("rehashtab") { + c++ + printf("rehash: %d\n", c) +} +--- stap_out2 +3 +--- stream_response +coroutine: 16 +--- no_error_log +[error] + + + +=== TEST 11: entries under ngx.thread. (content by lua) +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.thread) do + n = n + 1 + end + ngx.say("thread: ", n) + } +--- stap2 +global c +probe process("$LIBLUA_PATH").function("rehashtab") { + c++ + printf("rehash: %d\n", c) +} +--- stap_out2 +--- stream_response +thread: 3 +--- no_error_log +[error] + + + +=== TEST 12: entries under ngx.worker +--- stream_server_config + content_by_lua_block { + local n = 0 + for k, v in pairs(ngx.worker) do + n = n + 1 + end + ngx.say("worker: ", n) + } +--- stream_response +worker: 5 +--- no_error_log +[error] + + + +=== TEST 13: entries under the metatable of tcp sockets +--- stream_server_config + content_by_lua_block { + local n = 0 + local sock = ngx.socket.tcp() + for k, v in pairs(getmetatable(sock)) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 14 +--- no_error_log +[error] + + + +=== TEST 14: entries under the metatable of udp sockets +--- stream_server_config + content_by_lua_block { + local n = 0 + local sock = ngx.socket.udp() + for k, v in pairs(getmetatable(sock)) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 6 +--- no_error_log +[error] + + + +=== TEST 15: entries under the metatable of req raw sockets +--- stream_server_config + content_by_lua_block { + local n = 0 + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + for k, v in pairs(getmetatable(sock)) do + n = n + 1 + end + + local ok, err = sock:send("n = " .. n .. "\n") + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return + end + } +--- stream_response +n = 9 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/064-pcall.t b/src/deps/src/stream-lua-nginx-module/t/064-pcall.t new file mode 100644 index 000000000..5168792be --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/064-pcall.t @@ -0,0 +1,98 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +worker_connections(1014); +#master_on(); +#workers(4); +#log_level('warn'); +no_root_location(); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +#$ENV{LUA_CPATH} = "/usr/local/openresty/lualib/?.so;" . $ENV{LUA_CPATH}; + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: pcall works +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + function f(a, b) + if a == 0 and b == 0 then + error("zero error") + end + + return 23, "hello", true + end + + local res = {pcall(f, 0, 0)} + ngx.say("res len: ", #res) + ngx.say("res: ", unpack(res)) + + res = {pcall(f, 0)} + ngx.say("res len: ", #res) + ngx.say("res: ", unpack(res)) + } +--- stream_response eval +qr/^res len: 2 +res: falsecontent_by_lua\(nginx\.conf:\d+\):4: zero error +res len: 4 +res: true23hellotrue +$/s +--- no_error_log +[error] + + + +=== TEST 2: xpcall works +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + function f(a, b) + if a == 0 and b == 0 then + error("zero error") + end + + return 23, "hello", true + end + + function g() + return f(0, 0) + end + + function h() + return f(0) + end + + function err(...) + ngx.say("error handler called: ", ...) + return "this is the new err" + end + + local res = {xpcall(g, err)} + ngx.say("res len: ", #res) + ngx.say("res: ", unpack(res)) + + res = {xpcall(h, err)} + ngx.say("res len: ", #res) + ngx.say("res: ", unpack(res)) + } +--- stream_response eval +qr/^error handler called: content_by_lua\(nginx\.conf:\d+\):4: zero error +res len: 2 +res: falsethis is the new err +res len: 4 +res: true23hellotrue +$/ + +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/065-tcp-socket-timeout.t b/src/deps/src/stream-lua-nginx-module/t/065-tcp-socket-timeout.t new file mode 100644 index 000000000..3e757e96b --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/065-tcp-socket-timeout.t @@ -0,0 +1,872 @@ +# 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 ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'get helloworld'; +} + +use Test::Nginx::Socket::Lua::Stream; +use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 + 9); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +log_level("debug"); +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: lua_socket_connect_timeout only +--- stream_server_config + lua_socket_connect_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 100 +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 2: sock:settimeout() overrides lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 60s; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(150) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 150 +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 3: sock:settimeout(nil) does not override lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 102ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(nil) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 102 +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 + + + +=== TEST 4: sock:settimeout(0) does not override lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 102ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(0) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- stream_response +failed to connect: timeout +--- error_log +lua tcp socket connect timeout: 102 +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 5: -1 is bad timeout value +--- stream_server_config + lua_socket_connect_timeout 102ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(-1) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + } +--- error_log +bad timeout value +finalize stream request: 500 + + + +=== TEST 6: lua_socket_read_timeout only +--- stream_server_config + lua_socket_read_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket read timeout: 100 +lua tcp socket connect timeout: 60000 +lua tcp socket read timed out + + + +=== TEST 7: sock:settimeout() overrides lua_socket_read_timeout +--- stream_server_config + lua_socket_read_timeout 60s; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(150) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket read timeout: 150 +lua tcp socket read timed out + + + +=== TEST 8: sock:settimeout(nil) does not override lua_socket_read_timeout +--- stream_server_config + lua_socket_read_timeout 102ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(nil) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket read timeout: 102 +lua tcp socket read timed out + + + +=== TEST 9: sock:settimeout(0) does not override lua_socket_read_timeout +--- stream_server_config + lua_socket_read_timeout 102ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(0) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + + } +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket read timeout: 102 +lua tcp socket read timed out + + + +=== TEST 10: -1 is bad timeout value +--- stream_server_config + lua_socket_read_timeout 102ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(-1) + + local line + line, err = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } +--- error_log +bad timeout value +finalize stream request: 500 + + + +=== TEST 11: lua_socket_send_timeout only +--- stream_server_config + lua_socket_send_timeout 100ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- stap2 +global active = 0 +F(ngx_http_lua_socket_send) { + active = 1 + println(probefunc()) +} +probe syscall.send, + syscall.sendto, + syscall.writev +{ + if (active && pid() == target()) { + println(probefunc()) + } +} +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket send timeout: 100 +lua tcp socket connect timeout: 60000 +lua tcp socket write timed out + + + +=== TEST 12: sock:settimeout() overrides lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 60s; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(150) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 150 +lua tcp socket write timed out + + + +=== TEST 13: sock:settimeout(nil) does not override lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 102ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(nil) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 102 +lua tcp socket write timed out + + + +=== TEST 14: sock:settimeout(0) does not override lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 102ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(0) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- stream_response +connected: 1 +failed to send: timeout +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 102 +lua tcp socket write timed out + + + +=== TEST 15: sock:settimeout(-1) does not override lua_socket_send_timeout +--- stream_server_config + lua_socket_send_timeout 102ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(-1) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- error_log +bad timeout value +finalize stream request: 500 + + + +=== TEST 16: exit in user thread (entry thread is still pending on tcpsock:send) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + sock:settimeout(12000) + + local bytes, ok = sock:send("get helloworld!") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_coctx_cleanup) { + println("lua tcp socket cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 12000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua tcp socket cleanup +delete timer 12000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 17: re-connect after timed out +--- stream_server_config + lua_socket_connect_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("1: failed to connect: ", err) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("2: failed to connect: ", err) + return + end + + ngx.say("2: connected: ", ok) + return + end + + ngx.say("1: connected: ", ok) + } +--- stream_response +1: failed to connect: timeout +2: connected: 1 +--- error_log +lua tcp socket connect timeout: 100 +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 +--- timeout: 10 + + + +=== TEST 18: re-send on the same object after a send timeout happens +--- stream_server_config + #lua_socket_send_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(100) + + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + bytes, err = sock:send("blah") + if not bytes then + ngx.say("failed to send again: ", err) + end + end + } +--- stap2 +global active = 0 +F(ngx_http_lua_socket_send) { + active = 1 + println(probefunc()) +} +probe syscall.send, + syscall.sendto, + syscall.writev +{ + if (active && pid() == target()) { + println(probefunc()) + } +} +--- stream_response +connected: 1 +failed to send: timeout +failed to send again: closed +--- error_log +lua tcp socket send timeout: 100 +lua tcp socket connect timeout: 60000 +lua tcp socket write timed out + + + +=== TEST 19: abort when upstream sockets pending on writes +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(100) + ngx.thread.spawn(function () ngx.sleep(0.001) ngx.say("done") ngx.exit(200) end) + local bytes + bytes, err = sock:send("get helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- stap2 +global active = 0 +F(ngx_http_lua_socket_send) { + active = 1 + println(probefunc()) +} +probe syscall.send, + syscall.sendto, + syscall.writev +{ + if (active && pid() == target()) { + println(probefunc()) + } +} +--- stream_response +connected: 1 +done +--- error_log +lua tcp socket send timeout: 100 +lua tcp socket connect timeout: 60000 +--- no_error_log +lua tcp socket write timed out + + + +=== TEST 20: abort when downstream socket pending on writes +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + #lua_lingering_timeout 10ms; + + content_by_lua_block { + ngx.flush(true) + local sock, err = ngx.req.socket(true) + if not sock then + ngx.say("failed to acquire the req socket: ", err) + return + end + + sock:settimeout(100) + ngx.thread.spawn(function () + ngx.sleep(0.001) + ngx.log(ngx.WARN, "quitting request now") + ngx.exit(200) + end) + local bytes + bytes, err = sock:send("e\r\nget helloworld!") + if bytes then + ngx.say("sent: ", bytes) + else + ngx.say("failed to send: ", err) + end + } +--- stap2 +global active = 0 +F(ngx_http_lua_socket_send) { + active = 1 + println(probefunc()) +} +probe syscall.send, + syscall.sendto, + syscall.writev +{ + if (active && pid() == target()) { + println(probefunc()) + } +} +--- stream_response_like chomp +^received [1-9]\d* bytes of response data\.$ +--- log_stream_response +--- error_log +stream lua tcp socket send timeout: 100 +quitting request now +--- no_error_log +lua tcp socket write timed out +[alert] + + + +=== TEST 21: read timeout on receive(N) +--- stream_server_config + lua_socket_read_timeout 100ms; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(10) + + local line + line, err = sock:receive(3) + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive: ", err) + end + } +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua tcp socket read timeout: 10 +lua tcp socket connect timeout: 60000 +lua tcp socket read timed out + + + +=== TEST 22: concurrent operations while writing +--- stream_server_config + lua_socket_log_errors off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ready = false + + local function f() + while not ready do + ngx.sleep(0.001) + end + + local bytes, err = sock:send("flush_all") + ngx.say("send: ", bytes, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + + local ok, err = sock:getreusedtimes() + ngx.say("getreusedtimes: ", ok, " ", err) + + local ok, err = sock:setkeepalive() + ngx.say("setkeepalive: ", ok, " ", err) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + + sock:settimeout(1) + local res, err = sock:receive(1) + ngx.say("receive: ", res, " ", err) + end + + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + + ready = true + + sock:settimeout(300) + local bytes, err = sock:send("get helloworld!") + if not bytes then + ngx.say("send failed: ", err) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connect: 1 nil +send: nil socket busy writing +close: nil socket busy writing +getreusedtimes: 0 nil +setkeepalive: nil socket busy writing +connect: nil socket busy writing +receive: nil timeout +send failed: timeout +close: 1 nil + +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/066-socket-receiveuntil.t b/src/deps/src/stream-lua-nginx-module/t/066-socket-receiveuntil.t new file mode 100644 index 000000000..081fe2bcf --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/066-socket-receiveuntil.t @@ -0,0 +1,1238 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; + +run_tests(); + +__DATA__ + +=== TEST 1: memcached read lines +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local readline = sock:receiveuntil("\r\n") + local line, err, part = readline() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connected: 1 +request sent: 11 +received: OK +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 2: http read lines +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local readline = sock:receiveuntil("\r\n") + local line, err, part + + for i = 1, 7 do + line, err, part = readline() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: HTTP/1.1 200 OK +read: Server: nginx +read: Content-Type: text/plain +read: Content-Length: 4 +read: Connection: close +read: +failed to read a line: closed [foo +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 3: http read all the headers in a single run +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local line, err, part + + for i = 1, 2 do + line, err, part = read_headers() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: HTTP/1.1 200 OK\r +Server: nginx\r +Content-Type: text/plain\r +Content-Length: 4\r +Connection: close +failed to read a line: closed [foo +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 4: ambiguous boundary patterns (abcabd) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabd") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("abcabcabd") } + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abc +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 5: ambiguous boundary patterns (aa) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aa") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("abcabcaad") } + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabc +failed to read a line: closed [d +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 6: ambiguous boundary patterns (aaa) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aaa") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abaabcaaaef; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abaabc +failed to read a line: closed [ef +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 7: ambiguous boundary patterns (aaaaad) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aaaaad") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo baaaaaaaaeaaaaaaadf; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: baaaaaaaaeaa +failed to read a line: closed [f +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 8: ambiguous boundary patterns (aaaaad), small buffer, 2 bytes +--- stream_server_config + lua_socket_buffer_size 2; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aaaaad") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo baaaaaaaaeaaaaaaadf; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: baaaaaaaaeaa +failed to read a line: closed [f +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 9: ambiguous boundary patterns (aaaaad), small buffer, 1 byte +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aaaaad") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo baaaaaaaaeaaaaaaadf; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: baaaaaaaaeaa +failed to read a line: closed [f +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 10: ambiguous boundary patterns (abcabdabcabe) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabdabcabe") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabdabcabdabcabe; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabd +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 11: ambiguous boundary patterns (abcabdabcabe 2) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabdabcabe") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabdabcabcabdabcabe; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabdabc +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 12: ambiguous boundary patterns (abcabdabcabe 3) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabdabcabe") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcabdabcabe; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abc +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 13: ambiguous boundary patterns (abcabdabcabe 4) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabdabcabe") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo ababcabdabcabe; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: ab +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 14: ambiguous boundary patterns (--abc) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo -- ----abc; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: -- +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 15: ambiguous boundary patterns (--abc) +--- stream_server_config + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 7 do + line, err, part = reader(4) + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo "hello, world ----abc"; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: hell +read: o, w +read: orld +read: -- +read: +failed to read a line: nil [nil] +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 16: ambiguous boundary patterns (--abc), small buffer +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 7 do + line, err, part = reader(4) + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo "hello, world ----abc"; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: hell +read: o, w +read: orld +read: -- +read: +failed to read a line: nil [nil] +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 17: ambiguous boundary patterns (--abc), small buffer, mixed by other reading calls +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 7 do + line, err, part = reader(4) + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a chunk: ", err, " [", part, "]") + end + + local data, err, part = sock:receive(1) + if not data then + ngx.say("failed to read a byte: ", err, " [", part, "]") + break + else + ngx.say("read one byte: ", data) + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo "hello, world ----abc"; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: hell +read one byte: o +read: , wo +read one byte: r +read: ld - +read one byte: - +read: +read one byte: + +failed to read a chunk: nil [nil] +failed to read a byte: closed [] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 18: ambiguous boundary patterns (abcabd), small buffer +--- stream_server_config + lua_socket_buffer_size 3; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabd") + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcabd; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abc +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 19: long patterns +this exposed a memory leak in receiveuntil +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + if not sock then + ngx.say("failed to get req socket: ", err) + return + end + local reader, err = sock:receiveuntil("------------------------------------------- abcdefghijklmnopqrstuvwxyz") + if not reader then + ngx.say("failed to get reader: ", err) + return + end + ngx.say("ok") + } +--- stream_response +ok +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/067-req-socket.t b/src/deps/src/stream-lua-nginx-module/t/067-req-socket.t new file mode 100644 index 000000000..cc641eea6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/067-req-socket.t @@ -0,0 +1,1002 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 9); + +our $HtmlDir = html_dir; + +#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + + for i = 1, 3 do + local data, err, part = sock:receive(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + end + } +--- stream_request chomp +hello world! my +--- stream_response +got the request socket +received: hello +received: worl +received: d! my +--- no_error_log +[error] + + + +=== TEST 2: multipart rfc sample (just partial streaming) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + + local boundary = "simple boundary" + + local read_to_boundary = sock:receiveuntil("\r\n--" .. boundary) + local read_line = sock:receiveuntil("\r\n") + + local data, err, part = read_to_boundary() + if data then + ngx.say("preamble: [" .. data .. "]") + else + ngx.say("failed to read the first boundary: ", err) + return + end + + sock:settimeout(1000) + + local i = 1 + while true do + local line, err = read_line() + + if not line then + ngx.say("failed to read post-boundary line: ", err) + return + end + + m = ngx.re.match(line, "--$", "jo") + if m then + ngx.say("found the end of the stream") + sock:receive() -- consume the epiloque. + return + end + + while true do + local line, err = read_line() + if not line then + ngx.say("failed to read part ", i, " header: ", err) + return + end + + if line == "" then + -- the header part completes + break + end + + ngx.say("part ", i, " header: [", line, "]") + end + + local data, err, part = read_to_boundary() + if data then + ngx.say("part ", i, " body: [" .. data .. "]") + else + ngx.say("failed to read part ", i + 1, " boundary: ", err) + return + end + + i = i + 1 + end + } +--- stream_request eval +"This is the preamble. It is to be ignored, though it +is a handy place for mail composers to include an +explanatory note to non-MIME compliant readers.\r +--simple boundary\r +\r +This is implicitly typed plain ASCII text. +It does NOT end with a linebreak.\r +--simple boundary\r +Content-type: text/plain; charset=us-ascii\r +\r +This is explicitly typed plain ASCII text. +It DOES end with a linebreak. +\r +--simple boundary--\r +This is the epilogue. It is also to be ignored. +" +--- stream_response +got the request socket +preamble: [This is the preamble. It is to be ignored, though it +is a handy place for mail composers to include an +explanatory note to non-MIME compliant readers.] +part 1 body: [This is implicitly typed plain ASCII text. +It does NOT end with a linebreak.] +part 2 header: [Content-type: text/plain; charset=us-ascii] +part 2 body: [This is explicitly typed plain ASCII text. +It DOES end with a linebreak. +] +found the end of the stream +--- no_error_log +[error] + + + +=== TEST 3: multipart rfc sample (completely streaming) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + + local boundary = "simple boundary" + + local read_to_boundary = sock:receiveuntil("\r\n--" .. boundary) + local read_line = sock:receiveuntil("\r\n") + + local preamble = "" + while true do + local data, err, part = read_to_boundary(1) + if data then + preamble = preamble .. data + + elseif not err then + break + + else + ngx.say("failed to read the first boundary: ", err) + return + end + end + + ngx.say("preamble: [" .. preamble .. "]") + + local i = 1 + while true do + local line, err = read_line(50) + + if not line and err then + ngx.say("1: failed to read post-boundary line: ", err) + return + end + + if line then + local dummy + dummy, err = read_line(1) + if err then + ngx.say("2: failed to read post-boundary line: ", err) + return + end + + if dummy then + ngx.say("bad post-boundary line: ", dummy) + return + end + + m = ngx.re.match(line, "--$", "jo") + if m then + ngx.say("found the end of the stream") + sock:receive() -- consume the epiloque. + return + end + end + + while true do + local line, err = read_line(50) + if not line and err then + ngx.say("failed to read part ", i, " header: ", err) + return + end + + if line then + local line, err = read_line(1) + if line or err then + ngx.say("error") + return + end + end + + if line == "" then + -- the header part completes + break + end + + ngx.say("part ", i, " header: [", line, "]") + end + + local body = "" + + while true do + local data, err, part = read_to_boundary(1) + if data then + body = body .. data + + elseif err then + ngx.say("failed to read part ", i + 1, " boundary: ", err) + return + + else + break + end + end + + ngx.say("part ", i, " body: [" .. body .. "]") + + i = i + 1 + end + } +--- stream_request eval +"This is the preamble. It is to be ignored, though it +is a handy place for mail composers to include an +explanatory note to non-MIME compliant readers.\r +--simple boundary\r +\r +This is implicitly typed plain ASCII text. +It does NOT end with a linebreak.\r +--simple boundary\r +Content-type: text/plain; charset=us-ascii\r +\r +This is explicitly typed plain ASCII text. +It DOES end with a linebreak. +\r +--simple boundary--\r +This is the epilogue. It is also to be ignored. +" +--- stream_response +got the request socket +preamble: [This is the preamble. It is to be ignored, though it +is a handy place for mail composers to include an +explanatory note to non-MIME compliant readers.] +part 1 body: [This is implicitly typed plain ASCII text. +It does NOT end with a linebreak.] +part 2 header: [Content-type: text/plain; charset=us-ascii] +part 2 body: [This is explicitly typed plain ASCII text. +It DOES end with a linebreak. +] +found the end of the stream +--- no_error_log +[error] + + + +=== TEST 4: attempt to use the req socket across request boundary +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go() + ngx.say("done") + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock, err + +function go() + if not sock then + sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + else + for i = 1, 3 do + local data, err, part = sock:receive(5) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + end + end +end +--- stream_response_like +(?:got the request socket +|failed to receive: closed [d] +)?done +--- no_error_log +[alert] + + + +=== TEST 5: receive until on request_body - receiveuntil(1) on the last byte of the body +See https://groups.google.com/group/openresty/browse_thread/thread/43cf01da3c681aba for details +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go() + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go() + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + local data, err, part = sock:receive(56) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + + local discard_line = sock:receiveuntil('\r\n') + + local data, err, part = discard_line(8192) + if data then + ngx.say("received len: ", #data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + + local data, err, part = discard_line(1) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + + ngx.say("done") + ngx.flush(true) + + local res, err = sock:shutdown('send') + if not res then + ngx.log(ngx.ERR, "server: failed to shutdown: ", err) + return + end +end +--- stream_request +-----------------------------820127721219505131303151179################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################$ +--- stream_response +got the request socket +received: -----------------------------820127721219505131303151179 +received len: 8192 +received: $ +done +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 6: pipelined POST requests +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + test.go() + ngx.say("done") + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go() + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + return + end + + for i = 1, 5 do + local data, err, part = sock:receive(4) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + return + end + end +end +--- stream_request chomp +hello, worldhiya, wo +--- stream_response +got the request socket +received: hell +received: o, w +received: orld +received: hiya +received: , wo +done +--- no_error_log +[error] + + + +=== TEST 7: pipelined requests, big buffer, small steps +--- stream_server_config + lua_socket_buffer_size 5; + content_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + + for i = 1, 11 do + local data, err, part = sock:receive(2) + if data then + ngx.say("received: ", data) + else + ngx.say("failed to receive: ", err, " [", part, "]") + end + end + } +--- stap2 +M(http-lua-req-socket-consume-preread) { + println("preread: ", user_string_n($arg2, $arg3)) +} + +--- stream_request chomp +hello world +hiya globe +--- stream_response +got the request socket +received: he +received: ll +received: o +received: wo +received: rl +received: d + +received: hi +received: ya +received: g +received: lo +received: be +--- no_error_log +[error] + + + +=== TEST 8: failing reread after reading timeout happens +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + + if not sock then + ngx.say("failed to get socket: ", err) + return nil + end + + sock:settimeout(100); + + local data, err, partial = sock:receive(4096) + if err then + ngx.say("err: ", err, ", partial: ", partial) + end + + local data, err, partial = sock:receive(4096) + if err then + ngx.say("err: ", err, ", partial: ", partial) + return + end + } + +--- stream_request chomp +hello +--- stream_response +err: timeout, partial: hello +err: timeout, partial: + +--- error_log +stream lua tcp socket read timed out + + + +=== TEST 9: successful reread after reading timeout happens (receive -> receive) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("POST /back HTTP/1.0\r\nHost: localhost\r\nContent-Length: 1024\r\n\r\nabc") + if not bytes then + ngx.say("failed to send: ", err) + else + ngx.say("sent: ", bytes) + end + + ngx.sleep(0.2) + + local bytes, err = sock:send("hello world") + if not bytes then + ngx.say("failed to send: ", err) + else + ngx.say("sent: ", bytes) + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to receive header: ", err) + return + end + + for i = 1, 2 do + local line, err = sock:receive() + if not line then + ngx.say("failed to receive line: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + location = /back { + lua_socket_log_errors on; + content_by_lua_block { + ngx.send_headers() + ngx.flush(true) + + local sock, err = ngx.req.socket() + + if not sock then + ngx.say("failed to get socket: ", err) + return nil + end + + sock:settimeout(100); + + local data, err, partial = sock:receive(4096) + if err then + ngx.log(ngx.ERR, "err: ", err) + ngx.say("err: ", err, ", partial: ", partial) + else + ngx.say("received: ", data) + end + + ngx.sleep(0.1) + + local data, err, partial = sock:receive(11) + if err then + ngx.say("err: ", err, ", partial: ", partial) + else + ngx.say("received: ", data) + end + } + } + +--- stream_response +sent: 65 +sent: 11 +received: err: timeout, partial: abc +received: received: hello world + +--- error_log +lua tcp socket read timed out + + + +=== TEST 10: successful reread after reading timeout happens (receive -> receiveuntil) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("POST /back HTTP/1.0\r\nHost: localhost\r\nContent-Length: 1024\r\n\r\nabc") + if not bytes then + ngx.say("failed to send: ", err) + else + ngx.say("sent: ", bytes) + end + + ngx.sleep(0.2) + + local bytes, err = sock:send("hello world\n") + if not bytes then + ngx.say("failed to send: ", err) + else + ngx.say("sent: ", bytes) + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to receive header: ", err) + return + end + + for i = 1, 2 do + local line, err = sock:receive() + if not line then + ngx.say("failed to receive line: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + location = /back { + lua_socket_log_errors on; + content_by_lua_block { + ngx.send_headers() + ngx.flush(true) + + local sock, err = ngx.req.socket() + + if not sock then + ngx.say("failed to get socket: ", err) + return nil + end + + sock:settimeout(100); + + local data, err, partial = sock:receive(4096) + if err then + ngx.say("err: ", err, ", partial: ", partial) + else + ngx.say("received: ", data) + end + + ngx.sleep(0.1) + + local reader = sock:receiveuntil("\n") + local data, err, partial = reader() + if err then + ngx.say("err: ", err, ", partial: ", partial) + else + ngx.say("received: ", data) + end + } + } + +--- stream_response +sent: 65 +sent: 12 +received: err: timeout, partial: abc +received: received: hello world + +--- error_log +lua tcp socket read timed out + + + +=== TEST 11: successful reread after reading timeout happens (receiveuntil -> receive) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("POST /back HTTP/1.0\r\nHost: localhost\r\nContent-Length: 1024\r\n\r\nabc") + if not bytes then + ngx.say("failed to send: ", err) + else + ngx.say("sent: ", bytes) + end + + ngx.sleep(0.2) + + local bytes, err = sock:send("hello world\n") + if not bytes then + ngx.say("failed to send: ", err) + else + ngx.say("sent: ", bytes) + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to receive header: ", err) + return + end + + for i = 1, 2 do + local line, err = sock:receive() + if not line then + ngx.say("failed to receive line: ", err) + return + end + ngx.say("received: ", line) + end + } + +--- config + location = /back { + lua_socket_log_errors on; + content_by_lua_block { + ngx.send_headers() + ngx.flush(true) + + local sock, err = ngx.req.socket() + + if not sock then + ngx.say("failed to get socket: ", err) + return nil + end + + sock:settimeout(100); + + local reader = sock:receiveuntil("no-such-terminator") + local data, err, partial = reader() + if not data then + ngx.say("err: ", err, ", partial: ", partial) + else + ngx.say("received: ", data) + end + + ngx.sleep(0.1) + + local data, err, partial = sock:receive() + if err then + ngx.say("err: ", err, ", partial: ", partial) + else + ngx.say("received: ", data) + end + } + } + +--- stream_response +sent: 65 +sent: 12 +received: err: timeout, partial: abc +received: received: hello world + +--- error_log +lua tcp socket read timed out + + + +=== TEST 12: req socket GC'd +--- stream_server_config + content_by_lua_block { + do + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + end + collectgarbage() + ngx.log(ngx.WARN, "GC cycle done") + + ngx.say("done") + } +--- stream_response +got the request socket +done +--- no_error_log +[error] +--- grep_error_log eval: qr/stream lua finalize socket|GC cycle done/ +--- grep_error_log_out +stream lua finalize socket +GC cycle done + + + +=== TEST 13: sanity of raw req socket receive any +--- stream_server_config + + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + ngx.log(ngx.INFO, "Got raw req socket") + local data, err = sock:receiveany(500) + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + ngx.log(ngx.INFO, "Got: ", #data, " bytes") + + local bytes, err = sock:send("1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + } + +--- stream_request: hello +--- stream_response +1: received: hello +--- no_error_log +stream lua socket tcp_nodelay +[error] +--- error_log +Got raw req socket +Got: 5 bytes + + + +=== TEST 14: receiveany small block size for a big size block +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + sock:settimeouts(500, 100, 500) + ngx.sleep(0.2) + ngx.log(ngx.INFO, 'receiveany every 3 bytes per read ...') + local i = 0 + while true do + i = i + 1 + ngx.log(ngx.INFO, 'reading: ', i) + local data, err = sock:receiveany(3) + if not data then + if err == 'closed' then + ngx.log(ngx.INFO, 'client closed') + break + end + if err == 'timeout' then + ngx.log(ngx.INFO, 'client timeout, considered as closed') + break + end + ngx.log(ngx.ERR, 'server receiveany error: ', err) + break + end + if i == 1 then + ngx.log(ngx.INFO, 'send back ok ...') + local ok, err = sock:send("ok\n") + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return + end + end + ngx.log(ngx.INFO, "Time ", i, " - got ", #data, " bytes: ", data) + sock:send("receive: " .. data .. "\n") + end + } + +--- stream_request: hello, stream receiveany! +--- stream_response +ok +receive: hel +receive: lo, +receive: st +receive: rea +receive: m r +receive: ece +receive: ive +receive: any +receive: ! +--- no_error_log +receiveany error: +--- error_log +read timed out +client timeout + + + +=== TEST 15: receiveany with limited, max <= 0 +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if sock == nil then + ngx.log(ngx.ERR, 'raw req socket error: ', err) + return + end + sock:settimeouts(500, 500, 500) + + local function receiveany_log_err(...) + local ok, err = pcall(sock.receiveany, sock, ...) + if not ok then + ngx.log(ngx.ERR, 'failed receiveany ', err) + end + end + + + receiveany_log_err(0) + receiveany_log_err(-1) + receiveany_log_err(100, 200) + receiveany_log_err() + receiveany_log_err(nil) + } +--- error_log +bad argument #2 to '?' (bad max argument) +bad argument #2 to '?' (bad max argument) +expecting 2 arguments (including the object), but got 3 +expecting 2 arguments (including the object), but got 1 +bad argument #2 to '?' (bad max argument) + + + +=== TEST 16: receiveany send data after read side timeout +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if sock == nil then + ngx.log(ngx.ERR, 'failed to get raw req socket', err) + return + end + sock:settimeouts(500, 500, 500) + + local data, err, bytes + while true do + data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.log(ngx.ERR, 'receiveany unexpected err: ', err) + break + end + + data = "send data after read side closed" + bytes, err = sock:send(data) + if not bytes then + ngx.log(ngx.ERR, 'failed to send after error ',err) + end + + break + end + ngx.say(data) + end + + local bytes, err2 = sock:send("send data after read side ") + if not bytes then + ngx.log(ngx.ERR, "failed to send: ", err2) + end + + local bytes, err2 = sock:send(err) + if not bytes then + ngx.log(ngx.ERR, "failed to send: ", err2) + end + } +--- stream_response chomp +send data after read side timeout +--- error_log +receiveany unexpected err: timeout diff --git a/src/deps/src/stream-lua-nginx-module/t/068-socket-keepalive.t b/src/deps/src/stream-lua-nginx-module/t/068-socket-keepalive.t new file mode 100644 index 000000000..1d2669b62 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/068-socket-keepalive.t @@ -0,0 +1,2356 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +plan tests => repeat_each() * (blocks() * 4 + 32); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; +$ENV{TEST_NGINX_REDIS_PORT} ||= 6379; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +log_level('debug'); +no_long_string(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local function go(port) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to setkeepalive: ", err) + return + end + end + + go($TEST_NGINX_MEMCACHED_PORT) + go($TEST_NGINX_MEMCACHED_PORT) + } +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +connected: 1, reused: 1 +request sent: 11 +received: OK +--- error_log eval +qq{ +lua tcp socket get keepalive peer: using connection +lua tcp socket keepalive create connection pool for key "127\.0\.0\.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" +} +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for " +] + + + +=== TEST 2: free up the whole connection pool if no active connections +--- stream_server_config + content_by_lua_block { + local function go(port, keepalive) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + if keepalive then + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to setkeepalive: ", err) + return + end + + else + sock:close() + end + end + + go($TEST_NGINX_MEMCACHED_PORT, true) + go($TEST_NGINX_MEMCACHED_PORT, false) + } +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +connected: 1, reused: 1 +request sent: 11 +received: OK +--- error_log eval +[ +"lua tcp socket get keepalive peer: using connection", +"lua tcp socket keepalive: free connection pool for " +] +--- no_error_log +[error] + + + +=== TEST 3: upstream sockets close prematurely +--- config + location /foo { + server_tokens off; + keepalive_timeout 100ms; + echo foo; + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- error_log eval +[ +"lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for " +] +--- no_error_log +[error] +--- timeout: 3 + + + +=== TEST 4: http keepalive +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log eval +[ +"lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for " +] +--- timeout: 4 + + + +=== TEST 5: lua_socket_keepalive_timeout +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 100ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 100 ms", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 6: lua_socket_pool_size +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 100ms; + lua_socket_pool_size 1; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 100 ms", +qr/lua tcp socket connection pool size: 1\b/] +--- timeout: 4 + + + +=== TEST 7: "lua_socket_keepalive_timeout 0" means unlimited +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 0; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive timeout: unlimited", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 8: setkeepalive(timeout) overrides lua_socket_keepalive_timeout +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 60s; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(123) + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 123 ms", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 9: sock:setkeepalive(timeout, size) overrides lua_socket_pool_size +--- config + location /foo { + server_tokens off; + keepalive_timeout 100ms; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 60s; + lua_socket_pool_size 100; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(101, 25) + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 101 ms", +qr/lua tcp socket connection pool size: 25\b/] +--- timeout: 4 + + + +=== TEST 10: setkeepalive() 'pool_size' should be greater than zero +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not sock then + ngx.say(err) + return + end + + local ok, err = pcall(sock.setkeepalive, sock, 0, 0) + if not ok then + ngx.say(err) + return + end + ngx.say(ok) + } +--- stream_response +bad argument #3 to '?' (bad "pool_size" option value: 0) +--- no_error_log +[error] + + + +=== TEST 11: sock:keepalive_timeout(0) means unlimited +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 1000ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(0) + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive timeout: unlimited", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 12: sanity (uds) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + server_tokens off; + + location /foo { + echo foo; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local function go(path) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:" .. path) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("$TEST_NGINX_HTML_DIR/nginx.sock") + go("$TEST_NGINX_HTML_DIR/nginx.sock") + } +--- stream_response +connected: 1, reused: 0 +request sent: 61 +received response of 119 bytes +connected: 1, reused: 1 +request sent: 61 +received response of 119 bytes +--- error_log eval +[ +"lua tcp socket get keepalive peer: using connection", +'lua tcp socket keepalive create connection pool for key "unix:' +] +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for " +] + + + +=== TEST 13: github issue #108: ngx.locaiton.capture + redis.set_keepalive +--- SKIP: ngx_http_lua only + + + +=== TEST 14: github issue #110: ngx.exit with HTTP_NOT_FOUND causes worker process to exit +--- SKIP: ngx_http_lua only + + + +=== TEST 15: custom pools (different pool for the same host:port) - tcp +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, "A") + go($TEST_NGINX_MEMCACHED_PORT, "B") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 0 +--- error_log +lua tcp socket keepalive create connection pool for key "A" +lua tcp socket keepalive create connection pool for key "B" +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket get keepalive peer: using connection" +] + + + +=== TEST 16: custom pools (same pool for different host:port) - tcp +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, "foo") + go($TEST_NGINX_MEMCACHED_PORT, "foo") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +--- error_log +lua tcp socket keepalive create connection pool for key "foo" +lua tcp socket get keepalive peer: using connection +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +] + + + +=== TEST 17: custom pools (different pool for the same host:port) - unix +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + server_tokens off; + + location /foo { + echo foo; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local function go(pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock", { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("A") + go("B") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 0 +--- error_log +lua tcp socket keepalive create connection pool for key "A" +lua tcp socket keepalive create connection pool for key "B" +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket get keepalive peer: using connection" +] + + + +=== TEST 18: custom pools (same pool for the same path) - unix +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + server_tokens off; + + location /foo { + echo foo; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local function go(pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock", { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("A") + go("A") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +--- error_log +lua tcp socket keepalive create connection pool for key "A" +lua tcp socket get keepalive peer: using connection +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +] + + + +=== TEST 19: numeric pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, 3.14) + go($TEST_NGINX_SERVER_PORT, 3.14) + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +--- error_log +lua tcp socket keepalive create connection pool for key "3.14" +lua tcp socket get keepalive peer: using connection +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +] + + + +=== TEST 20: nil pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, nil) + go($TEST_NGINX_SERVER_PORT, nil) + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 0 +--- no_error_log +[error] + + + +=== TEST 21: (bad) table pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, {}) + go($TEST_NGINX_SERVER_PORT, {}) + } +--- stream_response +--- error_log +bad argument #3 to 'connect' (bad "pool" option type: table) + + + +=== TEST 22: (bad) boolean pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, true) + go($TEST_NGINX_SERVER_PORT, false) + } +--- stream_response +--- error_log +bad argument #3 to 'connect' (bad "pool" option type: boolean) + + + +=== TEST 23: clear the redis store +--- SKIP: ngx_http_lua only + + + +=== TEST 24: bug in send(): clear the chain writer ctx +--- SKIP +--- stream_server_config + content_by_lua_block { + local test = require "test" + local port = ngx.var.port + test.go($TEST_NGINX_REDIS_PORT) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send({}) + if err then + ngx.say("failed to send empty request: ", err) + return + end + + local req = "*2\r\n$3\r\nget\r\n$3\r\ndog\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ngx.say("done") +end +--- stap2 +global active +M(stream-lua-socket-tcp-send-start) { + active = 1 + printf("send [%s] %d\n", text_str(user_string_n($arg3, $arg4)), $arg4) +} +M(stream-lua-socket-tcp-receive-done) { + printf("receive [%s]\n", text_str(user_string_n($arg3, $arg4))) +} +F(ngx_output_chain) { + #printf("ctx->in: %s\n", ngx_chain_dump($ctx->in)) + #printf("ctx->busy: %s\n", ngx_chain_dump($ctx->busy)) + printf("output chain: %s\n", ngx_chain_dump($in)) +} +F(ngx_linux_sendfile_chain) { + printf("linux sendfile chain: %s\n", ngx_chain_dump($in)) +} +F(ngx_chain_writer) { + printf("chain writer ctx out: %p\n", $data) + printf("nginx chain writer: %s\n", ngx_chain_dump($in)) +} +F(ngx_stream_lua_socket_tcp_setkeepalive) { + delete active +} +M(stream-lua-socket-tcp-setkeepalive-buf-unread) { + printf("setkeepalive unread: [%s]\n", text_str(user_string_n($arg3, $arg4))) +} +probe syscall.recvfrom { + if (active && pid() == target()) { + printf("recvfrom(%s)", argstr) + } +} +probe syscall.recvfrom.return { + if (active && pid() == target()) { + printf(" = %s, data [%s]\n", retstr, text_str(user_string_n($ubuf, $size))) + } +} +probe syscall.writev { + if (active && pid() == target()) { + printf("writev(%s)", ngx_iovec_dump($vec, $vlen)) + /* + for (i = 0; i < $vlen; i++) { + printf(" %p [%s]", $vec[i]->iov_base, text_str(user_string_n($vec[i]->iov_base, $vec[i]->iov_len))) + } + */ + } +} +probe syscall.writev.return { + if (active && pid() == target()) { + printf(" = %s\n", retstr) + } +} +--- stream_response +received: $-1 +done +--- no_error_log +[error] + + + +=== TEST 25: setkeepalive() with explicit nil args +--- config + location /foo { + server_tokens off; + keepalive_timeout 100ms; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 100ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, res = reader() + + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(nil, nil) + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- error_log eval +[ +"lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 100 ms", +qr/lua tcp socket connection pool size: 30\b/ +] +--- no_error_log +[error] +--- timeout: 4 + + + +=== TEST 26: conn queuing: connect() verifies the options for connection pool +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + + local function check_opts_for_connect(opts) + local ok, err = pcall(function() + sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT, opts) + end) + if not ok then + ngx.say(err) + else + ngx.say("ok") + end + end + + check_opts_for_connect({pool_size = 'a'}) + check_opts_for_connect({pool_size = 0}) + check_opts_for_connect({backlog = -1}) + check_opts_for_connect({backlog = 0}) + } +--- stream_response_like +.+ 'connect' \(bad "pool_size" option type: string\) +.+ 'connect' \(bad "pool_size" option value: 0\) +.+ 'connect' \(bad "backlog" option value: -1\) +ok +--- no_error_log +[error] + + + +=== TEST 27: conn queuing: connect() can specify 'pool_size' which overrides setkeepalive() +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT; + + local function go() + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool_size = 1 }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive(0, 20) + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + -- reuse ok + go() + go() + + local sock1 = ngx.socket.connect("127.0.0.1", port) + local sock2 = ngx.socket.connect("127.0.0.1", port) + local ok, err = sock1:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + local ok, err = sock2:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + + -- the pool_size is 1 instead of 20 + sock1 = ngx.socket.connect("127.0.0.1", port) + sock2 = ngx.socket.connect("127.0.0.1", port) + ngx.say("reused: ", sock1:getreusedtimes()) + ngx.say("reused: ", sock2:getreusedtimes()) + sock1:setkeepalive(0, 20) + sock2:setkeepalive(0, 20) + } +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +connected: 1, reused: 1 +request sent: 11 +received: OK +reused: 1 +reused: 0 +--- error_log eval +[ +qq{lua tcp socket keepalive create connection pool for key "127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}"}, +"lua tcp socket connection pool size: 1", +] +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket connection pool size: 20" +] + + + +=== TEST 28: conn queuing: connect() can specify 'pool_size' for unix domain socket +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + } +--- stream_server_config + content_by_lua_block { + local path = "unix:" .. "$TEST_NGINX_HTML_DIR/nginx.sock"; + + local function go() + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect(path, { pool_size = 1 }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive(0, 20) + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go() + go() + + local sock1 = ngx.socket.connect(path) + local sock2 = ngx.socket.connect(path) + local ok, err = sock1:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + local ok, err = sock2:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + + -- the pool_size is 1 instead of 20 + sock1 = ngx.socket.connect(path) + sock2 = ngx.socket.connect(path) + ngx.say("reused: ", sock1:getreusedtimes()) + ngx.say("reused: ", sock2:getreusedtimes()) + sock1:setkeepalive(0, 20) + sock2:setkeepalive(0, 20) + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +reused: 1 +reused: 0 +--- error_log eval +[ +"lua tcp socket get keepalive peer: using connection", +'lua tcp socket keepalive create connection pool for key "unix:', +"lua tcp socket connection pool size: 1", +] +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket connection pool size: 20" +] + + + +=== TEST 29: conn queuing: connect() can specify 'pool_size' for custom pool +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + + local function go(pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { + pool = pool, + pool_size = 1 + }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", pool, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive(0, 20) + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("A") + go("B") + go("A") + go("B") + + local sock1 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + local sock2 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + local ok, err = sock1:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + local ok, err = sock2:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + + -- the pool_size is 1 instead of 20 + sock1 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + sock2 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + ngx.say("reused: ", sock1:getreusedtimes()) + ngx.say("reused: ", sock2:getreusedtimes()) + sock1:setkeepalive(0, 20) + sock2:setkeepalive(0, 20) + } +--- stream_response +connected: A, reused: 0 +connected: B, reused: 0 +connected: A, reused: 1 +connected: B, reused: 1 +reused: 1 +reused: 0 +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket connection pool size: 20" +] +--- error_log eval +[ +qq{lua tcp socket keepalive create connection pool for key "A"}, +qq{lua tcp socket keepalive create connection pool for key "B"}, +"lua tcp socket connection pool size: 1" +] + + + +=== TEST 30: conn queuing: connect() uses lua_socket_pool_size as default if 'backlog' is given +--- stream_server_config + lua_socket_pool_size 1234; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local sock, err = ngx.socket.connect("127.0.0.1", port, { backlog = 0 }) + if not sock then + ngx.say(err) + else + ngx.say("ok") + end + } +--- stream_response +ok +--- error_log +lua tcp socket connection pool size: 1234 +--- no_error_log +[error] + + + +=== TEST 31: conn queuing: more connect operations than 'backlog' size +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + + local opts = {pool_size = 2, backlog = 0} + local sock = ngx.socket.connect("127.0.0.1", port, opts) + local not_reused_socket, err = ngx.socket.connect("127.0.0.1", port, opts) + if not not_reused_socket then + ngx.say(err) + return + end + -- burst + local ok, err = ngx.socket.connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say(err) + return + end + + ok, err = sock:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + ngx.say("reused: ", sock:getreusedtimes()) + -- both queue and pool is full + ok, err = ngx.socket.connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- stream_response +too many waiting connect operations +reused: 1 +too many waiting connect operations +--- no_error_log +[error] + + + +=== TEST 32: conn queuing: once 'pool_size' is reached and pool has 'backlog' +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 2, backlog = 2} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0, function(premature) + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock2 then + ngx.log(ngx.ERR, err) + return + end + + ngx.log(ngx.WARN, "start to handle timer") + ngx.sleep(0.1) + sock2:close() + -- resume connect operation + ngx.log(ngx.WARN, "continue to handle timer") + end) + + ngx.sleep(0.05) + ngx.log(ngx.WARN, "start to handle cosocket") + local sock3, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock3 then + ngx.say(err) + return + end + ngx.log(ngx.WARN, "continue to handle cosocket") + + local req = "flush_all\r\n" + local bytes, err = sock3:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock3:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock3:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + ngx.say("setkeepalive: OK") + } +--- stream_response +request sent: 11 +received: OK +setkeepalive: OK +--- no_error_log +[error] +--- error_log +lua tcp socket queue connect operation for connection pool "127.0.0.1 +--- grep_error_log eval: qr/(start|continue) to handle \w+/ +--- grep_error_log_out +start to handle timer +start to handle cosocket +continue to handle timer +continue to handle cosocket + + + +=== TEST 33: conn queuing: do not count failed connect operations +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 0} + + local sock = ngx.socket.tcp() + sock:settimeouts(100, 3000, 3000) + local ok, err = sock:connect("127.0.0.2", 12345, opts) + if not ok then + ngx.say(err) + end + + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.say(err) + end + ngx.say("ok") + } +--- error_log +lua tcp socket connect timed out, when connecting to +--- stream_response +timeout +ok + + + +=== TEST 34: conn queuing: connect until backlog is reached +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0.01, function(premature) + ngx.log(ngx.WARN, "start to handle timer") + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock2 then + ngx.log(ngx.ERR, err) + return + end + + ngx.sleep(0.02) + local ok, err = sock2:close() + if not ok then + ngx.log(ngx.ERR, err) + end + ngx.log(ngx.WARN, "continue to handle timer") + end) + + ngx.sleep(0.02) + local sock3, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock3 then + ngx.say(err) + end + local ok, err = sock1:setkeepalive() + if not ok then + ngx.say(err) + return + end + ngx.sleep(0.01) -- run sock2 + + ngx.log(ngx.WARN, "start to handle cosocket") + local sock3, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock3 then + ngx.say(err) + return + end + ngx.log(ngx.WARN, "continue to handle cosocket") + + local ok, err = sock3:setkeepalive() + if not ok then + ngx.say(err) + end + } +--- stream_response +too many waiting connect operations +--- error_log +lua tcp socket queue connect operation for connection pool "127.0.0.1 +--- no_error_log +[error] +--- grep_error_log eval: qr/queue connect operation for connection pool|(start|continue) to handle \w+/ +--- grep_error_log_out +start to handle timer +queue connect operation for connection pool +start to handle cosocket +queue connect operation for connection pool +continue to handle timer +continue to handle cosocket + + + +=== TEST 35: conn queuing: memory reuse for host in queueing connect operation ctx +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 3} + local sock = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0.01, function(premature) + local sock, err = ngx.socket.connect("0.0.0.0", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + end) + + ngx.timer.at(0.015, function(premature) + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + end) + + ngx.timer.at(0.02, function(premature) + local sock, err = ngx.socket.connect("0.0.0.0", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + end) + + ngx.sleep(0.03) + local ok, err = sock:setkeepalive() + if not ok then + ngx.say(err) + return + end + ngx.say("ok") + } +--- stream_response +ok +--- grep_error_log eval: qr/queue connect operation for connection pool/ +--- grep_error_log_out +queue connect operation for connection pool +queue connect operation for connection pool +queue connect operation for connection pool +--- no_error_log +[error] + + + +=== TEST 36: conn queuing: connect() returns error after connect operation resumed +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + local sock = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0, function(premature) + local sock, err = ngx.socket.connect("", port, opts) + if not sock then + ngx.log(ngx.WARN, err) + end + end) + + ngx.sleep(0.01) + -- use 'close' to force parsing host instead of reusing conn + local ok, err = sock:close() + if not ok then + ngx.say(err) + return + end + ngx.say("ok") + } +--- stream_response +ok +--- error_log +failed to parse host name +--- no_error_log +[error] +--- grep_error_log eval: qr/queue connect operation for connection pool/ +--- grep_error_log_out +queue connect operation for connection pool + + + +=== TEST 37: conn queuing: in uthread +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 2} + + local conn_sock = function() + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.say(err) + return + end + ngx.say("start to handle uthread") + + ngx.sleep(0.01) + sock:close() + ngx.say("continue to handle other uthread") + end + + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local co1 = ngx.thread.spawn(conn_sock) + local co2 = ngx.thread.spawn(conn_sock) + local co3 = ngx.thread.spawn(conn_sock) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.log(ngx.ERR, err) + end + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.thread.wait(co3) + ngx.say("all uthreads ok") + } +--- stream_response +too many waiting connect operations +start to handle uthread +continue to handle other uthread +start to handle uthread +continue to handle other uthread +all uthreads ok +--- no_error_log +[error] +--- grep_error_log eval: qr/queue connect operation for connection pool/ +--- grep_error_log_out +queue connect operation for connection pool +queue connect operation for connection pool + + + +=== TEST 38: conn queuing: in access_by_lua +--- SKIP: ngx_http_lua only + + + +=== TEST 39: conn queuing: in rewrite_by_lua +--- SKIP: ngx_http_lua only + + + +=== TEST 40: conn queuing: in subrequest +--- SKIP: ngx_http_lua only + + + +=== TEST 41: conn queuing: timeouts when 'connect_timeout' is reached +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + local sock2 = ngx.socket.tcp() + sock2:settimeouts(100, 3000, 3000) + local ok, err = sock2:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- stream_response +timeout +--- error_log eval +"lua tcp socket queued connect timed out, when trying to connect to 127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" + + + +=== TEST 42: conn queuing: set timeout via lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 100ms; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + local sock2 = ngx.socket.tcp() + local ok, err = sock2:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- stream_response +timeout +--- error_log eval +"lua tcp socket queued connect timed out, when trying to connect to 127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" + + + +=== TEST 43: conn queuing: client aborting while connect operation is queued +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + local sock2 = ngx.socket.tcp() + sock2:settimeouts(3000, 3000, 3000) + local ok, err = sock2:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- ignore_stream_response +--- timeout: 0.1 +--- abort +--- no_error_log +[error] + + + +=== TEST 44: conn queuing: resume next connect operation if resumed connect failed immediately +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 2} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + ok, err = sock:connect("", port, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +failed to parse host name "": no host +connected in uthread +ok +--- no_error_log +[error] + + + +=== TEST 45: conn queuing: resume connect operation if resumed connect failed (timeout) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + sock:settimeouts(100, 3000, 3000) + ok, err = sock:connect("127.0.0.2", 12345, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +timeout +connected in uthread +ok +--- error_log +queue connect operation for connection pool "test" +lua tcp socket connect timed out, when connecting to + + + +=== TEST 46: conn queuing: resume connect operation if resumed connect failed (could not be resolved) +--- stream_server_config + resolver 127.0.0.2:12345 ipv6=off; + resolver_timeout 1s; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + sock:settimeouts(1, 3000, 3000) + ok, err = sock:connect("agentzh.org", 80, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +agentzh.org could not be resolved (110: Operation timed out) +connected in uthread +ok +--- error_log +queue connect operation for connection pool "test" + + + +=== TEST 47: conn queuing: resume connect operation if resumed connect failed (connection refused) +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + sock:settimeouts(100, 3000, 3000) + ok, err = sock:connect("127.0.0.1", 62345, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +connection refused +connected in uthread +ok +--- error_log +queue connect operation for connection pool "test" + + + +=== TEST 48: conn queuing: resume connect operation if resumed connect failed (uthread aborted while resolving) +--- stream_server_config + resolver 127.0.0.1 ipv6=off; + resolver_timeout 100s; + + content_by_lua_block { + local function sub() + local semaphore = require "ngx.semaphore" + local sem = semaphore.new() + + local function f() + sem:wait(0.1) + ngx.exit(0) + end + + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + ngx.timer.at(0, function() + sem:post() + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + package.loaded.for_timer_to_resume:post() + if not sock2 then + ngx.log(ngx.ALERT, "resume connect failed: ", err) + return + end + + ngx.log(ngx.INFO, "resume success") + end) + + ngx.thread.spawn(f) + local sock1, err = ngx.socket.connect("openresty.org", 80, opts) + if not sock1 then + ngx.say(err) + return + end + end + + local function t() + local semaphore = require "ngx.semaphore" + local for_timer_to_resume = semaphore.new() + package.loaded.for_timer_to_resume = for_timer_to_resume + + ngx.thread.spawn(sub) + for_timer_to_resume:wait(0.1) + end + + t() + } +--- no_error_log +[alert] +--- error_log +resume success + + + +=== TEST 49: conn queuing: resume connect operation if resumed connect failed (uthread killed while resolving) +--- stream_server_config + resolver 127.0.0.1 ipv6=off; + resolver_timeout 100s; + + content_by_lua_block { + local opts = {pool = "test", pool_size = 1, backlog = 1} + local port = $TEST_NGINX_MEMCACHED_PORT + + local function resolve() + local sock1, err = ngx.socket.connect("openresty.org", 80, opts) + if not sock1 then + ngx.say(err) + return + end + end + + local th = ngx.thread.spawn(resolve) + local ok, err = ngx.thread.kill(th) + if not ok then + ngx.log(ngx.ALERT, "kill thread failed: ", err) + return + end + + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock2 then + ngx.log(ngx.ALERT, "resume connect failed: ", err) + return + end + + ngx.log(ngx.INFO, "resume success") + } +--- no_error_log +[alert] +--- error_log +resume success + + + +=== TEST 50: conn queuing: increase the counter for connections created before creating the pool with setkeepalive() +--- stream_server_config + content_by_lua_block { + local function connect() + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not sock then + error("connect failed: " .. err) + end + + return sock + end + + local sock1 = connect() + local sock2 = connect() + assert(sock1:setkeepalive()) + assert(sock2:setkeepalive()) + + local sock1 = connect() + local sock2 = connect() + assert(sock1:close()) + assert(sock2:close()) + + ngx.say("ok") + } +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 51: conn queuing: only decrease the counter for connections which were counted by the pool +--- stream_server_config + content_by_lua_block { + local function connect() + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not sock then + error("connect failed: " .. err) + end + + return sock + end + + local sock1 = connect() + local sock2 = connect() + assert(sock1:setkeepalive(1000, 1)) + assert(sock2:setkeepalive(1000, 1)) + + local sock1 = connect() + local sock2 = connect() + assert(sock1:close()) + assert(sock2:close()) + + ngx.say("ok") + } +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 52: conn queuing: clean up pending connect operations which are in queue +--- stream_server_config + content_by_lua_block { + local function sub() + local opts = {pool = "test", pool_size = 1, backlog = 1} + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT, opts) + if not sock then + ngx.say("connect failed: " .. err) + return + end + + local function f() + assert(ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT, opts)) + end + + local th = ngx.thread.spawn(f) + local ok, err = ngx.thread.kill(th) + if not ok then + ngx.log(ngx.ERR, "kill thread failed: ", err) + return + end + + sock:close() + end + + local function t() + ngx.thread.spawn(sub) + -- let pending connect operation resumes first + ngx.sleep(0) + ngx.say("ok") + end + + t() + } +--- stream_response +ok +--- error_log +lua tcp socket abort queueing +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/069-null.t b/src/deps/src/stream-lua-nginx-module/t/069-null.t new file mode 100644 index 000000000..0f2aa6c6e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/069-null.t @@ -0,0 +1,79 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +#master_on(); +#workers(1); +#log_level('debug'); +#log_level('warn'); +#worker_connections(1024); + +plan tests => repeat_each() * (blocks() * 3 + 1); + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306; + +our $LuaCpath = $ENV{LUA_CPATH} || + '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;'; + +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: compare ngx.null with cjson.null +--- stream_config eval + "lua_package_cpath '$::LuaCpath';"; +--- stream_server_config + content_by_lua_block { + local cjson = require "cjson" + ngx.say(cjson.null == ngx.null) + ngx.say(cjson.encode(ngx.null)) + } +--- stream_response +true +null +--- no_error_log +[error] + + + +=== TEST 2: output ngx.null +--- stream_server_config + content_by_lua_block { + ngx.say("ngx.null: ", ngx.null) + } +--- stream_response +ngx.null: null +--- no_error_log +[error] + + + +=== TEST 3: output ngx.null in a table +--- stream_server_config + content_by_lua_block { + ngx.say({"ngx.null: ", ngx.null}) + } +--- stream_response +ngx.null: null +--- no_error_log +[error] + + + +=== TEST 4: log ngx.null +--- stream_server_config + content_by_lua_block { + print("ngx.null: ", ngx.null) + ngx.say("done") + } +--- stream_response +done +--- error_log +ngx.null: null +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/070-sha1.t b/src/deps/src/stream-lua-nginx-module/t/070-sha1.t new file mode 100644 index 000000000..321c3a0de --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/070-sha1.t @@ -0,0 +1,48 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_process_enabled(1); +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 1); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: set sha1 hello +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64(ngx.sha1_bin("hello"))) } +--- stream_response +qvTGHdzF6KLavt4PO0gs2a6pQ00= + + + +=== TEST 2: set sha1 "" +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64(ngx.sha1_bin(""))) } +--- stream_response +2jmj7l5rSw0yVb/vlWAYkK/YBwk= + + + +=== TEST 3: set sha1 nil +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64(ngx.sha1_bin(nil))) } +--- stream_response +2jmj7l5rSw0yVb/vlWAYkK/YBwk= + + + +=== TEST 4: set sha1 number +--- stream_server_config + content_by_lua_block { ngx.say(ngx.encode_base64(ngx.sha1_bin(512))) } +--- stream_response +zgmxJ9SPg4aKRWReJG07UvS97L4= +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/071-idle-socket.t b/src/deps/src/stream-lua-nginx-module/t/071-idle-socket.t new file mode 100644 index 000000000..8e73219bc --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/071-idle-socket.t @@ -0,0 +1,354 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; + +run_tests(); + +__DATA__ + +=== TEST 1: read events come when socket is idle +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("foofoo\r\n") + local line, err, part = reader() + if line then + ngx.print("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + + ngx.sleep(0.5) + + local data, err, part = sock:receive("*a") + if not data then + ngx.say("failed to read the 2nd part: ", err) + else + ngx.say("2nd part: [", data, "]") + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo -n foofoo; + echo_flush; + echo_sleep 0.3; + echo -n barbar; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: HTTP/1.1 200 OK\r +Server: nginx\r +Content-Type: text/plain\r +Transfer-Encoding: chunked\r +Connection: close\r +\r +6\r +2nd part: [6\r +barbar\r +0\r +\r +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 2: read timer cleared in time +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + + sock:settimeout(400) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + ngx.sleep(0.5) + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent again: ", bytes) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connected: 1 +request sent: 11 +received: OK +request sent again: 11 +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 3: connect timer cleared in time +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + + sock:settimeout(300) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + ngx.sleep(0.5) + + local req = "flush_all\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connected: 1 +request sent: 11 +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 4: send timer cleared in time +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + + sock:settimeout(300) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + ngx.sleep(0.5) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + return + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connected: 1 +request sent: 11 +received: OK +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 5: set keepalive when system socket recv buffer has unread data +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("foofoo\r\n") + local line, err, part = reader() + if line then + ngx.print("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + + ngx.sleep(0.5) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set keepalive: ", err) + end + } + +--- config + server_tokens off; + location = /foo { + echo -n foofoo; + echo_flush; + echo_sleep 0.3; + echo -n barbar; + more_clear_headers Date; + } +--- stream_response_like eval +qr{connected: 1 +request sent: 57 +read: HTTP/1\.1 200 OK\r +Server: nginx\r +Content-Type: text/plain\r +Transfer-Encoding: chunked\r +Connection: close\r +\r +6\r +failed to set keepalive: (?:unread data in buffer|connection in dubious state) +} +--- no_error_log +[error] + + + +=== TEST 6: set keepalive when cosocket recv buffer has unread data +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local data, err = sock:receive(1) + if not data then + ngx.say("failed to read the 1st byte: ", err) + return + end + + ngx.say("read: ", data) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set keepalive: ", err) + end + } + +--- stream_response eval +qq{connected: 1 +request sent: 11 +read: O +failed to set keepalive: unread data in buffer +} +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/073-backtrace.t b/src/deps/src/stream-lua-nginx-module/t/073-backtrace.t new file mode 100644 index 000000000..2cf9f754c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/073-backtrace.t @@ -0,0 +1,173 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * 59; + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block + { function bar() + return lua_concat(3) + end + function foo() + bar() + end + foo() + } +--- stream_response +--- error_log +attempt to call global 'lua_concat' +: in function 'bar' +:5: in function 'foo' +:7: in main chunk + + + +=== TEST 2: error(nil) +--- stream_server_config + content_by_lua_block + { function bar() + error(nil) + end + function foo() + bar() + end + foo() + } +--- stream_response +--- error_log eval +[ +'lua entry thread aborted: runtime error: unknown reason', +'stack traceback:', +" in function 'error'", +": in function 'bar'", +":5: in function 'foo'", +qr/:7: in main chunk/, +] + + + +=== TEST 3: deep backtrace in a single coroutine (more than 15) +--- stream_server_config eval +my $s = " + content_by_lua_block { +"; +my $prev; +for my $i (1..18) { + if (!defined $prev) { + $s .= " + local function func$i() + return error([[blah]]) + end"; + } else { + $s .= " + local function func$i() + local v = func$prev() + return v + end"; + } + $prev = $i; +} +$s .= " + func$prev() + } +"; +--- stap2 +probe process("$LIBLUA_PATH").function("lua_concat") { + println("lua concat") + //print_ubacktrace() +} +--- stap_out2 +--- stream_response +--- error_log +: blah +: in function 'func1' +:7: in function 'func2' +:11: in function 'func3' +:15: in function 'func4' +:19: in function 'func5' +:23: in function 'func6' +:27: in function 'func7' +:31: in function 'func8' +:35: in function 'func9' +:39: in function 'func10' +:43: in function 'func11' +:47: in function 'func12' +:51: in function 'func13' +:55: in function 'func14' +:59: in function 'func15' +:63: in function 'func16' +:67: in function 'func17' +:71: in function 'func18' +:74: in main chunk + + + +=== TEST 4: deep backtrace in a single coroutine (more than 22) +--- stream_server_config eval +my $s = " + content_by_lua_block { +"; +my $prev; +for my $i (1..23) { + if (!defined $prev) { + $s .= " + local function func$i() + return error([[blah]]) + end"; + } else { + $s .= " + local function func$i() + local v = func$prev() + return v + end"; + } + $prev = $i; +} +$s .= " + func$prev() + } +"; +--- stap2 +probe process("$LIBLUA_PATH").function("lua_concat") { + println("lua concat") + //print_ubacktrace() +} +--- stap_out2 +--- stream_response +--- error_log +: blah +: in function 'func1' +:7: in function 'func2' +:11: in function 'func3' +:15: in function 'func4' +:19: in function 'func5' +:23: in function 'func6' +:27: in function 'func7' +:31: in function 'func8' +:35: in function 'func9' +:39: in function 'func10' +:43: in function 'func11' +:47: in function 'func12' +:59: in function 'func15' +:63: in function 'func16' +:67: in function 'func17' +:71: in function 'func18' +:75: in function 'func19' +:79: in function 'func20' +:83: in function 'func21' +... diff --git a/src/deps/src/stream-lua-nginx-module/t/074-prefix-var.t b/src/deps/src/stream-lua-nginx-module/t/074-prefix-var.t new file mode 100644 index 000000000..96fbde345 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/074-prefix-var.t @@ -0,0 +1,58 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: $prefix +--- stream_config: lua_package_path "$prefix/html/?.lua;;"; +--- stream_server_config + content_by_lua_block { + local foo = require "foo" + foo.go() + } +--- user_files +>>> foo.lua +module("foo", package.seeall) + +function go() + ngx.say("Greetings from module foo.") +end +--- stream_response +Greetings from module foo. +--- no_error_log +[error] + + + +=== TEST 2: ${prefix} +--- stream_config: lua_package_path "${prefix}html/?.lua;;"; +--- stream_server_config + content_by_lua_block { + local foo = require "foo" + foo.go() + } +--- user_files +>>> foo.lua +module("foo", package.seeall) + +function go() + ngx.say("Greetings from module foo.") +end +--- stream_response +Greetings from module foo. +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/075-logby.t b/src/deps/src/stream-lua-nginx-module/t/075-logby.t new file mode 100644 index 000000000..c2366bddc --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/075-logby.t @@ -0,0 +1,365 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 8); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: log_by_lua +--- stream_server_config + content_by_lua_block { + ngx.say('hello') + } + + log_by_lua_block { ngx.log(ngx.ERR, "Hello from log_by_lua: ", ngx.var.protocol) } +--- stream_response +hello +--- error_log +Hello from log_by_lua: TCP + + + +=== TEST 2: log_by_lua_file +--- stream_server_config + content_by_lua_block { + ngx.say('hello') + } + + log_by_lua_file html/a.lua; +--- user_files +>>> a.lua +ngx.log(ngx.ERR, "Hello from log_by_lua: ", ngx.var.protocol) +--- stream_response +hello +--- error_log +Hello from log_by_lua: TCP + + + +=== TEST 3: log_by_lua_file & content_by_lua +--- stream_server_config + content_by_lua_block { ngx.say(ngx.var.remote_addr) } + log_by_lua_file html/a.lua; +--- user_files +>>> a.lua +ngx.log(ngx.ERR, "Hello from log_by_lua: ", ngx.var.status) +--- stream_response +127.0.0.1 +--- error_log +Hello from log_by_lua: 200 + + + +=== TEST 4: ngx.ctx available in log_by_lua (already defined) +--- stream_server_config + content_by_lua_block { ngx.ctx.counter = 3 ngx.say(ngx.ctx.counter) } + log_by_lua_block { ngx.log(ngx.ERR, "ngx.ctx.counter: ", ngx.ctx.counter) } +--- stream_response +3 +--- error_log +ngx.ctx.counter: 3 +lua release ngx.ctx + + + +=== TEST 5: ngx.ctx available in log_by_lua (not defined yet) +--- stream_server_config + content_by_lua_block { + ngx.say('hello') + } + + log_by_lua_block { + ngx.log(ngx.ERR, "ngx.ctx.counter: ", ngx.ctx.counter) + ngx.ctx.counter = "hello world" + } +--- stream_response +hello +--- error_log +ngx.ctx.counter: nil +lua release ngx.ctx + + + +=== TEST 6: log_by_lua + shared dict +--- stream_config + lua_shared_dict foo 100k; +--- stream_server_config + content_by_lua_block { + ngx.say('hello') + } + + log_by_lua_block { + local foo = ngx.shared.foo + local key = ngx.var.remote_addr .. ngx.status + local newval, err = foo:incr(key, 1) + if not newval then + if err == "not found" then + foo:add(key, 0) + newval, err = foo:incr(key, 1) + if not newval then + ngx.log(ngx.ERR, "failed to incr ", key, ": ", err) + return + end + else + ngx.log(ngx.ERR, "failed to incr ", key, ": ", err) + return + end + end + print(key, ": ", foo:get(key)) + } +--- stream_response +hello +--- error_log eval +qr{127.0.0.1200: [12]} +--- no_error_log +[error] + + + +=== TEST 7: lua error (string) +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { error("Bad") } +--- stream_response +ok +--- error_log eval +qr/failed to run log_by_lua\*: log_by_lua\(nginx\.conf:\d+\):1: Bad/ + + + +=== TEST 8: lua error (nil) +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { error(nil) } +--- stream_response +ok +--- error_log +failed to run log_by_lua*: unknown reason + + + +=== TEST 9: globals shared +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.INFO, "old foo: ", foo) + foo = foo + 1 + end + } +--- stream_response +ok +--- grep_error_log eval: qr/old foo: \d+/ +--- grep_error_log_out eval +["", "old foo: 1\n"] + + + +=== TEST 10: no ngx.print +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { ngx.print(32) return 1 } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 11: no ngx.say +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { ngx.say(32) return 1 } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 12: no ngx.flush +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { ngx.flush() } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 13: no ngx.eof +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { ngx.eof() } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 14: no ngx.exit +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { ngx.exit(0) } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 15: no ngx.req.socket() +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { return ngx.req.socket() } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 16: no ngx.socket.tcp() +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { return ngx.socket.tcp() } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 17: no ngx.socket.connect() +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { return ngx.socket.connect("127.0.0.1", 80) } +--- stream_response +ok +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 18: backtrace +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_block { + function foo() + bar() + end + + function bar() + error("something bad happened") + end + + foo() + } +--- stream_response +ok +--- error_log +something bad happened +stack traceback: +in function 'error' +in function 'bar' +in function 'foo' + + + +=== TEST 19: Lua file does not exist +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + log_by_lua_file html/test2.lua; +--- user_files +>>> test.lua +v = ngx.var["request_uri"] +ngx.print("request_uri: ", v, "\n") +--- stream_response +ok +--- error_log eval +qr/failed to load external Lua file ".*?test2\.lua": cannot open .*? No such file or directory/ + + + +=== TEST 20: log_by_lua runs before access logging (github issue #254) +--- stream_config + log_format basic '$remote_addr [$time_local] ' + '$protocol $status $bytes_sent $bytes_received ' + '$session_time'; + +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } + + access_log logs/foo.log basic; + log_by_lua_block { print("hello") } +--- stap +F(ngx_http_log_handler) { + println("log handler") +} +F(ngx_http_lua_log_handler) { + println("lua log handler") +} +--- stap_out +lua log handler +log handler + +--- stream_response +ok +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/077-sleep.t b/src/deps/src/stream-lua-nginx-module/t/077-sleep.t new file mode 100644 index 000000000..13e99e16f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/077-sleep.t @@ -0,0 +1,207 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * 39; + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sleep 0.5 - content +--- stream_server_config + content_by_lua_block { + ngx.update_time() + local before = ngx.now() + ngx.sleep(0.5) + local now = ngx.now() + ngx.say(now - before) + } +--- stream_response_like chop +^0\.(?:4[5-9]\d*|5[0-5]\d*|5)$ +--- error_log +lua ready to sleep for +stream lua sleep timer expired + + + +=== TEST 2: sleep a - content +--- stream_server_config + content_by_lua_block { + ngx.update_time() + local before = ngx.now() + ngx.sleep("a") + local now = ngx.now() + ngx.say(now - before) + } +--- stream_response +--- error_log +bad argument #1 to 'sleep' + + + +=== TEST 3: sleep 0.33 - multi-times in content +--- stream_server_config + content_by_lua_block { + ngx.update_time() + local start = ngx.now() + ngx.sleep(0.33) + ngx.sleep(0.33) + ngx.sleep(0.33) + ngx.say(ngx.now() - start) + } +--- stream_response_like chop +^(?:0\.9\d*|1\.[0-2]\d*|1)$ +--- error_log +lua ready to sleep for +stream lua sleep timer expired +--- no_error_log +[error] + + + +=== TEST 4: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep +--- stream_server_config + content_by_lua_block { + ngx.sleep(1) + ngx.say("blah") + ngx.sleep(1) + } +--- stream_response +blah +--- error_log +lua ready to sleep +stream lua sleep timer expired +--- no_error_log +[error] + + + +=== TEST 5: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep +--- stream_server_config + content_by_lua_block { + ngx.sleep(0.3) + ngx.say("blah") + ngx.sleep(0.5) + ngx.say("hiya") + } +--- stream_response +blah +hiya +--- error_log +lua ready to sleep for +stream lua sleep timer expired +--- no_error_log +[error] + + + +=== TEST 6: sleep 0 +--- stream_server_config + content_by_lua_block { + ngx.update_time() + local before = ngx.now() + ngx.sleep(0) + local now = ngx.now() + ngx.say("elapsed: ", now - before) + } +--- stream_response_like chop +elapsed: 0 +--- error_log +lua ready to sleep for +stream lua sleep timer expired +--- no_error_log +[error] + + + +=== TEST 7: ngx.sleep unavailable in log_by_lua +TODO +--- SKIP +--- stream_server_config + echo hello; + log_by_lua_block { + ngx.sleep(0.1) + } +--- stream_response +hello +--- wait: 0.1 +--- error_log +API disabled in the context of log_by_lua* + + + +=== TEST 8: ngx.sleep() fails to yield (xpcall err handler) +--- stream_server_config + content_by_lua_block { + local function f() + return error(1) + end + local function err() + ngx.sleep(0.001) + end + xpcall(f, err) + ngx.say("ok") + } +--- stream_response +ok +--- error_log +lua clean up the timer for pending ngx.sleep +--- no_error_log +[error] + + + +=== TEST 9: ngx.sleep() fails to yield (require) +--- stream_config + lua_package_path "$prefix/html/?.lua;;"; +--- stream_server_config + content_by_lua_block { + package.loaded["foosleep"] = nil + require "foosleep"; + } +--- user_files +>>> foosleep.lua +ngx.sleep(0.001) + +--- stream_response +--- wait: 0.2 +--- error_log eval +[ +"lua clean up the timer for pending ngx.sleep", +qr{runtime error: attempt to yield across (?:metamethod/)?C-call boundary}, +] + + + +=== TEST 10: sleep coctx handler did not get called in ngx.exit(). +--- stream_server_config + content_by_lua_block { + local function sleep(t) + --- nginx return reply to client without waiting + ngx.sleep(t) + end + + local function wait() + --- worker would crash afterwards + xpcall(function () error(1) end, function() return sleep(0.001) end) + --- ngx.exit was required to crash worker + ngx.exit(200) + end + + wait() + } +--- wait: 0.1 +--- stream_response +--- no_error_log +[error] +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/080-hup-shdict.t b/src/deps/src/stream-lua-nginx-module/t/080-hup-shdict.t new file mode 100644 index 000000000..4efac2407 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/080-hup-shdict.t @@ -0,0 +1,75 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : (); +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: initialize the fields in shdict +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + dogs:set("bah", 10502) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + val = dogs:get("bah") + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +10502 number +--- no_error_log +[error] + + + +=== TEST 2: retrieve the fields in shdict after HUP reload +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + -- dogs:set("foo", 32) + -- dogs:set("bah", 10502) + + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + val = dogs:get("bah") + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +10502 number +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/081-bytecode.t b/src/deps/src/stream-lua-nginx-module/t/081-bytecode.t new file mode 100644 index 000000000..5f9bd44b5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/081-bytecode.t @@ -0,0 +1,51 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: bytecode (not stripped) +--- stream_server_config + content_by_lua_block { + local f = assert(loadstring("local a = a and a + 1 or 1 ngx.say('a = ', a)", "=code")) + local bc = string.dump(f) + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/a.luac", "w")) + f:write(bc) + f:close() + } +--- stream_server_config2 + content_by_lua_file html/a.luac; +--- stream_response +a = 1 +--- no_error_log +[error] + + + +=== TEST 2: bytecode (stripped) +--- stream_server_config + content_by_lua_block { + local f = assert(loadstring("local a = a and a + 1 or 1 ngx.say('a = ', a)", "=code")) + local bc = string.dump(f, true) + local f = assert(io.open("$TEST_NGINX_SERVER_ROOT/html/a.luac", "w")) + f:write(bc) + f:close() + } +--- stream_server_config2 + content_by_lua_file html/a.luac; +--- stream_response +a = 1 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/083-bad-sock-self.t b/src/deps/src/stream-lua-nginx-module/t/083-bad-sock-self.t new file mode 100644 index 000000000..27a622a1c --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/083-bad-sock-self.t @@ -0,0 +1,100 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; + +run_tests(); + +__DATA__ + +=== TEST 1: receive +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + sock.receive("l") + } +--- stream_response +--- error_log +bad argument #1 to 'receive' (table expected, got string) + + + +=== TEST 2: receiveuntil +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + sock.receiveuntil(32, "ab") + } +--- stream_response +--- error_log +bad argument #1 to 'receiveuntil' (table expected, got number) + + + +=== TEST 3: send (bad arg number) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.tcp() + sock.send("hello") + } +--- stream_response +--- error_log +expecting 2 arguments (including the object), but got 1 + + + +=== TEST 4: send (bad self) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.tcp() + sock.send("hello", 32) + } +--- stream_response +--- error_log +bad argument #1 to 'send' (table expected, got string) + + + +=== TEST 5: getreusedtimes (bad self) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.tcp() + sock.getreusedtimes(2) + } +--- stream_response +--- error_log +bad argument #1 to 'getreusedtimes' (table expected, got number) + + + +=== TEST 6: close (bad self) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.tcp() + sock.close(2) + } +--- stream_response +--- error_log +bad argument #1 to 'close' (table expected, got number) + + + +=== TEST 7: setkeepalive (bad self) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.tcp() + sock.setkeepalive(2) + } +--- stream_response +--- error_log +bad argument #1 to 'setkeepalive' (table expected, got number) diff --git a/src/deps/src/stream-lua-nginx-module/t/084-inclusive-receiveuntil.t b/src/deps/src/stream-lua-nginx-module/t/084-inclusive-receiveuntil.t new file mode 100644 index 000000000..cd67e7e84 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/084-inclusive-receiveuntil.t @@ -0,0 +1,712 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 4); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; + +run_tests(); + +__DATA__ + +=== TEST 1: ambiguous boundary patterns (abcabd) - inclusive mode +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabd", { inclusive = true }) + + for i = 1, 3 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcabdabcabd; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabcabd +read: abcabd +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 2: ambiguous boundary patterns (abcabdabcabe 4) - inclusive mode +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabdabcabe", { inclusive = true }) + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo ababcabdabcabe; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: ababcabdabcabe +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 3: ambiguous boundary patterns (abcabd) - inclusive mode - small buffers +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("abcabd", { inclusive = true }) + + for i = 1, 3 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcabdabcabd; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabcabd +read: abcabd +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 4: inclusive option value nil +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aa", { inclusive = nil }) + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcaad; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabc +failed to read a line: closed [d +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 5: inclusive option value false +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aa", { inclusive = false }) + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcaad; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabc +failed to read a line: closed [d +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 6: inclusive option value true (aa) +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aa", { inclusive = true }) + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcaad; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: abcabcaa +failed to read a line: closed [d +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 7: bad inclusive option value type +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + ngx.flush(true) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aa", { inclusive = "true" }) + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcaad; + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +--- error_log +bad "inclusive" option value type: string +--- no_error_log +[alert] +[warn] + + + +=== TEST 8: bad option table +--- stream_server_config + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + ngx.flush(true) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("aa", { inclusive = "true" }) + + for i = 1, 2 do + line, err, part = reader() + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo abcabcaad; + more_clear_headers Date; + } +--- stream_response +connected: 1 +request sent: 57 +--- error_log +bad "inclusive" option value type: string +--- no_error_log +[alert] +[warn] + + + +=== TEST 9: ambiguous boundary patterns (--abc), small buffer +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc", { inclusive = true }) + + for i = 1, 7 do + line, err, part = reader(4) + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo "hello, world ----abc"; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: hell +read: o, w +read: orld +read: -- +read: --abc +failed to read a line: nil [nil] +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 10: ambiguous boundary patterns (--abc), small buffer, mixed by other reading calls +--- stream_server_config + lua_socket_buffer_size 1; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc", { inclusive = true }) + + for i = 1, 7 do + line, err, part = reader(4) + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a chunk: ", err, " [", part, "]") + end + + local data, err, part = sock:receive(1) + if not data then + ngx.say("failed to read a byte: ", err, " [", part, "]") + break + else + ngx.say("read one byte: ", data) + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + echo "hello, world ----abc"; + more_clear_headers Date; + } +--- stream_response eval +qq{connected: 1 +request sent: 57 +read: hell +read one byte: o +read: , wo +read one byte: r +read: ld - +read one byte: - +read: --abc +read one byte: + +failed to read a chunk: nil [nil] +failed to read a byte: closed [] +close: 1 nil +} +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/086-init-by.t b/src/deps/src/stream-lua-nginx-module/t/086-init-by.t new file mode 100644 index 000000000..6fb4e6e15 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/086-init-by.t @@ -0,0 +1,261 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 3); + +#no_diff(); +#no_long_string(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity (inline) +--- stream_config + init_by_lua_block { foo = "hello, FOO" } +--- stream_server_config + content_by_lua_block { ngx.say(foo) } +--- stream_response +hello, FOO +--- no_error_log +[error] + + + +=== TEST 2: sanity (file) +--- stream_config + init_by_lua_file html/init.lua; +--- stream_server_config + content_by_lua_block { ngx.say(foo) } +--- user_files +>>> init.lua +foo = "hello, FOO" +--- stream_response +hello, FOO +--- no_error_log +[error] + + + +=== TEST 3: require +--- stream_config + lua_package_path "$prefix/html/?.lua;;"; + init_by_lua_block { require "blah" } +--- stream_server_config + content_by_lua_block { + blah.go() + } +--- user_files +>>> blah.lua +module(..., package.seeall) + +function go() + ngx.say("hello, blah") +end +--- stream_response +hello, blah +--- no_error_log +[error] + + + +=== TEST 4: shdict (single) +--- stream_config + lua_shared_dict dogs 1m; + init_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 6) + dogs:get("Jim") + } +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + ngx.say("Jim: ", dogs:get("Jim")) + } +--- stream_response +Jim: 6 +--- no_error_log +[error] + + + +=== TEST 5: shdict (multi) +--- stream_config + lua_shared_dict dogs 1m; + lua_shared_dict cats 1m; + init_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 6) + dogs:get("Jim") + local cats = ngx.shared.cats + cats:set("Tom", 2) + dogs:get("Tom") + } +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + ngx.say("Jim: ", dogs:get("Jim")) + } +--- stream_response +Jim: 6 +--- no_error_log +[error] + + + +=== TEST 6: print +--- stream_config + lua_shared_dict dogs 1m; + lua_shared_dict cats 1m; + init_by_lua_block { + print("log from init_by_lua") + } +--- stream_server_config + content_by_lua_block { ngx.say('ok') } +--- stream_response +ok +--- grep_error_log chop +log from init_by_lua +--- grep_error_log_out eval +["log from init_by_lua\n", ""] + + + +=== TEST 7: ngx.log +--- stream_config + lua_shared_dict dogs 1m; + lua_shared_dict cats 1m; + init_by_lua_block { + ngx.log(ngx.NOTICE, "log from init_by_lua") + } +--- stream_server_config + content_by_lua_block { ngx.say('ok') } +--- stream_response +ok +--- grep_error_log chop +log from init_by_lua +--- grep_error_log_out eval +["log from init_by_lua\n", ""] + + + +=== TEST 8: require (with shm defined) +--- stream_config + lua_package_path "$prefix/html/?.lua;;"; + lua_shared_dict dogs 1m; + init_by_lua_block { require "blah" } +--- stream_server_config + content_by_lua_block { + blah.go() + } +--- user_files +>>> blah.lua +module(..., package.seeall) + +function go() + ngx.say("hello, blah") +end +--- stream_response +hello, blah +--- no_error_log +[error] + + + +=== TEST 9: coroutine API (inlined init_by_lua) +--- stream_config + init_by_lua_block { + local function f() + foo = 32 + coroutine.yield(78) + bar = coroutine.status(coroutine.running()) + end + local co = coroutine.create(f) + local ok, err = coroutine.resume(co) + if not ok then + print("Failed to resume our co: ", err) + return + end + baz = err + coroutine.resume(co) + } +--- stream_server_config + content_by_lua_block { + ngx.say("foo = ", foo) + ngx.say("bar = ", bar) + ngx.say("baz = ", baz) + } +--- stream_response +foo = 32 +bar = running +baz = 78 +--- no_error_log +[error] +Failed to resume our co: + + + +=== TEST 10: coroutine API (init_by_lua_file) +--- stream_config + init_by_lua_file html/init.lua; + +--- stream_server_config + content_by_lua_block { + ngx.say("foo = ", foo) + ngx.say("bar = ", bar) + ngx.say("baz = ", baz) + } +--- user_files +>>> init.lua +local function f() + foo = 32 + coroutine.yield(78) + bar = coroutine.status(coroutine.running()) +end +local co = coroutine.create(f) +local ok, err = coroutine.resume(co) +if not ok then + print("Failed to resume our co: ", err) + return +end +baz = err +coroutine.resume(co) + +--- stream_response +foo = 32 +bar = running +baz = 78 +--- no_error_log +[error] +Failed to resume our co: + + + +=== TEST 11: access a field in the ngx. table +--- stream_config + init_by_lua_block { + print("INIT 1: foo = ", ngx.foo) + ngx.foo = 3 + print("INIT 2: foo = ", ngx.foo) + } +--- stream_server_config + content_by_lua_block { ngx.say('ok') } +--- stream_response +ok +--- no_error_log +[error] +--- grep_error_log eval: qr/INIT \d+: foo = \S+/ +--- grep_error_log_out eval +[ +"INIT 1: foo = nil +INIT 2: foo = 3 +", +"", +] diff --git a/src/deps/src/stream-lua-nginx-module/t/087-udp-socket.t b/src/deps/src/stream-lua-nginx-module/t/087-udp-socket.t new file mode 100644 index 000000000..e768ad571 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/087-udp-socket.t @@ -0,0 +1,914 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (3 * blocks() + 14); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +#log_level 'warn'; + +no_long_string(); +#no_diff(); +#no_shuffle(); +check_accum_error_log(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + + content_by_lua_block { + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + local port = $TEST_NGINX_MEMCACHED_PORT + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("received ", #data, " bytes: ", data) + } + +--- config + server_tokens off; +--- stream_response eval +"connected\nreceived 12 bytes: \x{00}\x{01}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}" +--- no_error_log +[error] +--- log_level: debug +--- error_log +lua udp socket receive buffer size: 8192 + + + +=== TEST 2: multiple parallel queries +--- stream_server_config + + content_by_lua_block { + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + local port = $TEST_NGINX_MEMCACHED_PORT + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + req = "\0\2\0\0\0\1\0\0flush_all\r\n" + ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + ngx.sleep(0.05) + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("1: received ", #data, " bytes: ", data) + + data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("2: received ", #data, " bytes: ", data) + } + +--- config + server_tokens off; +--- stream_response_like eval +"^connected\n" +."1: received 12 bytes: " +."\x{00}[\1\2]\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}" +."2: received 12 bytes: " +."\x{00}[\1\2]\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}\$" +--- no_error_log +[error] + + + +=== TEST 3: access a TCP interface +--- stream_server_config + + content_by_lua_block { + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + local port = $TEST_NGINX_SERVER_PORT + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("received ", #data, " bytes: ", data) + } + +--- config + server_tokens off; +--- stream_response +connected +failed to receive data: connection refused +--- error_log eval +qr/recv\(\) failed \(\d+: Connection refused\)/ + + + +=== TEST 4: access conflicts of connect() on shared udp objects +--- stream_config + lua_package_path '$prefix/html/?.lua;;'; +--- stream_server_config + content_by_lua_block { + local function f() + local port = $TEST_NGINX_MEMCACHED_PORT + local foo = require "foo" + local udp = foo.get_udp() + + udp:settimeout(100) -- 100 ms + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + print("test: connected") + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + print("test: received ", #data, " bytes: ", data) + end + for i = 1, 170 do + assert(ngx.timer.at(0, f)) + end + } + +--- user_files +>>> foo.lua +module("foo", package.seeall) + +local udp + +function get_udp() + if not udp then + udp = ngx.socket.udp() + end + + return udp +end + +--- stap2 +M(http-lua-info) { + printf("udp resume: %p\n", $coctx) + print_ubacktrace() +} + +--- stream_response +--- error_log eval +qr/content_by_lua\(nginx\.conf:\d+\):9: bad request/ + + + +=== TEST 5: access conflicts of receive() on shared udp objects +--- stream_config + lua_package_path '$prefix/html/?.lua;;'; +--- stream_server_config + content_by_lua_block { + function f() + local port = $TEST_NGINX_MEMCACHED_PORT + local foo = require "foo" + local udp = foo.get_udp(port) + + local data, err = udp:receive() + if not data then + ngx.log(ngx.ERR, "failed to receive data: ", err) + return ngx.exit(500) + end + ngx.print("received ", #data, " bytes: ", data) + end + + for i = 1, 170 do + assert(ngx.timer.at(0, f)) + end + } + +--- user_files +>>> foo.lua +module("foo", package.seeall) + +local udp + +function get_udp(port) + if not udp then + udp = ngx.socket.udp() + + udp:settimeout(100) -- 100ms + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return ngx.exit(500) + end + end + + return udp +end +--- stream_response +--- error_log eval +qr/content_by_lua\(nginx\.conf:\d+\):7: bad request/ + + + +=== TEST 6: connect again immediately +--- stream_server_config + + content_by_lua_block { + local sock = ngx.socket.udp() + local port = $TEST_NGINX_MEMCACHED_PORT + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected again: ", ok) + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = sock:send(req) + if not ok then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", ok) + + local line, err = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive: ", err) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- stream_response eval +"connected: 1 +connected again: 1 +request sent: 1 +received: \0\1\0\0\0\1\0\0OK\r\n +close: 1 nil +" +--- no_error_log +[error] +--- error_log eval +["lua reuse socket upstream", "lua udp socket reconnect without shutting down"] +--- log_level: debug + + + +=== TEST 7: recv timeout +--- stream_server_config + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + + local sock = ngx.socket.udp() + sock:settimeout(100) -- 100 ms + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local line, err = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive: ", err) + end + + -- ok, err = sock:close() + -- ngx.say("close: ", ok, " ", err) + } + +--- stream_response +connected: 1 +failed to receive: timeout +--- error_log +lua udp socket read timed out + + + +=== TEST 8: with an explicit receive buffer size argument +--- stream_server_config + + content_by_lua_block { + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + local port = $TEST_NGINX_MEMCACHED_PORT + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive(1400) + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("received ", #data, " bytes: ", data) + } + +--- stream_response eval +"connected\nreceived 12 bytes: \x{00}\x{01}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}" +--- no_error_log +[error] +--- log_level: debug +--- error_log +lua udp socket receive buffer size: 1400 + + + +=== TEST 9: read timeout and re-receive +--- stream_server_config + content_by_lua_block { + local udp = ngx.socket.udp() + udp:settimeout(30) + local ok, err = udp:setpeername("127.0.0.1", 19232) + if not ok then + ngx.say("failed to setpeername: ", err) + return + end + local ok, err = udp:send("blah") + if not ok then + ngx.say("failed to send: ", err) + return + end + for i = 1, 2 do + local data, err = udp:receive() + if err == "timeout" then + -- continue + else + if not data then + ngx.say("failed to receive: ", err) + return + end + ngx.say("received: ", data) + return + end + end + + ngx.say("timed out") + } + +--- udp_listen: 19232 +--- udp_reply: hello world +--- udp_reply_delay: 45ms +--- stream_response +received: hello world +--- error_log +lua udp socket read timed out + + + +=== TEST 10: access the google DNS server (using IP addr) +--- stream_server_config + content_by_lua_block { + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + udp:settimeout(2000) -- 2 sec + + local ok, err = udp:setpeername("$TEST_NGINX_RESOLVER", 53) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "\0}\1\0\0\1\0\0\0\0\0\0\3www\6google\3com\0\0\1\0\1" + + -- ngx.print(req) + -- do return end + + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + + if string.match(data, "\3www\6google\3com") then + ngx.say("received a good response.") + else + ngx.say("received a bad response: ", #data, " bytes: ", data) + end + } + +--- stream_response +received a good response. +--- no_error_log +[error] +--- log_level: debug +--- error_log +lua udp socket receive buffer size: 8192 + + + +=== TEST 11: access the google DNS server (using domain names) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + -- avoid flushing google in "check leak" testing mode: + local counter = package.loaded.counter + if not counter then + counter = 1 + elseif counter >= 2 then + return ngx.exit(503) + else + counter = counter + 1 + end + package.loaded.counter = counter + + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + udp:settimeout(2000) -- 2 sec + + local ok, err = udp:setpeername("google-public-dns-a.google.com", 53) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "\0}\1\0\0\1\0\0\0\0\0\0\3www\6google\3com\0\0\1\0\1" + + -- ngx.print(req) + -- do return end + + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + + if string.match(data, "\3www\6google\3com") then + ngx.say("received a good response.") + else + ngx.say("received a bad response: ", #data, " bytes: ", data) + end + } + +--- stream_response +received a good response. +--- no_error_log +[error] +--- log_level: debug +--- error_log +lua udp socket receive buffer size: 8192 + + + +=== TEST 12: datagram unix domain socket +--- stream_server_config + + content_by_lua_block { + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + udp:settimeout(2000) -- 1 sec + + local ok, err = udp:setpeername("unix:a.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "hello,\nserver" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("received ", #data, " bytes: ", data) + } + +--- udp_listen: a.sock +--- udp_reply +hello, +client + +--- stream_response +connected +received 14 bytes: hello, +client + +--- stap2 +probe syscall.socket, syscall.connect { + print(name, "(", argstr, ")") +} + +probe syscall.socket.return, syscall.connect.return { + println(" = ", retstr) +} +--- no_error_log +[error] +[crit] +--- skip_eval: 3: $^O ne 'linux' + + + +=== TEST 13: bad request tries to setpeer +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to set peer: ", err) + else + ngx.say("peer set") + end + function f() + local sock = test.get_sock() + sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.udp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +peer set + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 14: bad request tries to send +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to set peer: ", err) + else + ngx.say("peer set") + end + function f() + local sock = test.get_sock() + sock:send("a") + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.udp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +peer set + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 15: bad request tries to receive +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to set peer: ", err) + else + ngx.say("peer set") + end + + local function f() + local sock = test.get_sock() + sock:receive() + end + + ngx.timer.at(0, f) + ngx.sleep(0.001) + } + +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.udp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +peer set + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):13: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 16: bad request tries to close +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local test = require "test" + local sock = test.new_sock() + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to set peer: ", err) + else + ngx.say("peer set") + end + local function f() + local sock = test.get_sock() + sock:close() + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +local sock + +function new_sock() + sock = ngx.socket.udp() + return sock +end + +function get_sock() + return sock +end +--- stream_response +peer set + +--- error_log eval +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ + +--- no_error_log +[alert] + + + +=== TEST 17: the upper bound of port range should be 2^16 - 1 +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.udp() + local ok, err = sock:setpeername("127.0.0.1", 65536) + if not ok then + ngx.say("failed to connect: ", err) + end + } +--- stream_response +failed to connect: bad port number: 65536 +--- no_error_log +[error] + + + +=== TEST 18: send boolean and nil +--- stream_server_config + content_by_lua_block { + local socket = ngx.socket + local udp = socket.udp() + local port = ngx.var.port + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local function send(data) + local bytes, err = udp:send(data) + if not bytes then + ngx.say("failed to send: ", err) + return + end + ngx.say("sent ok") + end + + send(true) + send(false) + send(nil) + } +--- stream_response +sent ok +sent ok +sent ok +--- no_error_log +[error] +--- grep_error_log eval +qr/sendto: fd:\d+ \d+ of \d+/ +--- grep_error_log_out eval +qr/sendto: fd:\d+ 4 of 4 +sendto: fd:\d+ 5 of 5 +sendto: fd:\d+ 3 of 3/ +--- log_level: debug + + + +=== TEST 19: UDP socket GC'ed in preread phase without Lua content phase +--- stream_server_config + preread_by_lua_block { + do + local udpsock = ngx.socket.udp() + + local res, err = udpsock:setpeername("127.0.0.1", 1234) + if not res then + ngx.log(ngx.ERR, err) + end + end + + ngx.timer.at(0, function() + collectgarbage() + ngx.log(ngx.WARN, "GC cycle done") + end) + } + + return 1; + +--- stream_response chomp +1 +--- no_error_log +[error] +--- error_log +cleanup lua udp socket upstream request +GC cycle done diff --git a/src/deps/src/stream-lua-nginx-module/t/089-phase.t b/src/deps/src/stream-lua-nginx-module/t/089-phase.t new file mode 100644 index 000000000..c00773e1f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/089-phase.t @@ -0,0 +1,94 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 2); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: get_phase in init_by_lua +--- stream_config + init_by_lua_block { phase = ngx.get_phase() } +--- stream_server_config + content_by_lua_block { + ngx.say(phase) + } +--- stream_response +init + + + +=== TEST 2: get_phase in access_by_lua +TODO +--- SKIP +--- stream_server_config + access_by_lua_block { + ngx.say(ngx.get_phase()) + ngx.exit(200) + } +--- stream_response +access + + + +=== TEST 3: get_phase in content_by_lua +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.get_phase()) + } +--- stream_response +content + + + +=== TEST 4: get_phase in log_by_lua_block +TODO +--- SKIP +--- stream_server_config + echo "OK"; + log_by_lua_block { + ngx.log(ngx.ERR, ngx.get_phase()) + } +--- error_log +log + + + +=== TEST 5: get_phase in ngx.timer callback +--- stream_server_config + content_by_lua_block { + local function f() + ngx.log(ngx.WARN, "current phase: ", ngx.get_phase()) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to add timer: ", err) + end + } +--- no_error_log +[error] +--- error_log +current phase: timer + + + +=== TEST 6: get_phase in init_worker_by_lua +--- stream_config + init_worker_by_lua_block { phase = ngx.get_phase() } +--- stream_server_config + content_by_lua_block { + ngx.say(phase) + } +--- stream_response +init_worker +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/090-log-socket-errors.t b/src/deps/src/stream-lua-nginx-module/t/090-log-socket-errors.t new file mode 100644 index 000000000..266b7c324 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/090-log-socket-errors.t @@ -0,0 +1,94 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: log socket errors off (tcp) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_socket_connect_timeout 1ms; + lua_socket_log_errors off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + ngx.say(err) + } + +--- config +--- stream_response +timeout +--- no_error_log +[error] + + + +=== TEST 2: log socket errors on (tcp) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_socket_connect_timeout 1ms; + lua_socket_log_errors on; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.2", 12345) + ngx.say(err) + } + +--- config +--- stream_response +timeout +--- error_log +stream lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 + + + +=== TEST 3: log socket errors on (udp) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_socket_log_errors on; + lua_socket_read_timeout 1ms; + content_by_lua_block { + local sock = ngx.socket.udp() + local ok, err = sock:setpeername("127.0.0.2", 12345) + ok, err = sock:receive() + ngx.say(err) + } + +--- config +--- stream_response +timeout +--- error_log +lua udp socket read timed out + + + +=== TEST 4: log socket errors off (udp) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_socket_log_errors off; + lua_socket_read_timeout 1ms; + content_by_lua_block { + local sock = ngx.socket.udp() + local ok, err = sock:setpeername("127.0.0.2", 12345) + ok, err = sock:receive() + ngx.say(err) + } + +--- config +--- stream_response +timeout +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/091-coroutine.t b/src/deps/src/stream-lua-nginx-module/t/091-coroutine.t new file mode 100644 index 000000000..fa6b85a78 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/091-coroutine.t @@ -0,0 +1,1406 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 27); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +our $StapScript = <<'_EOC_'; +global ids, cur + +function gen_id(k) { + if (ids[k]) return ids[k] + ids[k] = ++cur + return cur +} + +F(ngx_http_handler) { + delete ids + cur = 0 +} + +/* +F(ngx_http_lua_run_thread) { + id = gen_id($ctx->cur_co) + printf("run thread %d\n", id) +} + +probe process("/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2").function("lua_resume") { + id = gen_id($L) + printf("lua resume %d\n", id) +} +*/ + +M(http-lua-user-coroutine-resume) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("resume %x in %x\n", c, p) +} + +M(http-lua-thread-yield) { + println("thread yield") +} + +/* +F(ngx_http_lua_coroutine_yield) { + printf("yield %x\n", gen_id($L)) +} +*/ + +M(http-lua-user-coroutine-yield) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("yield %x in %x\n", c, p) +} + +F(ngx_http_lua_atpanic) { + printf("lua atpanic(%d):", gen_id($L)) + print_ubacktrace(); +} + +M(http-lua-user-coroutine-create) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("create %x in %x\n", c, p) +} + +F(ngx_http_lua_ngx_exec) { println("exec") } + +F(ngx_http_lua_ngx_exit) { println("exit") } +F(ngx_http_lua_ffi_exit) { println("exit") } +_EOC_ + +no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: basic coroutine print +--- stream_server_config + content_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + function f() + local cnt = 0 + for i = 1, 20 do + ngx.say("Hello, ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i=1,3 do + cr(c) + ngx.say("***") + end + } + +--- config +--- stap2 eval: $::StapScript +--- stream_response +Hello, 0 +*** +Hello, 1 +*** +Hello, 2 +*** +--- no_error_log +[error] + + + +=== TEST 2: basic coroutine2 +--- stream_server_config + content_by_lua_block { + function f(fid) + local cnt = 0 + while true do + ngx.say("cc", fid, ": ", cnt) + coroutine.yield() + cnt = cnt + 1 + end + end + + local ccs = {} + for i=1,3 do + ccs[#ccs+1] = coroutine.create(function() f(i) end) + end + + for i=1,9 do + local cc = table.remove(ccs, 1) + coroutine.resume(cc) + ccs[#ccs+1] = cc + end + } + +--- config +--- stream_response +cc1: 0 +cc2: 0 +cc3: 0 +cc1: 1 +cc2: 1 +cc3: 1 +cc1: 2 +cc2: 2 +cc3: 2 +--- no_error_log +[error] + + + +=== TEST 3: basic coroutine and cosocket +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + function worker(url) + local sock = ngx.socket.tcp() + local ok, err = sock:connect(url, 80) + coroutine.yield() + if not ok then + ngx.say("failed to connect to: ", url, " error: ", err) + return + end + coroutine.yield() + ngx.say("successfully connected to: ", url) + sock:close() + end + + local urls = { + "agentzh.org", + "openresty.com", + "openresty.org" + } + + local ccs = {} + for i, url in ipairs(urls) do + local cc = coroutine.create(function() worker(url) end) + ccs[#ccs+1] = cc + end + + while true do + if #ccs == 0 then break end + local cc = table.remove(ccs, 1) + local ok = coroutine.resume(cc) + if ok then + ccs[#ccs+1] = cc + end + end + + ngx.say("*** All Done ***") + } + +--- config +--- stream_response +successfully connected to: agentzh.org +successfully connected to: openresty.com +successfully connected to: openresty.org +*** All Done *** +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 4: coroutine.wrap(generate prime numbers) +--- stream_server_config + content_by_lua_block { + -- generate all the numbers from 2 to n + function gen (n) + return coroutine.wrap(function () + for i=2,n do coroutine.yield(i) end + end) + end + + -- filter the numbers generated by g, removing multiples of p + function filter (p, g) + return coroutine.wrap(function () + while 1 do + local n = g() + if n == nil then return end + if math.fmod(n, p) ~= 0 then coroutine.yield(n) end + end + end) + end + + N = 10 + x = gen(N) -- generate primes up to N + while 1 do + local n = x() -- pick a number until done + if n == nil then break end + ngx.say(n) -- must be a prime number + x = filter(n, x) -- now remove its multiples + end + } + +--- config +--- stream_response +2 +3 +5 +7 +--- no_error_log +[error] + + + +=== TEST 5: coroutine.wrap(generate prime numbers,reset create and resume) +--- stream_server_config + content_by_lua_block { + coroutine.create = nil + coroutine.resume = nil + -- generate all the numbers from 2 to n + function gen (n) + return coroutine.wrap(function () + for i=2,n do coroutine.yield(i) end + end) + end + + -- filter the numbers generated by g, removing multiples of p + function filter (p, g) + return coroutine.wrap(function () + while 1 do + local n = g() + if n == nil then return end + if math.fmod(n, p) ~= 0 then coroutine.yield(n) end + end + end) + end + + N = 10 + x = gen(N) -- generate primes up to N + while 1 do + local n = x() -- pick a number until done + if n == nil then break end + ngx.say(n) -- must be a prime number + x = filter(n, x) -- now remove its multiples + end + } + +--- config +--- stream_response +2 +3 +5 +7 +--- no_error_log +[error] + + + +=== TEST 6: coroutine.wrap(generate fib) +--- stream_server_config + content_by_lua_block { + function generatefib (n) + return coroutine.wrap(function () + local a,b = 1, 1 + while a <= n do + coroutine.yield(a) + a, b = b, a+b + end + end) + end + + -- In lua, because OP_TFORLOOP uses luaD_call to execute the iterator function, + -- and luaD_call is a C function, so we can not yield in the iterator function. + -- So the following case(using for loop) will be failed. + -- Luajit is OK. + if package.loaded["jit"] then + for i in generatefib(1000) do ngx.say(i) end + else + local gen = generatefib(1000) + while true do + local i = gen() + if not i then break end + ngx.say(i) + end + end + } + +--- config +--- stream_response +1 +1 +2 +3 +5 +8 +13 +21 +34 +55 +89 +144 +233 +377 +610 +987 +--- no_error_log +[error] + + + +=== TEST 7: coroutine wrap and cosocket +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + function worker(url) + local sock = ngx.socket.tcp() + local ok, err = sock:connect(url, 80) + coroutine.yield() + if not ok then + ngx.say("failed to connect to: ", url, " error: ", err) + return + end + coroutine.yield() + ngx.say("successfully connected to: ", url) + sock:close() + end + + local urls = { + "agentzh.org", + "openresty.com", + "openresty.org" + } + + local cfs = {} + for i, url in ipairs(urls) do + local cf = coroutine.wrap(function() worker(url) end) + cfs[#cfs+1] = cf + end + + for i=1,3 do cfs[i]() end + for i=1,3 do cfs[i]() end + for i=1,3 do cfs[i]() end + + ngx.say("*** All Done ***") + } + +--- config +--- stream_response +successfully connected to: agentzh.org +successfully connected to: openresty.com +successfully connected to: openresty.org +*** All Done *** +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 8: coroutine status, running +--- stream_server_config + content_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + local st, rn = coroutine.status, coroutine.running + + function f(self) + local cnt = 0 + if rn() ~= self then ngx.say("error"); return end + ngx.say("running: ", st(self)) --running + cy() + local c = cc(function(father) + ngx.say("normal: ", st(father)) + end) -- normal + cr(c, self) + end + + local c = cc(f) + ngx.say("suspended: ", st(c)) -- suspended + cr(c, c) + ngx.say("suspended: ", st(c)) -- suspended + cr(c, c) + ngx.say("dead: ", st(c)) -- dead + } + +--- config +--- stream_response +suspended: suspended +running: running +suspended: suspended +normal: normal +dead: dead +--- no_error_log +[error] + + + +=== TEST 9: entry coroutine yielded will be resumed immediately +--- stream_server_config + content_by_lua_block { + ngx.say("[", {coroutine.yield()}, "]") + ngx.say("[", {coroutine.yield(1, "a")}, "]") + ngx.say("done") + } + +--- config +--- stream_response +[] +[] +done +--- no_error_log +[error] + + + +=== TEST 10: thread traceback (multi-thread) +Note: only coroutine.wrap propagates errors to the parent coroutine +(and thus produces a traceback) +--- stream_server_config + content_by_lua_block { + local f = function(cr) coroutine.resume(cr) end + -- emit a error + local g = function() unknown.unknown = 1 end + local l1 = coroutine.wrap(f) + local l2 = coroutine.wrap(g) + local l3 = coroutine.wrap(function() l1(l2) end) + l3() + ngx.say("hello") + } +--- stream_response +--- error_log eval +["stack traceback:", "coroutine 0:", "coroutine 1:", "coroutine 2:"] +--- no_error +[crit] + + + +=== TEST 11: thread traceback (only the entry thread) +--- stream_server_config + content_by_lua_block { + -- emit a error + unknown.unknown = 1 + ngx.say("hello") + } + +--- config +--- error_log eval +["stack traceback:", "coroutine 0:"] + + + +=== TEST 12: bug: resume dead coroutine with args +--- stream_server_config + content_by_lua_block { + function print(...) + local args = {...} + local is_first = true + for i,v in ipairs(args) do + if is_first then + is_first = false + else + ngx.print(" ") + end + ngx.print(v) + end + ngx.print("\n") + end + + function foo (a) + print("foo", a) + return coroutine.yield(2*a) + end + + co = coroutine.create(function (a,b) + print("co-body", a, b) + local r = foo(a+1) + print("co-body", r) + local r, s = coroutine.yield(a+b, a-b) + print("co-body", r, s) + return b, "end" + end) + + print("main", coroutine.resume(co, 1, 10)) + print("main", coroutine.resume(co, "r")) + print("main", coroutine.resume(co, "x", "y")) + print("main", coroutine.resume(co, "x", "y")) + } + +--- config +--- stream_response +co-body 1 10 +foo 2 +main true 4 +co-body r +main true 11 -9 +co-body x y +main true 10 end +main false cannot resume dead coroutine +--- no_error_log +[error] + + + +=== TEST 13: deeply nested coroutines +--- stream_server_config + content_by_lua_block { + local create = coroutine.create + local resume = coroutine.resume + local yield = coroutine.yield + function f() + ngx.say("f begin") + yield() + local c2 = create(g) + ngx.say("1: resuming c2") + resume(c2) + ngx.say("2: resuming c2") + resume(c2) + yield() + ngx.say("3: resuming c2") + resume(c2) + ngx.say("f done") + end + + function g() + ngx.say("g begin") + yield() + ngx.say("g going") + yield() + ngx.say("g done") + end + + local c1 = create(f) + ngx.say("1: resuming c1") + resume(c1) + ngx.say("2: resuming c1") + resume(c1) + ngx.say("3: resuming c1") + resume(c1) + ngx.say("main done") + } + +--- config +--- stream_response +1: resuming c1 +f begin +2: resuming c1 +1: resuming c2 +g begin +2: resuming c2 +g going +3: resuming c1 +3: resuming c2 +g done +f done +main done +--- no_error_log +[error] + + + +=== TEST 14: using ngx.exit in user coroutines +--- stream_server_config + content_by_lua_block { + local create = coroutine.create + local resume = coroutine.resume + local yield = coroutine.yield + + local code = 400 + + function f() + local c2 = create(g) + yield() + code = code + 1 + resume(c2) + yield() + resume(c2) + end + + function g() + code = code + 1 + yield() + code = code + 1 + ngx.exit(code) + end + + local c1 = create(f) + resume(c1) + resume(c1) + resume(c1) + ngx.say("done") + } + +--- config +--- stap eval: $::StapScript +--- stap_out +create 2 in 1 +resume 2 in 1 +create 3 in 2 +yield 2 in 1 +resume 2 in 1 +resume 3 in 2 +yield 3 in 2 +yield 2 in 1 +resume 2 in 1 +resume 3 in 2 +exit + +--- stream_response +--- no_error_log +[error] + + + +=== TEST 15: resume coroutines from within another one that is not its parent +--- stream_server_config + content_by_lua_block { + local print = ngx.say + + local c1, c2 + + function f() + print("f 1") + print(coroutine.resume(c2)) + print("f 2") + end + + function g() + print("g 1") + -- print(coroutine.resume(c1)) + print("g 2") + end + + c1 = coroutine.create(f) + c2 = coroutine.create(g) + + coroutine.resume(c1) + } + +--- config +--- stream_response +f 1 +g 1 +g 2 +true +f 2 +--- no_error_log +[error] + + + +=== TEST 16: infinite recursive calls of coroutine.resume +--- stream_server_config + content_by_lua_block { + local print = ngx.say + + local c1, c2 + + function f() + print("f 1") + print(coroutine.resume(c2)) + print("f 2") + end + + function g() + print("g 1") + print(coroutine.resume(c1)) + print("g 2") + end + + c1 = coroutine.create(f) + c2 = coroutine.create(g) + + coroutine.resume(c1) + } + +--- config +--- stap2 eval: $::StapScript +--- stream_response +f 1 +g 1 +falsecannot resume normal coroutine +g 2 +true +f 2 +--- no_error_log +[error] + + + +=== TEST 17: resume running (entry) coroutines +--- stream_server_config + content_by_lua_block { + ngx.say(coroutine.status(coroutine.running())) + ngx.say(coroutine.resume(coroutine.running())) + } + +--- config +--- stream_response +running +falsecannot resume running coroutine +--- no_error_log +[error] + + + +=== TEST 18: resume running (user) coroutines +--- stream_server_config + content_by_lua_block { + local co + function f() + ngx.say("f: ", coroutine.status(co)) + ngx.say("f: ", coroutine.resume(co)) + end + co = coroutine.create(f) + ngx.say("chunk: ", coroutine.status(co)) + ngx.say("chunk: ", coroutine.resume(co)) + } + +--- config +--- stream_response +chunk: suspended +f: running +f: falsecannot resume running coroutine +chunk: true +--- no_error_log +[error] + + + +=== TEST 19: user coroutine end with errors, and the parent coroutine gets the right status +--- stream_server_config + content_by_lua_block { + local co + function f() + error("bad") + end + co = coroutine.create(f) + ngx.say("child: resume: ", coroutine.resume(co)) + ngx.say("child: status: ", coroutine.status(co)) + ngx.say("parent: status: ", coroutine.status(coroutine.running())) + } + +--- config +--- stream_response eval +qr/^child: resume: falsecontent_by_lua\(nginx\.conf:\d+\):4: bad +child: status: dead +parent: status: running +$/s +--- no_error_log +[error] + + + +=== TEST 20: entry coroutine is yielded by hand and still gets the right status +--- stream_server_config + content_by_lua_block { + local co = coroutine.running() + ngx.say("status: ", coroutine.status(co)) + coroutine.yield(co) + ngx.say("status: ", coroutine.status(co)) + } + +--- config +--- stream_response +status: running +status: running +--- no_error_log +[error] + + + +=== TEST 21: github issue #208: coroutine as iterator doesn't work +--- stream_server_config + content_by_lua_block { + local say = ngx.say + local wrap, yield = coroutine.wrap, coroutine.yield + + local function it(it_state) + for i = 1, it_state.i do + yield(it_state.path, tostring(i)) + end + return nil + end + + local function it_factory(path) + local it_state = { i = 10, path = path } + return wrap(it), it_state + end + + --[[ + for path, value in it_factory("test") do + say(path, value) + end + ]] + + do + local f, s, var = it_factory("test") + while true do + local path, value = f(s, var) + var = path + if var == nil then break end + say(path, value) + end + end + } + +--- config +--- more_headers +Cookie: abc=32 +--- stap2 eval: $::StapScript +--- stream_response +test1 +test2 +test3 +test4 +test5 +test6 +test7 +test8 +test9 +test10 +--- no_error_log +[error] + + + +=== TEST 22: init_by_lua + our own coroutines in content_by_lua +--- stream_config + init_by_lua_block { return } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + function worker(url) + local sock = ngx.socket.tcp() + local ok, err = sock:connect(url, 80) + coroutine.yield() + if not ok then + ngx.say("failed to connect to: ", url, " error: ", err) + return + end + coroutine.yield() + ngx.say("successfully connected to: ", url) + sock:close() + end + + local urls = { + "agentzh.org", + } + + local ccs = {} + for i, url in ipairs(urls) do + local cc = coroutine.create(function() worker(url) end) + ccs[#ccs+1] = cc + end + + while true do + if #ccs == 0 then break end + local cc = table.remove(ccs, 1) + local ok = coroutine.resume(cc) + if ok then + ccs[#ccs+1] = cc + end + end + + ngx.say("*** All Done ***") + } + +--- config +--- stream_response +successfully connected to: agentzh.org +*** All Done *** +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 23: init_by_lua_file + our own coroutines in content_by_lua +--- stream_config + init_by_lua_file html/init.lua; + +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + function worker(url) + local sock = ngx.socket.tcp() + local ok, err = sock:connect(url, 80) + coroutine.yield() + if not ok then + ngx.say("failed to connect to: ", url, " error: ", err) + return + end + coroutine.yield() + ngx.say("successfully connected to: ", url) + sock:close() + end + + local urls = { + "agentzh.org" + } + + local ccs = {} + for i, url in ipairs(urls) do + local cc = coroutine.create(function() worker(url) end) + ccs[#ccs+1] = cc + end + + while true do + if #ccs == 0 then break end + local cc = table.remove(ccs, 1) + local ok = coroutine.resume(cc) + if ok then + ccs[#ccs+1] = cc + end + end + + ngx.say("*** All Done ***") + } + +--- config +--- user_files +>>> init.lua +return + +--- stream_response +successfully connected to: agentzh.org +*** All Done *** +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 24: mixing coroutine.* API between init_by_lua and other contexts (github #304) - init_by_lua +--- stream_config + init_by_lua_block { + co_wrap = coroutine.wrap + co_yield = coroutine.yield + } + +--- stream_server_config + content_by_lua_block { + function generator() + return co_wrap(function() + co_yield("data") + end) + end + + local co = generator() + local data = co() + ngx.say(data) + } + +--- config + +--- stap2 eval: $::StapScript +--- stream_response +data +--- no_error_log +[error] + + + +=== TEST 25: mixing coroutine.* API between init_by_lua and other contexts (github #304) - init_by_lua_file +--- stream_config + init_by_lua_file html/init.lua; + +--- stream_server_config + content_by_lua_block { + function generator() + return co_wrap(function() + co_yield("data") + end) + end + + local co = generator() + local data = co() + ngx.say(data) + } + +--- config + +--- user_files +>>> init.lua +co_wrap = coroutine.wrap +co_yield = coroutine.yield + +--- stap2 eval: $::StapScript +--- stream_response +data +--- no_error_log +[error] + + + +=== TEST 26: coroutine context collicisions +--- stream_server_config + content_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + function f() + return 3 + end + + for i = 1, 10 do + collectgarbage() + local c = cc(f) + if coroutine.status(c) == "dead" then + ngx.say("found a dead coroutine") + return + end + cr(c) + end + ngx.say("ok") + } + +--- config +--- stap2 eval: $::StapScript +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 27: require "coroutine" +--- stream_server_config + content_by_lua_block { + local coroutine = require "coroutine" + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + function f() + local cnt = 0 + for i = 1, 20 do + ngx.say("Hello, ", cnt) + ngx.sleep(0.001) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i=1,3 do + cr(c) + ngx.say("***") + end + } + +--- config +--- stap2 eval: $::StapScript +--- stream_response +Hello, 0 +*** +Hello, 1 +*** +Hello, 2 +*** +--- no_error_log +[error] + + + +=== TEST 28: coroutine.wrap propagates errors to parent coroutine +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + print("in wrapped coroutine") + error("something went wrong") + end) + + co() + + ngx.say("ok") + } +--- stream_response +--- error_log eval +[ + qr/\[notice\] .*? in wrapped coroutine/, + qr/\[error\] .*? lua entry thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):\d+: something went wrong/, + "stack traceback:", + "coroutine 0:", + "coroutine 1:" +] + + + +=== TEST 29: coroutine.wrap propagates nested errors to parent coroutine +Note: in this case, both the error message and the traceback are constructed +from co1's stack level. +--- stream_server_config + content_by_lua_block { + local co1 = coroutine.wrap(function() + error("something went wrong in co1") + end) + + local co2 = coroutine.wrap(function() + co1() + end) + + co2() + } +--- stream_response +--- error_log eval +[ + qr/\[error\] .*? lua entry thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):\d+: something went wrong in co1/, + "stack traceback:", + "coroutine 0:", + "coroutine 1:", + "coroutine 2:" +] + + + +=== TEST 30: coroutine.wrap propagates nested errors with stack level to parent coroutine +Note: in this case, the error message is constructed at the entry thread stack +level, and the traceback is constructed from co1's stack level. +--- stream_server_config + content_by_lua_block { + local co1 = coroutine.wrap(function() + error("something went wrong in co1", 2) + end) + + local co2 = coroutine.wrap(function() + co1() + end) + + co2() + } +--- stream_response +--- error_log eval +[ + qr/\[error\] .*? lua entry thread aborted: runtime error: something went wrong in co1/, + "stack traceback:", + "coroutine 0:", + "coroutine 1:", + "coroutine 2:" +] + + + +=== TEST 31: coroutine.wrap runtime errors do not log errors +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + print("in wrapped coroutine") + error("something went wrong") + end) + + co() + } +--- stream_response +--- no_error_log eval +[ + qr/\[error\] .*? lua coroutine: runtime error:/, + "[alert]", + "[crit]", + "[emerg]", + "[warn]", +] + + + +=== TEST 32: coroutine.wrap does not return status boolean on yield +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + coroutine.yield("ok", "err") + end) + + local ret1, ret2 = co() + + ngx.say(ret1, ", ", ret2) + } +--- stream_response +ok, err +--- no_error_log +[error] + + + +=== TEST 33: coroutine.wrap does not return status boolean on done +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() end) + + local ret1 = co() + + ngx.say(ret1) + } +--- stream_response +nil +--- no_error_log +[emerg] +[alert] +[crit] +[error] +[warn] + + + +=== TEST 34: coroutine.wrap does not return status boolean on error +--- SKIP: not supported +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + error("something went wrong") + end) + + local ret1, ret2 = pcall(co) + + ngx.say(ret1, ", ", ret2) + } +--- response_body +false, something went wrong +--- no_error_log +[error] + + + +=== TEST 35: coroutine.wrap creates different function refs +--- stream_server_config + content_by_lua_block { + local f = function() end + local co = coroutine.wrap(f) + local co2 = coroutine.wrap(f) + + ngx.say("co == co2: ", co == co2) + } +--- stream_response +co == co2: false +--- no_error_log +[emerg] +[alert] +[crit] +[error] +[warn] + + + +=== TEST 36: coroutine.wrap supports yielding and resuming +--- stream_server_config + content_by_lua_block { + local function f() + local cnt = 0 + for i = 1, 20 do + ngx.say("co yield: ", cnt) + coroutine.yield() + cnt = cnt + 1 + end + end + + local f = coroutine.wrap(f) + for i = 1, 3 do + ngx.say("co resume") + f() + end + } +--- stream_response +co resume +co yield: 0 +co resume +co yield: 1 +co resume +co yield: 2 +--- no_error_log +[error] + + + +=== TEST 37: coroutine.wrap return values +--- stream_server_config + content_by_lua_block { + local function f() + local cnt = 0 + for i = 1, 20 do + coroutine.yield(cnt, cnt + 1) + cnt = cnt + 1 + end + end + + local f = coroutine.wrap(f) + for i = 1, 3 do + ngx.say("co resume") + local ret1, ret2 = f() + ngx.say("co yield: ", ret1, ", ", ret2) + end + } +--- stream_response +co resume +co yield: 0, 1 +co resume +co yield: 1, 2 +co resume +co yield: 2, 3 +--- no_error_log +[error] + + + +=== TEST 38: coroutine.wrap arguments +--- stream_server_config + content_by_lua_block { + local function f(step) + local cnt = 0 + for i = 1, 20 do + ngx.say("co yield: ", cnt) + coroutine.yield() + cnt = cnt + step + end + end + + local f = coroutine.wrap(f) + for i = 1, 3 do + ngx.say("co resume") + f(i) + end + } +--- stream_response +co resume +co yield: 0 +co resume +co yield: 1 +co resume +co yield: 2 +--- no_error_log +[error] + + + +=== TEST 39: coroutine.wrap in init_by_lua propagates errors (orig coroutine.wrap) +--- stream_config + init_by_lua_block { + local co = coroutine.wrap(function() + print("in wrapped coroutine") + error("something went wrong") + end) + + local err = co() + + ngx.log(ngx.CRIT, "err: ", err) + } +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } +--- must_die +--- grep_error_log eval: qr/init_by_lua error: .*? something went wrong/ +--- grep_error_log_out +init_by_lua error: init_by_lua:7: init_by_lua:4: something went wrong + + + +=== TEST 40: coroutine.resume runtime errors do not log errors +--- stream_server_config + content_by_lua_block { + local function f() + error("something went wrong") + end + + local ret1, ret2 = coroutine.resume(coroutine.create(f)) + ngx.say(ret1) + ngx.say(ret2) + } +--- stream_response_like +false +content_by_lua\(nginx.conf:\d+\):\d+: something went wrong +--- no_error_log eval +[ + qr/\[error\] .*? lua coroutine: runtime error:", + "stack traceback:", +] diff --git a/src/deps/src/stream-lua-nginx-module/t/093-uthread-spawn.t b/src/deps/src/stream-lua-nginx-module/t/093-uthread-spawn.t new file mode 100644 index 000000000..91267bf58 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/093-uthread-spawn.t @@ -0,0 +1,819 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 + 1); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; + +#no_shuffle(); +worker_connections(256); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: simple user thread without I/O +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +terminate 1: ok +delete thread 2 +delete thread 1 + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 2: two simple user threads without I/O +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("in thread 1") + end + + function g() + ngx.say("in thread 2") + end + + ngx.say("before 1") + ngx.thread.spawn(f) + ngx.say("after 1") + + ngx.say("before 2") + ngx.thread.spawn(g) + ngx.say("after 2") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +create 3 in 1 +spawn user thread 3 in 1 +terminate 3: ok +terminate 1: ok +delete thread 2 +delete thread 3 +delete thread 1 + +--- stream_response +before 1 +in thread 1 +after 1 +before 2 +in thread 2 +after 2 +--- no_error_log +[error] + + + +=== TEST 3: simple user thread with sleep +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("before sleep") + ngx.sleep(0.1) + ngx.say("after sleep") + end + + ngx.say("before thread create") + ngx.thread.spawn(f) + ngx.say("after thread create") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +before thread create +before sleep +after thread create +after sleep +--- no_error_log +[error] + + + +=== TEST 4: two simple user threads with sleep +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("1: before sleep") + ngx.sleep(0.2) + ngx.say("1: after sleep") + end + + function g() + ngx.say("2: before sleep") + ngx.sleep(0.1) + ngx.say("2: after sleep") + end + + ngx.say("1: before thread create") + ngx.thread.spawn(f) + ngx.say("1: after thread create") + + ngx.say("2: before thread create") + ngx.thread.spawn(g) + ngx.say("2: after thread create") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 1: ok +delete thread 1 +terminate 3: ok +delete thread 3 +terminate 2: ok +delete thread 2 + +--- wait: 0.1 +--- stream_response +1: before thread create +1: before sleep +1: after thread create +2: before thread create +2: before sleep +2: after thread create +2: after sleep +1: after sleep +--- no_error_log +[error] + + + +=== TEST 5: error in user thread +--- stream_server_config + content_by_lua_block { + function f() + ngx.blah() + end + + ngx.thread.spawn(f) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: fail +terminate 1: ok +delete thread 2 +delete thread 1 + +--- stream_response +after +--- error_log eval +qr/stream lua user thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):3: attempt to call field 'blah' \(a nil value\)/ + + + +=== TEST 6: nested user threads +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("before g") + ngx.thread.spawn(g) + ngx.say("after g") + end + + function g() + ngx.say("hello in g()") + end + + ngx.say("before f") + ngx.thread.spawn(f) + ngx.say("after f") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 3: ok +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 3 +delete thread 2 + +--- stream_response +before f +before g +hello in g() +after f +after g +--- no_error_log +[error] + + + +=== TEST 7: nested user threads (with I/O) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("before g") + ngx.thread.spawn(g) + ngx.say("after g") + end + + function g() + ngx.sleep(0.1) + ngx.say("hello in g()") + end + + ngx.say("before f") + ngx.thread.spawn(f) + ngx.say("after f") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +before f +before g +after f +after g +hello in g() +--- no_error_log +[error] + + + +=== TEST 8: coroutine status of a running user thread +--- stream_server_config + content_by_lua_block { + local co + function f() + co = coroutine.running() + ngx.sleep(0.1) + end + + ngx.thread.spawn(f) + ngx.say("status: ", coroutine.status(co)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +status: running +--- no_error_log +[error] + + + +=== TEST 9: coroutine status of a dead user thread +--- stream_server_config + content_by_lua_block { + local co + function f() + co = coroutine.running() + end + + ngx.thread.spawn(f) + ngx.say("status: ", coroutine.status(co)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +terminate 1: ok +delete thread 2 +delete thread 1 + +--- stream_response +status: zombie +--- no_error_log +[error] + + + +=== TEST 10: coroutine status of a "normal" user thread +--- stream_server_config + content_by_lua_block { + local co + function f() + co = coroutine.running() + local co2 = coroutine.create(g) + coroutine.resume(co2) + end + + function g() + ngx.sleep(0.1) + end + + ngx.thread.spawn(f) + ngx.say("status: ", coroutine.status(co)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 2 +terminate 1: ok +delete thread 1 +terminate 3: ok +terminate 2: ok +delete thread 2 + +--- stream_response +status: normal +--- no_error_log +[error] + + + +=== TEST 11: creating user threads in a user coroutine +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("before g") + ngx.thread.spawn(g) + ngx.say("after g") + end + + function g() + ngx.say("hello in g()") + end + + ngx.say("before f") + local co = coroutine.create(f) + coroutine.resume(co) + ngx.say("after f") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 3: ok +terminate 2: ok +delete thread 3 +terminate 1: ok +delete thread 1 + +--- stream_response +before f +before g +hello in g() +after g +after f +--- no_error_log +[error] + + + +=== TEST 12: manual time slicing between a user thread and the entry thread +--- stream_server_config + content_by_lua_block { + local yield = coroutine.yield + + function f() + local self = coroutine.running() + ngx.say("f 1") + yield(self) + ngx.say("f 2") + yield(self) + ngx.say("f 3") + end + + local self = coroutine.running() + ngx.say("0") + yield(self) + ngx.say("1") + ngx.thread.spawn(f) + ngx.say("2") + yield(self) + ngx.say("3") + yield(self) + ngx.say("4") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +terminate 1: ok +delete thread 2 +delete thread 1 + +--- stream_response +0 +1 +f 1 +2 +f 2 +3 +f 3 +4 +--- no_error_log +[error] + + + +=== TEST 13: manual time slicing between two user threads +--- stream_server_config + content_by_lua_block { + local yield = coroutine.yield + + function f() + local self = coroutine.running() + ngx.say("f 1") + yield(self) + ngx.say("f 2") + yield(self) + ngx.say("f 3") + end + + function g() + local self = coroutine.running() + ngx.say("g 1") + yield(self) + ngx.say("g 2") + yield(self) + ngx.say("g 3") + end + + ngx.thread.spawn(f) + ngx.thread.spawn(g) + ngx.say("done") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +f 1 +g 1 +f 2 +done +g 2 +f 3 +g 3 +--- no_error_log +[error] + + + +=== TEST 14: entry thread and a user thread flushing at the same time +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + coroutine.yield(coroutine.running) + ngx.flush(true) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + ngx.flush(true) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 15: two user threads flushing at the same time +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello from f") + ngx.flush(true) + end + + function g() + ngx.say("hello from g") + ngx.flush(true) + end + + ngx.thread.spawn(f) + ngx.thread.spawn(g) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out_like +^(?:create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3|create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +create 3 in 1 +spawn user thread 3 in 1 +terminate 3: ok +terminate 1: ok +delete thread 2 +delete thread 3 +delete thread 1)$ + +--- stream_response +hello from f +hello from g +--- no_error_log +[error] + + + +=== TEST 16: user threads + ngx.socket.tcp +--- stream_server_config + content_by_lua_block { + function f() + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.say("failed to send query: ", err) + return + end + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("received: ", line) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +before +after +received: OK +--- no_error_log +[error] + + + +=== TEST 17: user threads + ngx.socket.udp +--- stream_server_config + content_by_lua_block { + function f() + local sock = ngx.socket.udp() + local ok, err = sock:setpeername("127.0.0.1", 12345) + local bytes, err = sock:send("blah") + if not bytes then + ngx.say("failed to send query: ", err) + return + end + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("received: ", line) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out_like chop +^(?:create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +|create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +terminate 1: ok +delete thread 2 +delete thread 1 +)$ + +--- udp_listen: 12345 +--- udp_query: blah +--- udp_reply: hello udp +--- stream_response_like chop +^(?:before +after +received: hello udp +|before +received: hello udp +after)$ + +--- no_error_log +[error] + + + +=== TEST 18: simple user thread with ngx.req.socket() +--- stream_server_config + content_by_lua_block { + function f() + local sock = assert(ngx.req.socket()) + local body, err = sock:receive(11) + if not body then + ngx.say("failed to read body: ", err) + return + end + + ngx.say("body: ", body) + end + + ngx.say("before") + ngx.flush(true) + ngx.thread.spawn(f) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out_like chop +^(?:create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +terminate 1: ok +delete thread 2 +delete thread 1|create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2)$ + +--- stream_request chomp +hello world +--- stream_response_like chop +^(?:before +body: hello world +after|before +after +body: hello world)$ + +--- no_error_log +[error] + + + +=== TEST 19: simple user thread with args +--- stream_server_config + content_by_lua_block { + function f(a, b) + ngx.say("hello ", a, " and ", b) + end + + ngx.say("before") + ngx.thread.spawn(f, "foo", 3.14) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +terminate 1: ok +delete thread 2 +delete thread 1 + +--- stream_response +before +hello foo and 3.14 +after +--- no_error_log +[error] + + + +=== TEST 20: simple user thread without I/O +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("f") + end + + ngx.thread.spawn(f) + collectgarbage() + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +f +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/094-uthread-exit.t b/src/deps/src/stream-lua-nginx-module/t/094-uthread-exit.t new file mode 100644 index 000000000..4cb63e4f9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/094-uthread-exit.t @@ -0,0 +1,923 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; +$ENV{TEST_NGINX_REDIS_PORT} ||= '6379'; + +#no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: exit in user thread (entry thread is still pending to run) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + ngx.sleep(1) + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +M(timer-add) { + if ($arg2 == 1000) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 1000) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 1000) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +delete thread 1 + +--- stream_response +before +hello in thread +--- no_error_log +[error] + + + +=== TEST 2: exit in user thread (entry thread is still pending on ngx.sleep) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + ngx.sleep(1) + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 1000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 1000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_sleep_cleanup) { + println("lua sleep cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 1000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua sleep cleanup +delete timer 1000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 3: exit in a user thread (another user thread is still pending on ngx.sleep) +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("f") + ngx.exit(0) + end + + function g() + ngx.sleep(1) + ngx.say("g") + end + + ngx.thread.spawn(f) + ngx.thread.spawn(g) + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 1000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 1000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_sleep_cleanup) { + println("lua sleep cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +create 3 in 1 +spawn user thread 3 in 1 +add timer 1000 +terminate 1: ok +delete thread 1 +expire timer 100 +terminate 2: ok +delete thread 2 +lua sleep cleanup +delete timer 1000 +delete thread 3 +free request + +--- stream_response +end +f +--- no_error_log +[error] + + + +=== TEST 4: exit in user thread (entry already quits) +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("exiting the user thread") + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- wait: 0.1 +--- stream_response +before +after +exiting the user thread +--- no_error_log +[error] + + + +=== TEST 5: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.tcp) +--- stream_server_config + resolver 127.0.0.2:12345; + resolver_timeout 12s; + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.001) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.tcp() + local ok, err = sock:connect("agentzh.org", 80) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +F(ngx_resolve_name) { + printf("resolving %s\n", user_string_n($ctx->name->data, $ctx->name->len)) +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 1) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 1) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 12000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 1) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_tcp_resolve_cleanup) { + println("lua tcp resolve cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 1 +resolving agentzh.org +add timer 12000 +expire timer 1 +terminate 2: ok +delete thread 2 +lua tcp resolve cleanup +delete timer 12000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 6: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.udp) +--- stream_server_config + resolver 127.0.0.2:12345; + resolver_timeout 12s; + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.001) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.udp() + local ok, err = sock:setpeername("agentzh.org", 80) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +F(ngx_resolve_name) { + printf("resolving %s\n", user_string_n($ctx->name->data, $ctx->name->len)) +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 1) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 1) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 12000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 1) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_udp_resolve_cleanup) { + println("lua udp resolve cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 1 +resolving agentzh.org +add timer 12000 +expire timer 1 +terminate 2: ok +delete thread 2 +lua udp resolve cleanup +delete timer 12000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 7: exit in user thread (entry thread is still pending on tcpsock:connect) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.tcp() + sock:settimeout(12000) + local ok, err = sock:connect("127.0.0.2", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 12000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_coctx_cleanup) { + println("lua tcp socket cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 12000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua tcp socket cleanup +delete timer 12000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 8: exit in user thread (entry thread is still pending on tcpsock:receive) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, ok = sock:send("blpop not_exists 2\\r\\n") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + sock:settimeout(12000) + + local data, err = sock:receive() + if not data then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_coctx_cleanup) { + println("lua tcp socket cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 12000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua tcp socket cleanup +delete timer 12000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 9: exit in user thread (entry thread is still pending on tcpsock:receiveuntil's iterator) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, ok = sock:send("blpop not_exists 2\\r\\n") + if not bytes then + ngx.say("failed to send: ", err) + return + end + + local it, err = sock:receiveuntil("\\r\\n") + if not it then + ngx.say("failed to receive until: ", err) + return + end + + sock:settimeout(12000) + + local data, err = it() + if not data then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_coctx_cleanup) { + println("lua tcp socket cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 12000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua tcp socket cleanup +delete timer 12000 +delete thread 1 +free request + +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 10: exit in user thread (entry thread is still pending on udpsock:receive) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + local sock = ngx.socket.udp() + + local ok, err = sock:setpeername("8.8.8.8", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + sock:settimeout(12000) + + local data, err = sock:receive() + if not data then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("end") + } +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_udp_socket_cleanup) { + println("lua udp socket cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 12000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua udp socket cleanup +delete timer 12000 +delete thread 1 +free request + +--- wait: 0.1 +--- stream_response +before +hello in thread +after +--- no_error_log +[error] + + + +=== TEST 11: exit in user thread (entry thread is still pending on reqsock:receive) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + ngx.say("before") + ngx.thread.spawn(f) + ngx.say("after") + ngx.flush(true) + local sock = ngx.req.socket() + + sock:settimeout(12000) + + local data, err = sock:receive(1024) + if not data then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("end") + } + +--- stap2 eval: $::StapScript +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_http_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 12000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 12000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_http_lua_coctx_cleanup) { + println("lua tcp socket cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +add timer 100 +add timer 12000 +expire timer 100 +terminate 2: ok +delete thread 2 +lua tcp socket cleanup +delete timer 12000 +delete thread 1 +free request + +--- wait: 0.1 +--- stream_response +before +hello in thread +after +--- no_error_log +[error] +--- timeout: 6 diff --git a/src/deps/src/stream-lua-nginx-module/t/098-uthread-wait.t b/src/deps/src/stream-lua-nginx-module/t/098-uthread-wait.t new file mode 100644 index 000000000..f916ed54f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/098-uthread-wait.t @@ -0,0 +1,1251 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; + +#no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: simple user thread wait without I/O +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +hello in thread +thread created: zombie +done +--- no_error_log +[error] + + + +=== TEST 2: simple user thread wait with I/O +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to wait thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +thread created: running +hello in thread +done +--- no_error_log +[error] + + + +=== TEST 3: wait on uthreads on the reversed order of their termination +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("f: hello") + return "done" + end + + function g() + ngx.sleep(0.2) + ngx.say("g: hello") + return "done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("f thread created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("g thread created: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tg) + if not ok then + ngx.say("failed to wait g: ", res) + return + end + + ngx.say("g: ", res) + + ngx.say("f thread status: ", coroutine.status(tf)) + + ok, res = ngx.thread.wait(tf) + if not ok then + ngx.say("failed to wait f: ", res) + return + end + + ngx.say("f: ", res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 2: ok +terminate 3: ok +delete thread 3 +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +f thread created: running +g thread created: running +f: hello +g: hello +g: done +f thread status: zombie +f: done +--- no_error_log +[error] + + + +=== TEST 4: wait on uthreads on the exact order of their termination +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("f: hello") + return "done" + end + + function g() + ngx.sleep(0.2) + ngx.say("g: hello") + return "done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("f thread created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("g thread created: ", coroutine.status(tg)) + + ok, res = ngx.thread.wait(tf) + if not ok then + ngx.say("failed to wait f: ", res) + return + end + + ngx.say("f: ", res) + + ngx.say("g thread status: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tg) + if not ok then + ngx.say("failed to wait g: ", res) + return + end + + ngx.say("g: ", res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 +terminate 1: ok +delete thread 1 + +--- wait: 0.1 +--- stream_response +f thread created: running +g thread created: running +f: hello +f: done +g thread status: running +g: hello +g: done +--- no_error_log +[error] + + + +=== TEST 5: simple user thread wait without I/O (return multiple values) +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + return "done", 3.14 + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res1, res2 = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res1) + return + end + + ngx.say("res: ", res1, " ", res2) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +hello in thread +thread created: zombie +res: done 3.14 +--- no_error_log +[error] + + + +=== TEST 6: simple user thread wait with I/O, return multiple values +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("hello in thread") + return "done", 3.14 + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + local ok, res1, res2 = ngx.thread.wait(t) + if not ok then + ngx.say("failed to wait thread: ", res1) + return + end + + ngx.say("res: ", res1, " ", res2) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +thread created: running +hello in thread +res: done 3.14 +--- no_error_log +[error] + + + +=== TEST 7: simple user thread wait without I/O, throw errors +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + error("bad bad!") + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to wait thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: fail +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +hello in thread +thread created: zombie +failed to wait thread: bad bad! +--- error_log eval +qr/lua user thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):4: bad bad!/ + + + +=== TEST 8: simple user thread wait with I/O, throw errors +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("hello in thread") + error("bad bad!") + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to wait thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: fail +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +thread created: running +hello in thread +failed to wait thread: bad bad! +--- error_log eval +qr/lua user thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):5: bad bad!/ + + + +=== TEST 9: simple user thread wait without I/O (in a user coroutine) +--- stream_server_config + content_by_lua_block { + function g() + ngx.say("hello in thread") + return "done" + end + + function f() + local t, err = ngx.thread.spawn(g) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + ngx.say(res) + end + + local co = coroutine.create(f) + coroutine.resume(co) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 3: ok +delete thread 3 +terminate 2: ok +terminate 1: ok +delete thread 1 + +--- stream_response +hello in thread +thread created: zombie +done +--- no_error_log +[error] + + + +=== TEST 10: simple user thread wait with I/O (in a user coroutine) +--- stream_server_config + content_by_lua_block { + function g() + ngx.sleep(0.1) + ngx.say("hello in thread") + return "done" + end + + function f() + local t, err = ngx.thread.spawn(g) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + ngx.say(res) + end + + local co = coroutine.create(f) + coroutine.resume(co) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 3: ok +delete thread 3 +terminate 2: ok +terminate 1: ok +delete thread 1 + +--- stream_response +thread created: running +hello in thread +done +--- no_error_log +[error] + + + +=== TEST 11: waiting on two simple user threads without I/O +--- stream_server_config + content_by_lua_block { + -- local out = function (...) ngx.log(ngx.ERR, ...) end + local out = ngx.say + + function f() + out("f: hello") + return "f done" + end + + function g() + out("g: hello") + return "g done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + out("failed to spawn thread f: ", err) + return + end + + out("thread f created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + out("failed to spawn thread g: ", err) + return + end + + out("thread g created: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tf, tg) + if not ok then + out("failed to wait thread: ", res) + return + end + + out("res: ", res) + + out("f status: ", coroutine.status(tf)) + out("g status: ", coroutine.status(tg)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +create 3 in 1 +spawn user thread 3 in 1 +terminate 3: ok +delete thread 2 +terminate 1: ok +delete thread 3 +delete thread 1 + +--- stream_response +f: hello +thread f created: zombie +g: hello +thread g created: zombie +res: f done +f status: dead +g status: zombie + +--- no_error_log +[error] + + + +=== TEST 12: waiting on two simple user threads with I/O +--- stream_server_config + content_by_lua_block { + -- local out = function (...) ngx.log(ngx.ERR, ...) end + local out = ngx.say + + function f() + ngx.sleep(0.1) + out("f: hello") + return "f done" + end + + function g() + ngx.sleep(0.2) + out("g: hello") + return "g done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + out("failed to spawn thread f: ", err) + return + end + + out("thread f created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + out("failed to spawn thread g: ", err) + return + end + + out("thread g created: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tf, tg) + if not ok then + out("failed to wait thread: ", res) + return + end + + out("res: ", res) + + out("f status: ", coroutine.status(tf)) + out("g status: ", coroutine.status(tg)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 +terminate 3: ok +delete thread 3 + +--- stream_response +thread f created: running +thread g created: running +f: hello +res: f done +f status: dead +g status: running +g: hello + +--- no_error_log +[error] + + + +=== TEST 13: waiting on two simple user threads with I/O (uthreads completed in reversed order) +--- stream_server_config + content_by_lua_block { + -- local out = function (...) ngx.log(ngx.ERR, ...) end + local out = ngx.say + + function f() + ngx.sleep(0.2) + out("f: hello") + return "f done" + end + + function g() + ngx.sleep(0.1) + out("g: hello") + return "g done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + out("failed to spawn thread f: ", err) + return + end + + out("thread f created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + out("failed to spawn thread g: ", err) + return + end + + out("thread g created: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tf, tg) + if not ok then + out("failed to wait thread: ", res) + return + end + + out("res: ", res) + + out("f status: ", coroutine.status(tf)) + out("g status: ", coroutine.status(tg)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 3: ok +delete thread 3 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +thread f created: running +thread g created: running +g: hello +res: g done +f status: running +g status: dead +f: hello + +--- no_error_log +[error] + + + +=== TEST 14: waiting on two simple user threads without I/O, both aborted by errors +--- stream_server_config + content_by_lua_block { + -- local out = function (...) ngx.log(ngx.ERR, ...) end + local out = ngx.say + + function f() + out("f: hello") + error("f done") + end + + function g() + out("g: hello") + error("g done") + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + out("failed to spawn thread f: ", err) + return + end + + out("thread f created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + out("failed to spawn thread g: ", err) + return + end + + out("thread g created: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tf, tg) + if not ok then + out("failed to wait thread: ", res) + else + out("res: ", res) + end + + out("f status: ", coroutine.status(tf)) + out("g status: ", coroutine.status(tg)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: fail +create 3 in 1 +spawn user thread 3 in 1 +terminate 3: fail +delete thread 2 +terminate 1: ok +delete thread 3 +delete thread 1 + +--- stream_response +f: hello +thread f created: zombie +g: hello +thread g created: zombie +failed to wait thread: f done +f status: dead +g status: zombie + +--- error_log eval +qr/lua user thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):7: f done/ + + + +=== TEST 15: waiting on two simple user threads with I/O, both aborted by errors +--- stream_server_config + content_by_lua_block { + -- local out = function (...) ngx.log(ngx.ERR, ...) end + local out = ngx.say + + function f() + ngx.sleep(0.1) + out("f: hello") + error("f done") + end + + function g() + ngx.sleep(0.2) + out("g: hello") + error("g done") + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + out("failed to spawn thread f: ", err) + return + end + + out("thread f created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + out("failed to spawn thread g: ", err) + return + end + + out("thread g created: ", coroutine.status(tg)) + + local ok, res = ngx.thread.wait(tf, tg) + if not ok then + out("failed to wait thread: ", res) + else + out("res: ", res) + end + + out("f status: ", coroutine.status(tf)) + out("g status: ", coroutine.status(tg)) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 2: fail +delete thread 2 +terminate 1: ok +delete thread 1 +terminate 3: fail +delete thread 3 + +--- stream_response +thread f created: running +thread g created: running +f: hello +failed to wait thread: f done +f status: dead +g status: running +g: hello + +--- error_log eval +qr/lua user thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):8: f done/ + + + +=== TEST 16: wait on uthreads on the exact order of their termination, but exit the world early +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + ngx.say("f: hello") + return "done" + end + + function g() + ngx.sleep(0.2) + ngx.say("g: hello") + return "done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("f thread created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("g thread created: ", coroutine.status(tg)) + + ok, res = ngx.thread.wait(tf, tg) + if not ok then + ngx.say("failed to wait: ", res) + return + end + + ngx.say("res: ", res) + + ngx.exit(200) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 3 +delete thread 1 + +--- stream_response +f thread created: running +g thread created: running +f: hello +res: done + +--- no_error_log +[error] + + + +=== TEST 17: wait on uthreads on the reversed order of their termination, but exit the world early +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.2) + ngx.say("f: hello") + return "f done" + end + + function g() + ngx.sleep(0.1) + ngx.say("g: hello") + return "g done" + end + + local tf, err = ngx.thread.spawn(f) + if not tf then + ngx.say("failed to spawn thread f: ", err) + return + end + + ngx.say("f thread created: ", coroutine.status(tf)) + + local tg, err = ngx.thread.spawn(g) + if not tg then + ngx.say("failed to spawn thread g: ", err) + return + end + + ngx.say("g thread created: ", coroutine.status(tg)) + + ok, res = ngx.thread.wait(tf, tg) + if not ok then + ngx.say("failed to wait: ", res) + return + end + + ngx.say("res: ", res) + + ngx.exit(200) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 3: ok +delete thread 3 +terminate 1: ok +delete thread 2 +delete thread 1 + +--- stream_response +f thread created: running +g thread created: running +g: hello +res: g done + +--- no_error_log +[error] + + + +=== TEST 18: entry coroutine waiting on a thread not created by itself +--- stream_server_config + content_by_lua_block { + local t + + function f() + ngx.sleep(0.1) + return "done" + end + + function g() + t = ngx.thread.spawn(f) + end + + local co = coroutine.create(g) + coroutine.resume(co) + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 2: ok +terminate 1: fail +delete thread 3 +delete thread 1 + +--- stream_response +--- error_log +only the parent coroutine can wait on the thread + + + +=== TEST 19: entry coroutine waiting on a user coroutine +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + coroutine.yield() + return "done" + end + + local co = coroutine.create(f) + coroutine.resume(co) + + local ok, res = ngx.thread.wait(co) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: fail +delete thread 1 + +--- stream_response +--- error_log eval +qr/lua entry thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):11: attempt to wait on a coroutine that is not a user thread/ + + + +=== TEST 20: lua backtrace dumper may access dead parent coroutines +--- stream_server_config + content_by_lua_block { + function f() + ngx.sleep(0.1) + collectgarbage() + error("f done") + end + + ngx.thread.spawn(f) + ngx.say("ok") + + collectgarbage() + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: fail +delete thread 2 + +--- stream_response +ok + +--- error_log eval +qr/lua user thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):5: f done/ + + + +=== TEST 21: waiting on a dead coroutine +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + return + end + + ngx.say(res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +hello in thread +thread created: zombie +failed to run thread: already waited or killed +--- no_error_log +[error] + + + +=== TEST 22: spawn and wait uthreads for many times +--- stream_server_config + content_by_lua_block { + function f() + -- ngx.say("hello in thread") + return "done" + end + + for i = 1, 100 do + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + break + end + + -- ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to run thread: ", res) + break + end + + ngx.say(i, ": ", res) + end + } +--- stream_response eval +my $s = ''; +for my $i (1..100) { + $s .= "$i: done\n"; +} +$s; + +--- no_error_log +[error] +[alert] + + + +=== TEST 23: hanging bug +--- stream_server_config + content_by_lua_block { + local function foo() + ngx.say("ok") + end + + ngx.sleep(0.001) + local co = ngx.thread.spawn(foo) + ngx.thread.wait(co) + } +--- stream_response +ok +--- no_error_log +[error] +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/099-c-api.t b/src/deps/src/stream-lua-nginx-module/t/099-c-api.t new file mode 100644 index 000000000..be3627f65 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/099-c-api.t @@ -0,0 +1,373 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_process_enabled(1); +log_level('warn'); + +repeat_each(3); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +run_tests(); + +__DATA__ + +=== TEST 1: find zone +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + void *ngx_stream_lua_find_zone(char *data, size_t len); + ]] + + local buf = ffi.new("char[?]", 4) + ffi.copy(buf, "foo", 3) + local zone = ffi.C.ngx_stream_lua_find_zone(buf, 3) + ngx.say("foo zone: ", tonumber(ffi.cast("long", zone)) ~= 0 and "defined" or "undef") + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + ngx.say("dogs zone: ", tonumber(ffi.cast("long", zone)) ~= 0 and "defined" or "undef") + } +--- stream_response +foo zone: undef +dogs zone: defined +--- no_error_log +[error] + + + +=== TEST 2: number typed value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + typedef struct { + size_t len; + char *data; + } ngx_str_t; + + typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + double n; /* number */ + ngx_str_t s; /* string */ + } value; + + } ngx_stream_lua_value_t; + + void *ngx_stream_lua_find_zone(char *data, size_t len); + intptr_t ngx_stream_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_stream_lua_value_t *val); + ]] + + local dogs = ngx.shared.dogs + dogs:set("foo", 1234567) + dogs:set("bar", 3.14159) + + local buf = ffi.new("char[?]", 4) + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + + ffi.copy(buf, "foo", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("foo: rc=", tonumber(rc), + ", type=", val[0].type, + ", val=", tonumber(val[0].value.n)) + + ffi.copy(buf, "bar", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("bar: rc=", tonumber(rc), + ", type=", val[0].type, + ", val=", tonumber(val[0].value.n)) + } +--- stream_response +foo: rc=0, type=3, val=1234567 +bar: rc=0, type=3, val=3.14159 +--- no_error_log +[error] +--- SKIP + + + +=== TEST 3: boolean typed value +--- SKIP +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + typedef struct { + size_t len; + char *data; + } ngx_str_t; + + typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + double n; /* number */ + ngx_str_t s; /* string */ + } value; + + } ngx_stream_lua_value_t; + + void *ngx_stream_lua_find_zone(char *data, size_t len); + intptr_t ngx_stream_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_stream_lua_value_t *val); + ]] + + local dogs = ngx.shared.dogs + dogs:set("foo", true) + dogs:set("bar", false) + + local buf = ffi.new("char[?]", 4) + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + + ffi.copy(buf, "foo", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("foo: rc=", tonumber(rc), + ", type=", tonumber(val[0].type), + ", val=", tonumber(val[0].value.b)) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + ffi.copy(buf, "bar", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("bar: rc=", tonumber(rc), + ", type=", tonumber(val[0].type), + ", val=", tonumber(val[0].value.b)) + } +--- stream_response +foo: rc=0, type=1, val=1 +bar: rc=0, type=1, val=0 +--- no_error_log +[error] + + + +=== TEST 4: key not found +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + typedef struct { + size_t len; + char *data; + } ngx_str_t; + + typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + double n; /* number */ + ngx_str_t s; /* string */ + } value; + + } ngx_stream_lua_value_t; + + void *ngx_stream_lua_find_zone(char *data, size_t len); + intptr_t ngx_stream_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_stream_lua_value_t *val); + ]] + + local dogs = ngx.shared.dogs + dogs:flush_all() + + local buf = ffi.new("char[?]", 4) + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + + ffi.copy(buf, "foo", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("foo: rc=", tonumber(rc)) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + ffi.copy(buf, "bar", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("bar: rc=", tonumber(rc)) + } +--- stream_response +foo: rc=-5 +bar: rc=-5 +--- no_error_log +[error] +--- SKIP + + + +=== TEST 5: string typed value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + typedef struct { + size_t len; + char *data; + } ngx_str_t; + + typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + double n; /* number */ + ngx_str_t s; /* string */ + } value; + + } ngx_stream_lua_value_t; + + void *ngx_stream_lua_find_zone(char *data, size_t len); + intptr_t ngx_stream_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_stream_lua_value_t *val); + ]] + + local dogs = ngx.shared.dogs + dogs:set("foo", "hello world") + dogs:set("bar", "") + + local buf = ffi.new("char[?]", 4) + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + + local s = ffi.new("char[?]", 20) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + val[0].value.s.len = 20 + val[0].value.s.data = s + + ffi.copy(buf, "foo", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("foo: rc=", tonumber(rc), + ", type=", tonumber(val[0].type), + ", val=", ffi.string(val[0].value.s.data, val[0].value.s.len), + ", len=", tonumber(val[0].value.s.len)) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + val[0].value.s.len = 20 + val[0].value.s.data = s + + ffi.copy(buf, "bar", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("bar: rc=", tonumber(rc), + ", type=", tonumber(val[0].type), + ", val=", ffi.string(val[0].value.s.data, val[0].value.s.len), + ", len=", tonumber(val[0].value.s.len)) + } +--- stream_response +foo: rc=0, type=4, val=hello world, len=11 +bar: rc=0, type=4, val=, len=0 +--- no_error_log +[error] +--- SKIP + + + +=== TEST 6: nil typed value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + typedef struct { + size_t len; + char *data; + } ngx_str_t; + + typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + double n; /* number */ + ngx_str_t s; /* string */ + } value; + + } ngx_stream_lua_value_t; + + void *ngx_stream_lua_find_zone(char *data, size_t len); + intptr_t ngx_stream_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_stream_lua_value_t *val); + ]] + + local dogs = ngx.shared.dogs + dogs:set("foo", nil) + + local buf = ffi.new("char[?]", 4) + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + + local val = ffi.new("ngx_stream_lua_value_t[?]", 1) + + ffi.copy(buf, "foo", 3) + local rc = ffi.C.ngx_stream_lua_shared_dict_get(zone, buf, 3, val) + ngx.say("foo: rc=", tonumber(rc)) + } +--- stream_response +foo: rc=-5 +--- no_error_log +[error] +--- SKIP + + + +=== TEST 7: find zone (multiple zones) +--- stream_config + lua_shared_dict dogs 1m; + lua_shared_dict cats 1m; +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + + ffi.cdef[[ + void *ngx_stream_lua_find_zone(char *data, size_t len); + ]] + + local buf = ffi.new("char[?]", 4) + ffi.copy(buf, "cats", 4) + local zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + local cats = tostring(zone) + + ffi.copy(buf, "dogs", 4) + zone = ffi.C.ngx_stream_lua_find_zone(buf, 4) + local dogs = tostring(zone) + + ngx.say("dogs == cats ? ", dogs == cats) + -- ngx.say("dogs: ", dogs) + -- ngx.say("cats ", cats) + } +--- stream_response +dogs == cats ? false +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/100-client-abort.t b/src/deps/src/stream-lua-nginx-module/t/100-client-abort.t new file mode 100644 index 000000000..f2d26edc9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/100-client-abort.t @@ -0,0 +1,573 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = <<_EOC_; +$t::StapThread::GCScript + +F(ngx_http_lua_check_broken_connection) { + println("lua check broken conn") +} + +F(ngx_http_lua_request_cleanup) { + println("lua req cleanup") +} +_EOC_ + +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 + 13); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; +$ENV{TEST_NGINX_REDIS_PORT} ||= '6379'; + +#no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.1 +--- wait: 1.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 2: sleep + stop (log handler still gets called) +TODO +--- SKIP +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + ngx.sleep(1) + log_by_lua_block { + ngx.log(ngx.NOTICE, "here in log by lua") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- stream_response +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection +here in log by lua + + + +=== TEST 3: sleep + ignore +--- stream_server_config + lua_check_client_abort off; + content_by_lua_block { + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +lua req cleanup + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] + + + +=== TEST 4: need body on + sleep + stop (log handler still gets called) +TODO +--- SKIP +--- stream_server_config + lua_check_client_abort on; + lua_need_request_body on; + content_by_lua_block { + ngx.sleep(1) + log_by_lua_block { + ngx.log(ngx.NOTICE, "here in log by lua") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- stream_response +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection +here in log by lua + + + +=== TEST 5: ngx.req.socket + receive() + sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + sock:receive() + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- abort +--- stream_request +hello +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 6: ngx.req.socket + receive(N) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + sock:receive(5) + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 0.1 +--- timeout: 0.2 +--- abort +--- stream_request chomp +hello +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 7: ngx.req.socket + receive(n) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + sock:receive(2) + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out_like +^(?:lua check broken conn +terminate 1: ok +delete thread 1 +lua req cleanup|lua check broken conn +lua req cleanup +delete thread 1)$ + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] + + + +=== TEST 8: ngx.req.socket + m * receive(n) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + sock:receive(2) + sock:receive(2) + sock:receive(1) + ngx.sleep(0.5) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 0.6 +--- timeout: 0.2 +--- abort +--- stream_request chomp +hello +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 9: ngx.req.socket + receiveuntil + sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + local it = sock:receiveuntil("\n") + it() + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 10: ngx.req.socket + receiveuntil + it(n) + sleep + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + local it = sock:receiveuntil("\n") + it(2) + it(3) + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- timeout: 0.2 +--- wait: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 11: cosocket + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock, err = ngx.socket.tcp() + if not sock then + ngx.log(ngx.ERR, "failed to get socket: ", err) + return + end + + ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + local bytes, err = sock:send("blpop nonexist 2\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send query: ", err) + return + end + + -- ngx.log(ngx.ERR, "about to receive") + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive query: ", err) + return + end + + ngx.log(ngx.ERR, "res: ", res) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection + + + +=== TEST 12: ngx.req.socket + receive all + ignore +--- stream_server_config + lua_check_client_abort off; + content_by_lua_block { + local sock = ngx.req.socket() + local res, err, part = sock:receive("*a") + if not res then + ngx.log(ngx.NOTICE, "failed to receive: ", err, ": ", part) + return + end + print("received data: ", res) + } +--- stream_request +hello +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +received data: hello + + + +=== TEST 13: ngx.req.socket + receive all + stop +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local sock = ngx.req.socket() + local res, err, part = sock:receive("*a") + if not res then + ngx.log(ngx.NOTICE, "failed to receive: ", err, ": ", part) + return + end + error("bad") + } +--- stream_request +hello +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] + + + +=== TEST 14: ngx.req.read_body + sleep + stop (log handler still gets called) +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + ngx.req.read_body() + ngx.sleep(0.1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua req cleanup +delete thread 1 + +--- shutdown: 1 +--- stream_response +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection +--- SKIP + + + +=== TEST 15: sleep (default off) +--- stream_server_config + content_by_lua_block { + ngx.sleep(1) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +lua req cleanup + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +[alert] + + + +=== TEST 16: ngx.say +--- stream_server_config + #postpone_output 1; + content_by_lua_block { + ngx.sleep(0.2) + for i = 1, 2 do + local ok, err = ngx.say("hello") + if not ok then + ngx.log(ngx.WARN, "say failed: ", err) + return + end + end + } +--- wait: 0.2 +--- timeout: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +[alert] +--- error_log +say failed: nginx output filter error + + + +=== TEST 17: ngx.print +--- stream_server_config + #postpone_output 1; + content_by_lua_block { + ngx.sleep(0.2) + for i = 1, 2 do + local ok, err = ngx.print("hello") + if not ok then + ngx.log(ngx.WARN, "print failed: ", err) + return + end + end + } +--- wait: 0.2 +--- timeout: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +[alert] +--- error_log +print failed: nginx output filter error + + + +=== TEST 18: ngx.flush +--- stream_server_config + #postpone_output 1; + content_by_lua_block { + ngx.say("hello") + ngx.sleep(0.2) + local ok, err = ngx.flush() + if not ok then + ngx.log(ngx.WARN, "flush failed: ", err) + return + end + ngx.log(ngx.WARN, "flush succeeded") + } +--- wait: 0.2 +--- timeout: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +[alert] +--- error_log +flush succeeded + + + +=== TEST 19: ngx.eof +--- stream_server_config + #postpone_output 1; + content_by_lua_block { + ngx.sleep(0.2) + local ok, err = ngx.eof() + if not ok then + ngx.log(ngx.WARN, "eof failed: ", err) + return + end + ngx.log(ngx.WARN, "eof succeeded") + } +--- wait: 0.2 +--- timeout: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +[alert] +--- error_log +eof succeeded diff --git a/src/deps/src/stream-lua-nginx-module/t/101-on-abort.t b/src/deps/src/stream-lua-nginx-module/t/101-on-abort.t new file mode 100644 index 000000000..6c1e96e70 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/101-on-abort.t @@ -0,0 +1,461 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = <<_EOC_; +$t::StapThread::GCScript + +F(ngx_http_lua_check_broken_connection) { + println("lua check broken conn") +} + +F(ngx_http_lua_request_cleanup) { + println("lua req cleanup") +} +_EOC_ + +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 + 27); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; +$ENV{TEST_NGINX_REDIS_PORT} ||= '6379'; + +#no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: ignore the client abort event in the user callback +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.sleep(0.7) + ngx.log(ngx.NOTICE, "main handler done") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out_like chop +^create 2 in 1 +lua check broken conn +terminate 2: ok +(?:lua check broken conn +)?terminate 1: ok +delete thread 2 +delete thread 1 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- wait: 0.7 +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection +on abort called +main handler done + + + +=== TEST 2: abort in the user callback +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + ngx.exit(444) + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.sleep(0.7) + ngx.log(ngx.NOTICE, "main handler done") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +lua check broken conn +terminate 2: ok +lua req cleanup +delete thread 2 +delete thread 1 + +--- timeout: 0.2 +--- wait: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +main handler done +--- error_log +stream client prematurely closed connection +on abort called + + + +=== TEST 3: accessing cosocket in callback +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to redis: ", err) + ngx.exit(499) + end + local bytes, err = sock:send("flushall\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send query: ", err) + ngx.exit(499) + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive: ", err) + ngx.exit(499) + end + ngx.log(ngx.NOTICE, "callback done: ", res) + ngx.exit(499) + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.sleep(0.7) + ngx.log(ngx.NOTICE, "main handler done") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +lua check broken conn +terminate 2: ok +lua req cleanup +delete thread 2 +delete thread 1 + +--- timeout: 0.2 +--- abort +--- wait: 0.5 +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +main handler done +--- error_log +stream client prematurely closed connection +on abort called +callback done: +OK + + + +=== TEST 4: ignore the client abort event in the user callback (no check) +--- stream_server_config + lua_check_client_abort off; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + ngx.say("cannot set on_abort: ", err) + return + end + + ngx.sleep(0.7) + ngx.log(ngx.NOTICE, "main handler done") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +terminate 1: ok +delete thread 1 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- stream_response +cannot set on_abort: lua_check_client_abort is off +--- no_error_log +stream client prematurely closed connection +on abort called +main handler done + + + +=== TEST 5: regsiter on_abort callback but no client abortion +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.say("done") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +lua req cleanup +delete thread 2 + +--- stream_response +done +--- no_error_log +[error] +stream client prematurely closed connection +on abort called +main handler done + + + +=== TEST 6: ignore the client abort event in the user callback (uthread) +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.thread.spawn(function () + ngx.sleep(0.7) + ngx.log(ngx.NOTICE, "main handler done") + end) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 1: ok +delete thread 1 +lua check broken conn +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 +lua req cleanup + +--- timeout: 0.2 +--- abort +--- wait: 0.7 +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +--- error_log +stream client prematurely closed connection +on abort called +main handler done + + + +=== TEST 7: abort in the user callback (uthread) +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + ngx.exit(444) + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.thread.spawn(function () + ngx.sleep(0.7) + ngx.log(ngx.NOTICE, "main handler done") + end) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 1: ok +delete thread 1 +lua check broken conn +terminate 2: ok +lua req cleanup +delete thread 2 +delete thread 3 + +--- timeout: 0.2 +--- wait: 0.1 +--- abort +--- stream_response +receive stream response error: timeout +--- no_error_log +[error] +main handler done +--- error_log +stream client prematurely closed connection +on abort called + + + +=== TEST 8: regsiter on_abort callback but no client abortion (uthread) +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.thread.spawn(function () + ngx.sleep(0.1) + ngx.say("done") + end) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +terminate 1: ok +delete thread 1 +terminate 3: ok +delete thread 3 +lua req cleanup +delete thread 2 + +--- stream_response +done +--- no_error_log +[error] +stream client prematurely closed connection +on abort called +main handler done + + + +=== TEST 9: regsiter on_abort callback multiple times +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + ngx.say("1: cannot set on_abort: " .. err) + return + end + + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + ngx.say("2: cannot set on_abort: " .. err) + return + end + + ngx.thread.spawn(function () + ngx.sleep(0.1) + ngx.say("done") + end) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +lua req cleanup +delete thread 2 + +--- stream_response +2: cannot set on_abort: duplicate call + +--- no_error_log +[error] + + + +=== TEST 10: regsiter on_abort callback but no client abortion (2 uthreads and 1 pending) +--- stream_server_config + lua_check_client_abort on; + content_by_lua_block { + local ok, err = ngx.on_abort(function () + ngx.log(ngx.NOTICE, "on abort called") + end) + + if not ok then + error("cannot set on_abort: " .. err) + end + + ngx.thread.spawn(function () + ngx.sleep(0.1) + ngx.say("done") + ngx.exit(200) + end) + + ngx.thread.spawn(function () + ngx.sleep(100) + end) + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +spawn user thread 3 in 1 +create 4 in 1 +spawn user thread 4 in 1 +terminate 1: ok +delete thread 1 +terminate 3: ok +lua req cleanup +delete thread 2 +delete thread 3 +delete thread 4 + +--- wait: 0.5 +--- stream_response +done +--- no_error_log +[error] +stream client prematurely closed connection +on abort called +main handler done diff --git a/src/deps/src/stream-lua-nginx-module/t/106-timer.t b/src/deps/src/stream-lua-nginx-module/t/106-timer.t new file mode 100644 index 000000000..192817dec --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/106-timer.t @@ -0,0 +1,1956 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 8 + 45); + +#no_diff(); +no_long_string(); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; + +worker_connections(1024); +run_tests(); + +__DATA__ + +=== TEST 1: simple at +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f(premature) + print("elapsed: ", ngx.now() - begin) + print("timer prematurely expired: ", premature) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] +timer prematurely expired: true + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:4[4-9]|5[0-6])\d*, context: ngx\.timer, client: \d+\.\d+\.\d+\.\d+, server: 0\.0\.0\.0:\d+/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"timer prematurely expired: false", +] + + + +=== TEST 2: separated global env +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + foo = 3 + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.06) + ngx.say("foo = ", foo) + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer +foo = 3 + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:4[4-9]|5[0-6])/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 3: lua variable sharing via upvalue +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local foo + local function f() + foo = 3 + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.06) + ngx.say("foo = ", foo) + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer +foo = 3 + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:4[4-9]|5[0-6])/, +"stream lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 4: simple at (sleep in the timer callback) +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + ngx.sleep(0.02) + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.12 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:6[4-9]|7[0-9]|8[0-6])/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 5: tcp cosocket in timer handler (short connections) +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function fail(...) + ngx.log(ngx.ERR, ...) + end + local function f() + print("my lua timer handler") + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + fail("failed to send request: ", err) + return + end + + print("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + print("received: ", line) + + else + if err == "closed" then + break + end + fail("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + print("close: ", ok, " ", err) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"connected: 1", +"request sent: 57", +"received: HTTP/1.1 200 OK", +qr/received: Server: \S+/, +"received: Content-Type: text/plain", +"received: Content-Length: 4", +"received: Connection: close", +"received: foo", +"close: 1 nil", +] + + + +=== TEST 6: tcp cosocket in timer handler (keep-alive connections) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" + +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config + +--- user_files +>>> test.lua +module("test", package.seeall) + +local function fail(...) + ngx.log(ngx.ERR, ...) +end + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + fail("failed to send request: ", err) + return + end + print("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + print("received: ", line) + + else + fail("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + fail("failed to set reusable: ", err) + end +end + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +qr/go\(\): connected: 1, reused: \d+/, +"go(): request sent: 11", +"go(): received: OK", +] + + + +=== TEST 7: 0 timer +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0(?:[^.]|\.00)/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 8: udp cosocket in timer handler +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function fail(...) + ngx.log(ngx.ERR, ...) + end + local function f() + print("my lua timer handler") + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + local port = $TEST_NGINX_MEMCACHED_PORT + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok) + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + fail("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + fail("failed to receive data: ", err) + return + end + print("received ", #data, " bytes: ", data) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"connected: 1", +"received 12 bytes: \x{00}\x{01}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}" +] + + + +=== TEST 9: simple at (sleep in the timer callback) - log_by_lua +TODO +--- SKIP +--- stream_server_config + echo hello world; + log_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + ngx.sleep(0.02) + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.log(ngx.ERR, "failed to set timer: ", err) + return + end + print("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 + +--- stream_response +hello world + +--- wait: 0.12 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"registered timer", +qr/\[lua\] .*? my lua timer handler/, +qr/\[lua\] log_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:6[4-9]|7[0-6])/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 10: tcp cosocket in timer handler (keep-alive connections) - log_by_lua +TODO +--- SKIP +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" + +--- stream_server_config + log_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.log(ngx.ERR, "failed to set timer: ", err) + return + end + print("registered timer") + } + +--- config + +--- user_files +>>> test.lua +module("test", package.seeall) + +local function fail(...) + ngx.log(ngx.ERR, ...) +end + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + fail("failed to send request: ", err) + return + end + print("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + print("received: ", line) + + else + fail("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + fail("failed to set reusable: ", err) + end +end + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 + +--- stream_response +hello + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"registered timer", +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +qr/go\(\): connected: 1, reused: \d+/, +"go(): request sent: 11", +"go(): received: OK", +] + + + +=== TEST 11: coroutine API +--- stream_server_config + content_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + local function f() + function f() + local cnt = 0 + for i = 1, 20 do + print("cnt = ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i=1,3 do + cr(c) + print("after resume, i = ", i) + end + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"cnt = 0", +"after resume, i = 1", +"cnt = 1", +"after resume, i = 2", +"cnt = 2", +"after resume, i = 3", +] + + + +=== TEST 12: ngx.thread API +--- stream_server_config + content_by_lua_block { + local function fail (...) + ngx.log(ngx.ERR, ...) + end + local function handle() + function f() + print("hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + fail("failed to spawn thread: ", err) + return + end + + print("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + fail("failed to run thread: ", res) + return + end + + print("wait result: ", res) + end + local ok, err = ngx.timer.at(0.01, handle) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 3: ok +delete thread 3 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"hello in thread", +"thread created: zombie", +"wait result: done", +] + + + +=== TEST 13: shared dict +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local function f() + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + dogs:set("bah", 10502) + local val = dogs:get("foo") + print("get foo: ", val, " ", type(val)) + val = dogs:get("bah") + print("get bah: ", val, " ", type(val)) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"get foo: 32 number", +"get bah: 10502 number", +] + + + +=== TEST 14: ngx.exit(0) +--- stream_server_config + content_by_lua_block { + local function f() + local function g() + print("BEFORE ngx.exit") + ngx.exit(0) + end + g() + print("CANNOT REACH HERE") + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"BEFORE ngx.exit", +] +--- no_error_log +CANNOT REACH HERE +API disabled + + + +=== TEST 15: ngx.exit(403) +--- stream_server_config + content_by_lua_block { + local function f() + local function g() + print("BEFORE ngx.exit") + ngx.exit(403) + end + g() + print("CANNOT REACH HERE") + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] +CANNOT REACH HERE +API disabled + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"BEFORE ngx.exit", +] + + + +=== TEST 16: exit in user thread (entry thread is still pending on ngx.sleep) +--- stream_server_config + content_by_lua_block { + local function handle() + local function f() + print("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + print("BEFORE thread spawn") + ngx.thread.spawn(f) + print("AFTER thread spawn") + ngx.sleep(1) + print("entry thread END") + end + local ok, err = ngx.timer.at(0.05, handle) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_stream_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 1000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 1000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_stream_lua_sleep_cleanup) { + println("lua sleep cleanup") +} +_EOC_ + +--- stap_out_like chop +(?:create 2 in 1 +terminate 1: ok +delete thread 1 +free request +create 3 in 2 +spawn user thread 3 in 2 +add timer 100 +add timer 1000 +expire timer 100 +terminate 3: ok +delete thread 3 +lua sleep cleanup +delete timer 1000 +delete thread 2|create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +spawn user thread 3 in 2 +add timer 100 +add timer 1000 +free request +expire timer 100 +terminate 3: ok +delete thread 3 +lua sleep cleanup +delete timer 1000 +delete thread 2)$ + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] +API disabled +entry thread END + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"BEFORE thread spawn", +"hello in thread", +"AFTER thread spawn", +] + + + +=== TEST 17: chained timers (0 delay) +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + local ok, err = ngx.timer.at(0, g) + if not ok then + fail("failed to set timer: ", err) + return + end + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +'lua ngx.timer expired', +'stream lua close fake stream connection', +qr/trace: \[m\]\[f\]\[g\], context: ngx\.timer, client: \d+\.\d+\.\d+\.\d+, server: 0\.0\.0\.0:\d+/, +] + + + +=== TEST 18: chained timers (non-zero delay) +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + local ok, err = ngx.timer.at(0.01, g) + if not ok then + fail("failed to set timer: ", err) + return + end + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection +trace: [m][f][g] + + + +=== TEST 19: multiple parallel timers +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + fail("failed to set timer: ", err) + return + end + local ok, err = ngx.timer.at(0.01, g) + if not ok then + fail("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection +trace: [m][f][g] + + + +=== TEST 20: lua_max_pending_timers +--- stream_config + lua_max_pending_timers 1; +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0.01, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +failed to set timer g: too many pending timers + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] +[error] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection + + + +=== TEST 21: lua_max_pending_timers (just not exceeding) +--- stream_config + lua_max_pending_timers 2; +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0.01, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] +[error] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection +trace: [m][f][g] + + + +=== TEST 22: lua_max_pending_timers - chained timers (non-zero delay) - not exceeding +--- stream_config + lua_max_pending_timers 1; + +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + local ok, err = ngx.timer.at(0.01, g) + if not ok then + fail("failed to set timer: ", err) + return + end + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection +trace: [m][f][g] + + + +=== TEST 23: lua_max_pending_timers - chained timers (zero delay) - not exceeding +--- stream_config + lua_max_pending_timers 1; + +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + local ok, err = ngx.timer.at(0, g) + if not ok then + fail("failed to set timer: ", err) + return + end + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +create 3 in 2 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection +trace: [m][f][g] + + + +=== TEST 24: lua_max_running_timers (just not enough) +--- stream_config + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local f, g + + g = function () + ngx.sleep(0.01) + end + + f = function () + ngx.sleep(0.01) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[crit] +[error] + +--- error_log eval +[ +qr/\[alert\] .*? 1 lua_max_running_timers are not enough/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +] + + + +=== TEST 25: lua_max_running_timers (just enough) +--- stream_config + lua_max_running_timers 2; +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local f, g + + g = function () + ngx.sleep(0.01) + end + + f = function () + ngx.sleep(0.01) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] +[error] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection + + + +=== TEST 26: lua_max_running_timers (just enough) - 2 +--- stream_config + lua_max_running_timers 2; +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local f, g + + g = function () + ngx.timer.at(0.02, f) + ngx.sleep(0.01) + end + + f = function () + ngx.sleep(0.01) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to set timer g: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 1 +terminate 1: ok +delete thread 1 +create 4 in 3 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 +terminate 4: ok +delete thread 4 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] +[error] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection + + + +=== TEST 27: user args +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f(premature, a, b, c) + print("elapsed: ", ngx.now() - begin) + print("timer prematurely expired: ", premature) + print("timer user args: ", a, " ", b, " ", c) + end + local ok, err = ngx.timer.at(0.05, f, 1, "hello", true) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] +timer prematurely expired: true + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:4[4-9]|5[0-6])\d*, context: ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"timer prematurely expired: false", +"timer user args: 1 hello true", +] + + + +=== TEST 28: use of ngx.ctx +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f(premature) + ngx.ctx.s = "hello" + print("elapsed: ", ngx.now() - begin) + print("timer prematurely expired: ", premature) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] +timer prematurely expired: true + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: .*?, context: ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"timer prematurely expired: false", +"lua release ngx.ctx at ref ", +] + + + +=== TEST 29: syslog error log +--- stream_config + #error_log syslog:server=127.0.0.1:12345 error; +--- stream_server_config + content_by_lua_block { + local function f() + ngx.log(ngx.ERR, "Bad bad bad") + end + ngx.timer.at(0, f) + ngx.sleep(0.001) + ngx.say("ok") + } + +--- config +--- log_level: error +--- error_log_file: syslog:server=127.0.0.1:12345 +--- udp_listen: 12345 +--- udp_query eval: qr/Bad bad bad/ +--- udp_reply: hello +--- wait: 0.1 +--- stream_response +ok +--- error_log +Bad bad bad +--- skip_nginx: 4: < 1.7.1 + + + +=== TEST 30: log function location when failed to run a timer +--- stream_config + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local function g() + ngx.sleep(0.01) + end + + local function f() + ngx.sleep(0.01) + end + + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to create timer f: ", err) + return + end + + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to create timer g: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at =content_by_lua\(nginx.conf:\d+\):2: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] + + + +=== TEST 31: log function location when failed to run a timer (anonymous function) +--- stream_config + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local function f() + ngx.sleep(0.01) + end + + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + + local ok, err = ngx.timer.at(0, function() + ngx.sleep(0.01) + end) + + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at =content_by_lua\(nginx.conf:\d+\):12: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] + + + +=== TEST 32: log function location when failed to run a timer (lua file) +--- user_files +>>> test.lua +local _M = {} + +function _M.run() + ngx.sleep(0.01) +end + +return _M +--- stream_config + lua_package_path '$TEST_NGINX_HTML_DIR/?.lua;./?.lua;;'; + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local test = require "test" + + local ok, err = ngx.timer.at(0, test.run) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + local ok, err = ngx.timer.at(0, test.run) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at @.+\/test.lua:3: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] + + + +=== TEST 33: log function location when failed to run a timer with arg (lua file) +--- user_files +>>> test.lua +local _M = {} + +function _M.run() + ngx.sleep(0.01) +end + +return _M +--- stream_config + lua_package_path '$TEST_NGINX_HTML_DIR/?.lua;./?.lua;;'; + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local test = require "test" + + local ok, err = ngx.timer.at(0, test.run, "arg") + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + local ok, err = ngx.timer.at(0, test.run, "arg") + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at @.+\/test.lua:3: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] diff --git a/src/deps/src/stream-lua-nginx-module/t/107-timer-errors.t b/src/deps/src/stream-lua-nginx-module/t/107-timer-errors.t new file mode 100644 index 000000000..8650ae072 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/107-timer-errors.t @@ -0,0 +1,239 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 7); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: ngx.say() +--- stream_server_config + content_by_lua_block { + local function f() + ngx.say("hello") + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +qr/\[error\] .*? runtime error: content_by_lua\(nginx\.conf:\d+\):3: API disabled in the context of ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 2: ngx.print() +--- stream_server_config + content_by_lua_block { + local function f() + ngx.print("hello") + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +qr/\[error\] .*? runtime error: content_by_lua\(nginx\.conf:\d+\):3: API disabled in the context of ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 3: ngx.flush() +--- stream_server_config + content_by_lua_block { + local function f() + ngx.flush() + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +qr/\[error\] .*? runtime error: content_by_lua\(nginx\.conf:\d+\):3: API disabled in the context of ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 4: ngx.on_abort +--- stream_server_config + content_by_lua_block { + local function f() + ngx.on_abort(f) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +qr/\[error\] .*? runtime error: content_by_lua\(nginx\.conf:\d+\):3: API disabled in the context of ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 5: ngx.eof +--- stream_server_config + content_by_lua_block { + local function f() + ngx.eof() + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +qr/\[error\] .*? runtime error: content_by_lua\(nginx\.conf:\d+\):3: API disabled in the context of ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 6: ngx.req.socket +--- stream_server_config + content_by_lua_block { + local function f() + local sock, err = ngx.req.socket() + if not sock then + ngx.log(ngx.ERR, "failed to get req sock: ", err) + end + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +qr/\[error\] .*? runtime error: content_by_lua\(nginx\.conf:\d+\):3: API disabled in the context of ngx\.timer/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] diff --git a/src/deps/src/stream-lua-nginx-module/t/108-timer-safe.t b/src/deps/src/stream-lua-nginx-module/t/108-timer-safe.t new file mode 100644 index 000000000..7cecb3452 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/108-timer-safe.t @@ -0,0 +1,1027 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 8 + 37); + +#no_diff(); +no_long_string(); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; + +worker_connections(1024); +run_tests(); + +__DATA__ + +=== TEST 1: simple at +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.05) + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:4[4-9]|5[0-6])/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 2: simple at (sleep in the timer callback) +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + ngx.sleep(0.02) + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.05) + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.5 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:6[4-9]|7[0-6])/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 3: tcp cosocket in timer handler (short connections) +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function fail(...) + ngx.log(ngx.ERR, ...) + end + local function f() + print("my lua timer handler") + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + fail("failed to send request: ", err) + return + end + + print("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + print("received: ", line) + + else + if err == "closed" then + break + end + fail("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + print("close: ", ok, " ", err) + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.02) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stap2 eval: $::StapScript +--- stap3 eval: $::GCScript +--- stap_out2 +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 3: ok +delete thread 3 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"connected: 1", +"request sent: 57", +"received: HTTP/1.1 200 OK", +qr/received: Server: \S+/, +"received: Content-Type: text/plain", +"received: Content-Length: 4", +"received: Connection: close", +"received: foo", +"close: 1 nil", +] + + + +=== TEST 4: tcp cosocket in timer handler (keep-alive connections) +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" + +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.02) + } + +--- config + +--- user_files +>>> test.lua +module("test", package.seeall) + +local function fail(...) + ngx.log(ngx.ERR, ...) +end + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + fail("failed to send request: ", err) + return + end + print("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + print("received: ", line) + + else + fail("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + fail("failed to set reusable: ", err) + end +end + +--- stap2 eval: $::StapScript +--- stap3 eval: $::GCScript +--- stap_out2 +create 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +qr/go\(\): connected: 1, reused: \d+/, +"go(): request sent: 11", +"go(): received: OK", +] + + + +=== TEST 5: 0 timer +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function f() + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 1: ok +delete thread 1 +terminate 2: ok +delete thread 2 + +--- stream_response +registered timer + +--- wait: 0.02 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0(?:[^.]|\.00)/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 6: udp cosocket in timer handler +--- stream_server_config + content_by_lua_block { + local begin = ngx.now() + local function fail(...) + ngx.log(ngx.ERR, ...) + end + local function f() + print("my lua timer handler") + local socket = ngx.socket + -- local socket = require "socket" + + local udp = socket.udp() + + local port = $TEST_NGINX_MEMCACHED_PORT + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok) + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + fail("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + fail("failed to receive data: ", err) + return + end + print("received ", #data, " bytes: ", data) + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.05) + } + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +"connected: 1", +"received 12 bytes: \x{00}\x{01}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}" +] + + + +=== TEST 7: simple at (sleep in the timer callback) - log_by_lua +TODO +--- SKIP +--- stream_server_config + echo hello world; + echo_sleep 0.07; + log_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + ngx.sleep(0.02) + print("elapsed: ", ngx.now() - begin) + end + local ok, err = ngx.timer.at(0.05, f) + if not ok then + ngx.log(ngx.ERR, "failed to set timer: ", err) + return + end + print("registered timer") + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 + +--- stream_response +hello world + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"registered timer", +qr/\[lua\] .*? my lua timer handler/, +qr/\[lua\] log_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:6[4-9]|7[0-6])/, +"lua ngx.timer expired", +"stream lua close fake stream connection" +] + + + +=== TEST 8: tcp cosocket in timer handler (keep-alive connections) - log_by_lua +TODO +--- SKIP +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" + +--- stream_server_config + echo hello; + echo_sleep 0.01; + log_by_lua_block { + local begin = ngx.now() + local function f() + print("my lua timer handler") + + local test = require "test" + local port = $TEST_NGINX_MEMCACHED_PORT + test.go(port) + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.log(ngx.ERR, "failed to set timer: ", err) + return + end + print("registered timer") + } + +--- config + +--- user_files +>>> test.lua +module("test", package.seeall) + +local function fail(...) + ngx.log(ngx.ERR, ...) +end + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + fail("failed to connect: ", err) + return + end + + print("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + fail("failed to send request: ", err) + return + end + print("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + print("received: ", line) + + else + fail("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + fail("failed to set reusable: ", err) + end +end + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 + +--- stream_response +hello + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"registered timer", +qr/\[lua\] .*? my lua timer handler/, +"lua ngx.timer expired", +"stream lua close fake stream connection", +qr/go\(\): connected: 1, reused: \d+/, +"go(): request sent: 11", +"go(): received: OK", +] + + + +=== TEST 9: coroutine API +--- stream_server_config + content_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + local function f() + function f() + local cnt = 0 + for i = 1, 20 do + print("cnt = ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i=1,3 do + cr(c) + print("after resume, i = ", i) + end + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.01) + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"cnt = 0", +"after resume, i = 1", +"cnt = 1", +"after resume, i = 2", +"cnt = 2", +"after resume, i = 3", +] + + + +=== TEST 10: ngx.thread API +--- stream_server_config + content_by_lua_block { + local function fail (...) + ngx.log(ngx.ERR, ...) + end + local function handle() + function f() + print("hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + fail("failed to spawn thread: ", err) + return + end + + print("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + fail("failed to run thread: ", res) + return + end + + print("wait result: ", res) + end + local ok, err = ngx.timer.at(0.01, handle) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.02) + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +terminate 3: ok +delete thread 3 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"hello in thread", +"thread created: zombie", +"wait result: done", +] + + + +=== TEST 11: shared dict +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local function f() + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + dogs:set("bah", 10502) + local val = dogs:get("foo") + print("get foo: ", val, " ", type(val)) + val = dogs:get("bah") + print("get bah: ", val, " ", type(val)) + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.02) + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"get foo: 32 number", +"get bah: 10502 number", +] + + + +=== TEST 12: ngx.exit(0) +--- stream_server_config + content_by_lua_block { + local function f() + local function g() + print("BEFORE ngx.exit") + ngx.exit(0) + end + g() + print("CANNOT REACH HERE") + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.01) + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[alert] +[crit] + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"BEFORE ngx.exit", +] +--- no_error_log +CANNOT REACH HERE +API disabled + + + +=== TEST 13: ngx.exit(403) +--- stream_server_config + content_by_lua_block { + local function f() + local function g() + print("BEFORE ngx.exit") + ngx.exit(403) + end + g() + print("CANNOT REACH HERE") + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.01) + } + +--- config +--- stap2 +F(ngx_stream_lua_timer_handler) { + println("lua timer handler") +} + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] +CANNOT REACH HERE +API disabled + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"BEFORE ngx.exit", +] + + + +=== TEST 14: exit in user thread (entry thread is still pending on ngx.sleep) +--- stream_server_config + content_by_lua_block { + local function handle() + local function f() + print("hello in thread") + ngx.sleep(0.1) + ngx.exit(0) + end + + print("BEFORE thread spawn") + ngx.thread.spawn(f) + print("AFTER thread spawn") + ngx.sleep(1) + print("entry thread END") + end + local ok, err = ngx.timer.at(0.01, handle) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + ngx.sleep(0.12) + } + +--- config +--- stap eval +<<'_EOC_' . $::GCScript; + +global timers + +F(ngx_stream_free_request) { + println("free request") +} + +M(timer-add) { + if ($arg2 == 1000 || $arg2 == 100) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) + } +} + +M(timer-del) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("delete timer %d\n", tm) + delete timers[$arg1] + } + /* + if (tm == 1000) { + print_ubacktrace() + } + */ +} + +M(timer-expire) { + tm = timers[$arg1] + if (tm == 1000 || tm == 100) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] + } +} + +F(ngx_stream_lua_sleep_cleanup) { + println("lua sleep cleanup") +} +_EOC_ + +--- stap_out +create 2 in 1 +create 3 in 2 +spawn user thread 3 in 2 +add timer 100 +add timer 1000 +expire timer 100 +terminate 3: ok +delete thread 3 +lua sleep cleanup +delete timer 1000 +delete thread 2 +terminate 1: ok +delete thread 1 +free request + +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] +API disabled +entry thread END + +--- error_log eval +[ +"lua ngx.timer expired", +"stream lua close fake stream connection", +"BEFORE thread spawn", +"hello in thread", +"AFTER thread spawn", +] + + + +=== TEST 15: chained timers (non-zero delay) +--- stream_server_config + content_by_lua_block { + local s = "" + + local function fail(...) + ngx.log(ngx.ERR, ...) + end + + local function g() + s = s .. "[g]" + print("trace: ", s) + end + + local function f() + local ok, err = ngx.timer.at(0.01, g) + if not ok then + fail("failed to set timer: ", err) + return + end + s = s .. "[f]" + end + local ok, err = ngx.timer.at(0.01, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + s = "[m]" + ngx.sleep(0.03) + } + +--- config +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +create 3 in 2 +terminate 2: ok +delete thread 2 +terminate 3: ok +delete thread 3 +terminate 1: ok +delete thread 1 + +--- stream_response +registered timer + +--- wait: 0.1 +--- no_error_log +[error] +[alert] +[crit] + +--- error_log +lua ngx.timer expired +stream lua close fake stream connection +trace: [m][f][g] diff --git a/src/deps/src/stream-lua-nginx-module/t/109-timer-hup.t b/src/deps/src/stream-lua-nginx-module/t/109-timer-hup.t new file mode 100644 index 000000000..425640967 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/109-timer-hup.t @@ -0,0 +1,477 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : (); + +use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * 73; + +#no_diff(); +no_long_string(); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; + +worker_connections(1024); +run_tests(); + +__DATA__ + +=== TEST 1: single timer +--- stream_server_config + content_by_lua_block { + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local i = 0 + local function f(premature) + i = i + 1 + print("timer prematurely expired: ", premature) + print("in callback: hello, ", i) + end + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + os.execute("kill -HUP " .. pid) + } + +--- config +--- stream_response +registered timer + +--- wait: 0.3 +--- no_error_log +[error] +[alert] +[crit] +in callback: hello, 2 +timer prematurely expired: false + +--- error_log +lua abort pending timers +lua ngx.timer expired +stream lua close fake stream connection +in callback: hello, 1 +timer prematurely expired: true + + + +=== TEST 2: multiple timers +--- stream_server_config + content_by_lua_block { + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local i = 0 + local function f(premature) + i = i + 1 + print("timer prematurely expired: ", premature) + print("in callback: hello, ", i, "!") + end + for i = 1, 10 do + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + end + ngx.say("registered timers") + os.execute("kill -HUP " .. pid) + } + +--- config +--- stream_response +registered timers + +--- wait: 0.3 +--- no_error_log +[error] +[alert] +[crit] +in callback: hello, 11! +timer prematurely expired: false + +--- error_log +lua abort pending timers +lua ngx.timer expired +stream lua close fake stream connection +in callback: hello, 1! +in callback: hello, 2! +in callback: hello, 3! +in callback: hello, 4! +in callback: hello, 5! +in callback: hello, 6! +in callback: hello, 7! +in callback: hello, 8! +in callback: hello, 9! +in callback: hello, 10! +timer prematurely expired: true + + + +=== TEST 3: trying to add new timer after HUP reload +--- stream_server_config + content_by_lua_block { + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local function f(premature) + print("timer prematurely expired: ", premature) + local ok, err = ngx.timer.at(3, f) + if not ok then + print("failed to register a new timer after reload: ", err) + else + print("registered a new timer after reload") + end + end + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + os.execute("kill -HUP " .. pid) + } + +--- config +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] +in callback: hello, 2 +timer prematurely expired: false + +--- error_log +lua abort pending timers +lua ngx.timer expired +stream lua close fake stream connection +timer prematurely expired: true +failed to register a new timer after reload: process exiting, context: ngx.timer + + + +=== TEST 4: trying to add new timer after HUP reload +--- stream_server_config + content_by_lua_block { + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local function g(premature) + print("g: timer prematurely expired: ", premature) + print("g: exiting=", ngx.worker.exiting()) + end + + local function f(premature) + print("f: timer prematurely expired: ", premature) + print("f: exiting=", ngx.worker.exiting()) + local ok, err = ngx.timer.at(0, g) + if not ok then + print("f: failed to register a new timer after reload: ", err) + else + print("f: registered a new timer after reload") + end + end + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + os.execute("kill -HUP " .. pid) + } + +--- config +--- stream_response +registered timer + +--- wait: 0.2 +--- no_error_log +[error] +[alert] +[crit] +in callback: hello, 2 +failed to register a new timer after reload + +--- error_log +lua abort pending timers +lua ngx.timer expired +stream lua close fake stream connection +f: timer prematurely expired: true +f: registered a new timer after reload +f: exiting=true +g: timer prematurely expired: false +g: exiting=true + + + +=== TEST 5: HUP reload should abort pending timers +--- stream_server_config + content_by_lua_block { + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local function f(premature) + print("f: timer prematurely expired: ", premature) + print("f: exiting=", ngx.worker.exiting()) + end + + for i = 1, 100 do + local ok, err = ngx.timer.at(3 + i, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + end + ngx.say("ok") + os.execute("kill -HUP " .. pid) + } + +--- config +--- stream_response +ok + +--- wait: 0.5 +--- no_error_log +[error] +[alert] +[crit] +in callback: hello, 2 +failed to register a new timer after reload + +--- grep_error_log eval: qr/lua found \d+ pending timers/ +--- grep_error_log_out +lua found 100 pending timers + + + +=== TEST 6: HUP reload should abort pending timers (coroutine + cosocket) +TODO +--- SKIP +--- http_config + #lua_shared_dict test_dict 1m; + + server { + listen $TEST_NGINX_RAND_PORT_1; + location = /foo { + echo 'foo'; + } + } + +--- stream_server_config + content_by_lua_block { + local stream_req = {"GET /foo HTTP/1.1", "Host: localhost:1234", "", ""} + stream_req = table.concat(stream_req, "\r\n") + + -- Connect the socket + local sock = ngx.socket.tcp() + local ok,err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.log(ngx.ERR, err) + end + + -- Send the request + local ok,err = sock:send(stream_req) + + -- Get Headers + repeat + local line, err = sock:receive("*l") + until not line or string.find(line, "^%s*$") + + function foo() + repeat + -- Get and read chunk + local line, err = sock:receive("*l") + local len = tonumber(line) + if len > 0 then + local chunk, err = sock:receive(len) + coroutine.yield(chunk) + sock:receive(2) + else + -- Read last newline + sock:receive(2) + end + until len == 0 + end + + co = coroutine.create(foo) + repeat + local chunk = select(2,coroutine.resume(co)) + until chunk == nil + + -- Breaks the timer + sock:setkeepalive() + ngx.say("ok") + + log_by_lua_block { + local background_thread + background_thread = function(premature) + ngx.log(ngx.DEBUG, premature) + if premature then + ngx.shared["test_dict"]:delete("background_flag") + return + end + local ok, err = ngx.timer.at(1, background_thread) + + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + f:close() + + os.execute("kill -HUP " .. pid) + end + local dict = ngx.shared["test_dict"] + + if dict:get("background_flag") == nil then + local ok, err = ngx.timer.at(0, background_thread) + if ok then + dict:set("test_dict", 1) + end + end + } + +--- config + location = /foo {--- stream_response +ok + +--- wait: 0.3 +--- no_error_log +[error] +[alert] +[crit] +in callback: hello, 2 +failed to register a new timer after reload + +--- grep_error_log eval: qr/lua found \d+ pending timers/ +--- grep_error_log_out +lua found 1 pending timers + + + +=== TEST 7: HUP reload should abort pending timers (fuzz test) +--- stream_config + lua_max_pending_timers 8192; + +--- stream_server_config + content_by_lua_block { + local job = function(premature, kill) + if premature then + return + end + + if kill then + local f, err = assert(io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r")) + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + f:close() + + os.execute("kill -HUP " .. pid) + end + end + + math.randomseed(ngx.time()) + local rand = math.random + local newtimer = ngx.timer.at + for i = 1, 8191 do + local delay = rand(4096) + local ok, err = newtimer(delay, job, false) + if not ok then + ngx.say("failed to create timer at ", delay, ": ", err) + return + end + end + local ok, err = newtimer(0, job, true) + if not ok then + ngx.say("failed to create the killer timer: ", err) + return + end + ngx.say("ok") + } + +--- config +--- stream_response +ok + +--- wait: 0.3 +--- no_error_log +[error] +[alert] + +--- grep_error_log eval: qr/stream lua found \d+ pending timers/ +--- grep_error_log_out +stream lua found 8191 pending timers +--- timeout: 20 diff --git a/src/deps/src/stream-lua-nginx-module/t/114-config.t b/src/deps/src/stream-lua-nginx-module/t/114-config.t new file mode 100644 index 000000000..4ed3f154d --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/114-config.t @@ -0,0 +1,39 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: ngx.config.debug +--- stream_server_config + content_by_lua_block { + ngx.say("debug: ", ngx.config.debug) + } + +--- stream_response_like chop +^debug: (?:true|false)$ +--- no_error_log +[error] + + + +=== TEST 2: ngx.config.subystem +--- stream_server_config + content_by_lua_block { + ngx.say("subsystem: ", ngx.config.subsystem) + } +--- stream_response +subsystem: stream +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/115-quote-sql-str.t b/src/deps/src/stream-lua-nginx-module/t/115-quote-sql-str.t new file mode 100644 index 000000000..6f42bac37 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/115-quote-sql-str.t @@ -0,0 +1,67 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#log_level("warn"); +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: \0 +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.quote_sql_str("a\0b\0")) + } + +--- config +--- stream_response +'a\0b\0' +--- no_error_log +[error] + + + +=== TEST 2: \t +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.quote_sql_str("a\tb\t")) + } + +--- config +--- stream_response +'a\tb\t' +--- no_error_log +[error] + + + +=== TEST 3: \b +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.quote_sql_str("a\bb\b")) + } + +--- config +--- stream_response +'a\bb\b' +--- no_error_log +[error] + + + +=== TEST 4: \Z +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.quote_sql_str("a\026b\026")) + } + +--- config +--- stream_response +'a\Zb\Z' +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/116-raw-req-socket.t b/src/deps/src/stream-lua-nginx-module/t/116-raw-req-socket.t new file mode 100644 index 000000000..5f6c07af9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/116-raw-req-socket.t @@ -0,0 +1,576 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * 49; + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + + local bytes, err = sock:send("1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + } + +--- stream_request: hello +--- stream_response +1: received: hello +--- no_error_log +stream lua socket tcp_nodelay +[error] + + + +=== TEST 2: multiple raw req sockets +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + local sock2, err = ngx.req.socket(true) + if not sock2 then + ngx.log(ngx.ERR, "server: failed to get raw req socket2: ", err) + return + end + + } + +--- stap2 +F(ngx_stream_header_filter) { + println("header filter") +} +F(ngx_stream_lua_req_socket) { + println("lua req socket") +} +--- stream_response +--- error_log +server: failed to get raw req socket2: duplicate call + + + +=== TEST 3: ngx.say after ngx.req.socket(true) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + local ok, err = ngx.say("ok") + if not ok then + ngx.log(ngx.ERR, "failed to say: ", err) + return + end + } + +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 4: ngx.print after ngx.req.socket(true) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + local ok, err = ngx.print("ok") + if not ok then + ngx.log(ngx.ERR, "failed to print: ", err) + return + end + } + +--- stream_response chomp +ok +--- no_error_log +[error] + + + +=== TEST 5: ngx.eof after ngx.req.socket(true) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + local ok, err = ngx.eof() + if not ok then + ngx.log(ngx.ERR, "failed to eof: ", err) + return + end + } + +--- config + server_tokens off; + +--- stream_response +--- no_error_log +[error] + + + +=== TEST 6: ngx.flush after ngx.req.socket(true) +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + local ok, err = ngx.flush() + if not ok then + ngx.log(ngx.ERR, "failed to flush: ", err) + return + end + } + +--- stream_response +--- no_error_log +[error] + + + +=== TEST 7: receive timeout +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + sock:settimeout(100) + + local data, err, partial = sock:receive(10) + if not data then + ngx.log(ngx.ERR, "server: 1: failed to receive: ", err, ", received: ", partial) + end + + data, err, partial = sock:receive(10) + if not data then + ngx.log(ngx.ERR, "server: 2: failed to receive: ", err, ", received: ", partial) + end + + ngx.exit(444) + } + +--- stream_request chomp +ab +--- stream_response +--- wait: 0.1 +--- error_log +stream lua tcp socket read timed out +server: 1: failed to receive: timeout, received: ab +server: 2: failed to receive: timeout, received: +--- no_error_log +[alert] + + + +=== TEST 8: on_abort called during ngx.sleep() +--- stream_server_config + lua_check_client_abort on; + + content_by_lua_block { + local ok, err = ngx.on_abort(function (premature) + ngx.log(ngx.WARN, "mysock handler aborted") end) + if not ok then + ngx.log(ngx.ERR, "failed to set on_abort handler: ", err) + return + end + + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + + print("msg received: ", data) + + local bytes, err = sock:send("1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + + ngx.sleep(1) + } + +--- stream_request chomp +hello +--- stream_response +receive stream response error: timeout +--- abort +--- timeout: 0.2 +--- error_log +mysock handler aborted +msg received: hello +--- no_error_log +[error] +--- wait: 1.1 + + + +=== TEST 9: on_abort called during sock:receive() +--- stream_server_config + lua_check_client_abort on; + + content_by_lua_block { + local ok, err = ngx.on_abort(function (premature) ngx.log(ngx.WARN, "mysock handler aborted") end) + if not ok then + ngx.log(ngx.ERR, "failed to set on_abort handler: ", err) + return + end + + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + + print("msg received: ", data) + + local bytes, err = sock:send("1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + + local data, err = sock:receive() + if not data then + ngx.log(ngx.WARN, "failed to receive a line: ", err) + return + end + } + +--- stream_response +receive stream response error: timeout +--- timeout: 0.2 +--- abort +--- error_log +server: failed to receive: client aborted +--- wait: 0.1 + + + +=== TEST 10: receiveuntil +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local reader = sock:receiveuntil("rld") + local data, err = reader() + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + + local bytes, err = sock:send("1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + + local LINGERING_TIME = 30 -- 30 seconds + local LINGERING_TIMEOUT = 5000 -- 5 seconds + + local ok, err = sock:shutdown("send") + if not ok then + ngx.log(ngx.ERR, "failed to shutdown ", err) + return + end + + local deadline = ngx.time() + LINGERING_TIME + + sock:settimeouts(nil, nil, LINGERING_TIMEOUT) + + repeat + local data, _, partial = sock:receive(1024) + until (not data and not partial) or ngx.time() >= deadline + } + +--- stream_request +hello, world +--- stream_response +1: received: hello, wo +--- error_log +stream lua shutdown socket write direction +attempt to receive data on a closed socket + + + +=== TEST 11: request body not read yet +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "failed to receive: ", err) + return + end + + local ok, err = sock:send("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n" .. data) + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return + end + + local res, err = sock:shutdown('send') + if not res then + ngx.log(ngx.ERR, "server: failed to shutdown: ", err) + return + end + } + +--- stream_request +hello +--- stream_response eval +"HTTP/1.1 200 OK\r +Content-Length: 5\r +\r +hello" + +--- no_error_log +[error] + + + +=== TEST 12: read chunked request body with raw req socket +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "failed to new: ", err) + return + end + local function myerr(...) + ngx.log(ngx.ERR, ...) + return ngx.exit(400) + end + local num = tonumber + local MAX_CHUNKS = 1000 + local eof = false + local chunks = {} + for i = 1, MAX_CHUNKS do + local line, err = sock:receive() + if not line then + myerr("failed to receive chunk size: ", err) + end + + local size = num(line, 16) + if not size then + myerr("bad chunk size: ", line) + end + + if size == 0 then -- last chunk + -- receive the last line + line, err = sock:receive() + if not line then + myerr("failed to receive last chunk: ", err) + end + + if line ~= "" then + myerr("bad last chunk: ", line) + end + + eof = true + break + end + + local chunk, err = sock:receive(size) + if not chunk then + myerr("failed to receive chunk of size ", size, ": ", err) + end + + local data, err = sock:receive(2) + if not data then + myerr("failed to receive chunk terminator: ", err) + end + + if data ~= "\r\n" then + myerr("bad chunk terminator: ", data) + end + + chunks[i] = chunk + end + + if not eof then + myerr("too many chunks (more than ", MAX_CHUNKS, ")") + end + + local concat = table.concat + local body = concat{"got ", #chunks, " chunks.\nrequest body: "} + .. concat(chunks) .. "\n" + local ok, err = sock:send(body) + if not ok then + myerr("failed to send response: ", err) + end + } + +--- config +--- stream_request eval +"5\r +hey, \r +b\r +hello world\r +0\r +\r +" +--- stream_response +got 2 chunks. +request body: hey, hello world + +--- no_error_log +[error] +[alert] + + + +=== TEST 13: shutdown can only be called once and prevents all further output +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "failed to receive: ", err) + return + end + + local ok, err = sock:send("it works\n") + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return + end + + local ok, err = sock:shutdown("send") + if not ok then + ngx.log(ngx.ERR, "failed to shutdown ", err) + return + end + + ok, err = sock:shutdown("send") + if ok or err ~= "already shutdown" then + ngx.log(ngx.ERR, "shutdown called multiple times without proper error: ", err) + return + end + + ok, err = ngx.say("this should not work") + if ok or err ~= "seen eof" then + ngx.log(ngx.ERR, "ngx.say completed without proper error: ", err) + return + end + + ok, err = sock:send("this should not work") + if ok or err ~= "closed" then + ngx.log(ngx.ERR, "sock:send completed without proper error: ", err) + return + end + } + +--- stream_request +hello +--- stream_response +it works +--- error_log +stream lua shutdown socket write direction + + + +=== TEST 14: simulated lingering close +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "failed to receive: ", err) + return + end + + local ok, err = sock:shutdown("send") + if not ok then + ngx.log(ngx.ERR, "failed to shutdown ", err) + return + end + + sock:settimeouts(nil, nil, 5000) + + repeat + local data = sock:receive(1024) + until not data + } + +--- stream_request +1234567890 +--- error_log +stream lua shutdown socket write direction diff --git a/src/deps/src/stream-lua-nginx-module/t/117-raw-req-socket-timeout.t b/src/deps/src/stream-lua-nginx-module/t/117-raw-req-socket-timeout.t new file mode 100644 index 000000000..e5a0ce551 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/117-raw-req-socket-timeout.t @@ -0,0 +1,102 @@ +# 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 ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'hello, world'; + $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1; +} + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 2); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: pending response data +--- stream_server_config + #postpone_output 1; + content_by_lua_block { + ngx.say("hello") + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + } + +--- config + server_tokens off; + +--- stream_request +--- stap2 +F(ngx_stream_header_filter) { + println("header filter") +} +F(ngx_stream_lua_req_socket) { + println("lua req socket") +} +--- stream_response +hello +--- error_log +server: failed to get raw req socket: pending data to write + + + +=== TEST 2: send timeout +--- stream_server_config + #postpone_output 1; + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + sock:settimeout(100) + local ok, err = sock:send("hello, world!") + if not ok then + ngx.log(ngx.ERR, "server: failed to send: ", err) + end + ngx.exit(444) + } + +--- config + server_tokens off; + +--- stream_request +--- stream_response_like chomp +^received \d+ bytes of response data\.$ +--- log_stream_response +--- error_log +stream lua tcp socket write timed out +server: failed to send: timeout +--- no_error_log +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/119-config-prefix.t b/src/deps/src/stream-lua-nginx-module/t/119-config-prefix.t new file mode 100644 index 000000000..cd50f9452 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/119-config-prefix.t @@ -0,0 +1,28 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: content_by_lua +--- stream_server_config + content_by_lua_block { + ngx.say("prefix: ", ngx.config.prefix()) + } +--- stream_response_like chop +^prefix: \/\S+$ +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/120-re-find.t b/src/deps/src/stream-lua-nginx-module/t/120-re-find.t new file mode 100644 index 000000000..36cec76c2 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/120-re-find.t @@ -0,0 +1,798 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 1); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9]+)", "jo") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + end + ngx.say("not matched!") + end + } +--- stream_response +from: 8 +to: 11 +matched: 1234 +--- no_error_log +[error] + + + +=== TEST 2: empty matched string +--- stream_server_config + content_by_lua_block { + local s = "hello, world" + local from, to, err = ngx.re.find(s, "[0-9]*") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + end + ngx.say("not matched!") + end + } +--- stream_response +from: 1 +to: 0 +matched: +--- no_error_log +[error] + + + +=== TEST 3: multiple captures (with o) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([a-z]+).*?([0-9]{2})[0-9]+", "o") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + ngx.say("not matched!") + end + } +--- stream_response +from: 1 +to: 11 +matched: hello, 1234 +--- no_error_log +[error] + + + +=== TEST 4: not matched +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "foo") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +not matched. +--- no_error_log +[error] + + + +=== TEST 5: case sensitive by default +--- stream_server_config + content_by_lua_block { + local from = ngx.re.find("hello, 1234", "HELLO") + if from then + ngx.say(from) + else + ngx.say("not matched.") + end + } +--- stream_response +not matched. +--- no_error_log +[error] + + + +=== TEST 6: case insensitive +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "HELLO", "i") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +from: 1 +to: 5 +matched: hello +--- no_error_log +[error] + + + +=== TEST 7: UTF-8 mode +--- stream_server_config + content_by_lua_block { + local s = "hello章亦春" + local from, to, err = ngx.re.find(s, "HELLO.{2}", "iu") + if not from then + ngx.say("FAIL: ", err) + return + end + + ngx.say(string.sub(s, from, to)) + } +--- stream_response_like chop +^(?:FAIL: bad argument \#2 to '\?' \(pcre_compile\(\) failed: this version of PCRE is not compiled with PCRE_UTF8 support in "HELLO\.\{2\}" at "HELLO\.\{2\}"\)|hello章亦)$ +--- no_error_log +[error] + + + +=== TEST 8: multi-line mode (^ at line head) +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, "^world", "m") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +from: 7 +to: 11 +matched: world +--- no_error_log +[error] + + + +=== TEST 9: multi-line mode (. does not match \n) +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, ".*", "m") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +from: 1 +to: 5 +matched: hello +--- no_error_log +[error] + + + +=== TEST 10: single-line mode (^ as normal) +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, "^world", "s") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +not matched. +--- no_error_log +[error] + + + +=== TEST 11: single-line mode (dot all) +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, ".*", "s") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +from: 1 +to: 11 +matched: hello +world +--- no_error_log +[error] + + + +=== TEST 12: extended mode (ignore whitespaces) +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, "\\w \\w", "x") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stream_response +from: 1 +to: 2 +matched: he +--- no_error_log +[error] + + + +=== TEST 13: bad pattern +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, "(abc") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + if err then + ngx.say("error: ", err) + + else + ngx.say("not matched.") + end + end + } +--- stream_response +error: pcre_compile() failed: missing ) in "(abc" +--- no_error_log +[error] + + + +=== TEST 14: bad option +--- stream_server_config + content_by_lua_block { + local s = "hello\nworld" + local from, to, err = ngx.re.find(s, ".*", "H") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + if err then + ngx.say("error: ", err) + return + end + + ngx.say("not matched.") + end + } +--- stream_response +--- error_log +unknown flag "H" + + + +=== TEST 15: anchored match (failed) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9]+)", "a") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + if err then + ngx.say("error: ", err) + return + end + + ngx.say("not matched.") + end + } +--- stream_response +not matched. +--- no_error_log +[error] + + + +=== TEST 16: anchored match (succeeded) +--- stream_server_config + content_by_lua_block { + local s = "1234, hello" + local from, to, err = ngx.re.find(s, "([0-9]+)", "a") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + if err then + ngx.say("error: ", err) + return + end + + ngx.say("not matched.") + end + } +--- stream_response +from: 1 +to: 4 +matched: 1234 +--- no_error_log +[error] + + + +=== TEST 17: match with ctx but no pos +--- stream_server_config + content_by_lua_block { + local ctx = {} + local from, to = ngx.re.find("1234, hello", "([0-9]+)", "", ctx) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("pos: ", ctx.pos) + else + ngx.say("not matched!") + ngx.say("pos: ", ctx.pos) + end + } +--- stream_response +from: 1 +to: 4 +pos: 5 +--- no_error_log +[error] + + + +=== TEST 18: match with ctx and a pos +--- stream_server_config + content_by_lua_block { + local ctx = { pos = 3 } + local from, to, err = ngx.re.find("1234, hello", "([0-9]+)", "", ctx) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("pos: ", ctx.pos) + else + ngx.say("not matched!") + ngx.say("pos: ", ctx.pos) + end + } +--- stream_response +from: 3 +to: 4 +pos: 5 +--- no_error_log +[error] + + + +=== TEST 19: named subpatterns w/ extraction +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "(?[a-z]+), [0-9]+") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + if err then + ngx.say("error: ", err) + return + end + + ngx.say("not matched.") + end + } +--- stream_response +from: 1 +to: 11 +matched: hello, 1234 +--- no_error_log +[error] + + + +=== TEST 20: bad UTF-8 +--- stream_server_config + content_by_lua_block { + local target = "你好" + local regex = "你好" + + local from, to, err = ngx.re.find(string.sub(target, 1, 4), regex, "u") + + if err then + ngx.say("error: ", err) + return + end + + if m then + ngx.say("matched: ", from) + else + ngx.say("not matched") + end + } +--- stream_response_like chop +^error: pcre_exec\(\) failed: -10$ + +--- no_error_log +[error] + + + +=== TEST 21: UTF-8 mode without UTF-8 sequence checks +--- stream_server_config + content_by_lua_block { + local s = "你好" + local from, to, err = ngx.re.find(s, ".", "U") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + + else + ngx.say("not matched.") + end + } +--- stap +probe process("$LIBPCRE_PATH").function("pcre_compile") { + printf("compile opts: %x\n", $options) +} + +probe process("$LIBPCRE_PATH").function("pcre_exec") { + printf("exec opts: %x\n", $options) +} + +--- stap_out +compile opts: 800 +exec opts: 2000 + +--- stream_response +from: 1 +to: 3 +matched: 你 +--- no_error_log +[error] + + + +=== TEST 22: just hit match limit +--- stream_config + lua_regex_match_limit 5600; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local from, to, err = ngx.re.find(s, re, "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not from then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match.") + return +end + +--- stream_response +failed to match. +--- no_error_log +[error] + + + +=== TEST 23: just not hit match limit +--- stream_config + lua_regex_match_limit 5700; +--- stream_server_config + content_by_lua_file html/a.lua; + +--- user_files +>>> a.lua +local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==] + +s = string.rep([[ABCDEFG]], 10) + +local start = ngx.now() + +local from, to, err = ngx.re.find(s, re, "o") + +--[[ +ngx.update_time() +local elapsed = ngx.now() - start +ngx.say(elapsed, " sec elapsed.") +]] + +if not from then + if err then + ngx.say("error: ", err) + return + end + ngx.say("failed to match") + return +end + +--- stream_response +failed to match +--- no_error_log +[error] + + + +=== TEST 24: specify the group (1) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9])([0-9]+)", "jo", nil, 1) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + end + ngx.say("not matched!") + end + } +--- stream_response +from: 8 +to: 8 +matched: 1 +--- no_error_log +[error] + + + +=== TEST 25: specify the group (0) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9])([0-9]+)", "jo", nil, 0) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + end + ngx.say("not matched!") + end + } +--- stream_response +from: 8 +to: 11 +matched: 1234 +--- no_error_log +[error] + + + +=== TEST 26: specify the group (2) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9])([0-9]+)", "jo", nil, 2) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + end + ngx.say("not matched!") + end + } +--- stream_response +from: 9 +to: 11 +matched: 234 +--- no_error_log +[error] + + + +=== TEST 27: specify the group (3) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9])([0-9]+)", "jo", nil, 3) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + return + end + ngx.say("not matched!") + end + } +--- stream_response +error: nth out of bound +--- no_error_log +[error] + + + +=== TEST 28: specify the group (4) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9])([0-9]+)", "jo", nil, 4) + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + return + end + ngx.say("not matched!") + end + } +--- stream_response +error: nth out of bound +--- no_error_log +[error] + + + +=== TEST 29: nil submatch (2nd) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "([0-9])|(hello world)", "jo", nil, 2) + if from or to then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + return + end + ngx.say("not matched!") + end + } +--- stream_response +not matched! +--- no_error_log +[error] + + + +=== TEST 30: nil submatch (1st) +--- stream_server_config + content_by_lua_block { + local s = "hello, 1234" + local from, to, err = ngx.re.find(s, "(hello world)|([0-9])", "jo", nil, 1) + if from or to then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + return + end + ngx.say("not matched!") + end + } +--- stream_response +not matched! +--- no_error_log +[error] + + + +=== TEST 31: ignore match limit in DFA mode +--- stream_config + lua_regex_match_limit 1; +--- stream_server_config + content_by_lua_block { + local s = "This is no more" + local from, to, err = ngx.re.find(s, "<.*>", "d") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + return + end + ngx.say("not matched!") + end + } +--- stream_response +from: 9 +to: 56 +matched: +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/121-version.t b/src/deps/src/stream-lua-nginx-module/t/121-version.t new file mode 100644 index 000000000..3f7a86184 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/121-version.t @@ -0,0 +1,39 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: nginx version +--- stream_server_config + content_by_lua_block { + ngx.say("version: ", ngx.config.nginx_version) + } +--- stream_response_like chop +^version: \d+$ +--- no_error_log +[error] + + + +=== TEST 2: ngx_lua_version +--- stream_server_config + content_by_lua_block { + ngx.say("version: ", ngx.config.ngx_lua_version) + } +--- stream_response_like chop +^version: \d+$ +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/122-worker.t b/src/deps/src/stream-lua-nginx-module/t/122-worker.t new file mode 100644 index 000000000..2aad66e61 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/122-worker.t @@ -0,0 +1,127 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: content_by_lua + ngx.worker.exiting +--- stream_server_config + content_by_lua_block { + ngx.say("worker exiting: ", ngx.worker.exiting()) + } +--- stream_response +worker exiting: false +--- no_error_log +[error] + + + +=== TEST 2: content_by_lua + ngx.worker.pid +TODO +--- SKIP +--- stream_server_config + content_by_lua_block { + local pid = ngx.worker.pid() + ngx.say("worker pid: ", pid) + if pid ~= tonumber(ngx.var.pid) then + ngx.say("worker pid is wrong.") + else + ngx.say("worker pid is correct.") + end + } +--- stream_response_like +worker pid: \d+ +worker pid is correct\. +--- no_error_log +[error] + + + +=== TEST 3: content_by_lua + ngx.worker.pid +--- stream_server_config + content_by_lua_block { + local pid = ngx.worker.pid() + ngx.say("worker pid: ", pid) + } +--- stream_response_like +^worker pid: \d+ +--- no_error_log +[error] + + + +=== TEST 4: init_worker_by_lua + ngx.worker.pid +TODO +--- SKIP +--- stream_config + init_worker_by_lua_block { + my_pid = ngx.worker.pid() + } +--- stream_server_config + content_by_lua_block { + ngx.say("worker pid: ", my_pid) + if my_pid ~= tonumber(ngx.var.pid) then + ngx.say("worker pid is wrong.") + else + ngx.say("worker pid is correct.") + end + } +--- stream_response_like +worker pid: \d+ +worker pid is correct\. +--- no_error_log +[error] + + + +=== TEST 5: init_worker_by_lua + ngx.worker.pid +--- stream_config + init_worker_by_lua_block { + my_pid = ngx.worker.pid() + } +--- stream_server_config + content_by_lua_block { + ngx.say("worker pid: ", my_pid) + } +--- stream_response_like +worker pid: \d+ +--- no_error_log +[error] + + + +=== TEST 6: content_by_lua + ngx.worker.pids +--- stream_server_config + content_by_lua_block { + local pid = ngx.worker.pid() + local pids = ngx.worker.pids() + ngx.say("worker pid: ", pid) + local count = ngx.worker.count() + if count ~= #pids then + ngx.say("worker pids is wrong.") + end + for i = 1, count do + if pids[i] == pid then + ngx.say("worker pid is correct.") + return + end + end + ngx.say("worker pid is wrong.") + } +--- stream_response_like +worker pid: \d+ +worker pid is correct\. +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/123-lua-path.t b/src/deps/src/stream-lua-nginx-module/t/123-lua-path.t new file mode 100644 index 000000000..3607f464e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/123-lua-path.t @@ -0,0 +1,63 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3 + 1); + +$ENV{LUA_PATH} = "../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;/foo/bar/baz"; +$ENV{LUA_CPATH} = "/baz/bar/foo"; +#no_diff(); +#no_long_string(); +master_on(); +no_shuffle(); +check_accum_error_log(); +run_tests(); + +__DATA__ + +=== TEST 1: LUA_PATH & LUA_CPATH env (code cache on) +--- main_config +env LUA_PATH; +env LUA_CPATH; + +--- stream_server_config + content_by_lua_block { + ngx.say(package.path) + ngx.say(package.cpath) + } +--- stream_response_like +(?:\.\.\/lua-resty-core\/lib\/\?\.lua;\.\.\/lua-resty-lrucache\/lib\/\?\.lua;){1,2}\/foo\/bar\/baz +/baz/bar/foo + +--- no_error_log +[error] + + + +=== TEST 2: LUA_PATH & LUA_CPATH env (code cache off) +--- main_config +env LUA_PATH; +env LUA_CPATH; + +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + ngx.say(package.path) + ngx.say(package.cpath) + } +--- stream_response_like +(?:\.\.\/lua-resty-core\/lib\/\?\.lua;\.\.\/lua-resty-lrucache\/lib\/\?\.lua;){1,2}\/foo\/bar\/baz +/baz/bar/foo + +--- no_error_log +[error] +--- error_log eval +qr/\[alert\] .*? lua_code_cache is off/ diff --git a/src/deps/src/stream-lua-nginx-module/t/124-init-worker.t b/src/deps/src/stream-lua-nginx-module/t/124-init-worker.t new file mode 100644 index 000000000..7db582041 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/124-init-worker.t @@ -0,0 +1,775 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(1); + +plan tests => repeat_each() * (blocks() * 4 + 1); + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +sub read_file { + my $infile = shift; + open my $in, $infile + or die "cannot open $infile for reading: $!"; + my $cert = do { local $/; <$in> }; + close $in; + $cert; +} + +our $DSTRootCertificate = read_file("t/cert/root-ca.crt"); +our $ServerRoot = server_root(); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: set a global lua var +--- stream_config + init_worker_by_lua_block { + foo = ngx.md5("hello world") + } +--- stream_server_config + content_by_lua_block { + ngx.say("foo = ", foo) + } +--- stream_response +foo = 5eb63bbbe01eeed093cb22bb8f5acdc3 +--- no_error_log +[error] + + + +=== TEST 2: no ngx.say() +--- stream_config + init_worker_by_lua_block { + ngx.say("hello") + } +--- stream_server_config + content_by_lua_block { + ngx.say("foo = ", foo) + } +--- stream_response +foo = nil +--- error_log +API disabled in the context of init_worker_by_lua* + + + +=== TEST 3: timer.at +--- stream_config + init_worker_by_lua_block { + _G.my_counter = 0 + local function warn(...) + ngx.log(ngx.WARN, ...) + end + local function handler(premature) + warn("timer expired (premature: ", premature, "; counter: ", + _G.my_counter, ")") + _G.my_counter = _G.my_counter + 1 + end + local ok, err = ngx.timer.at(0, handler) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + end + warn("created timer: ", ok) + } +--- stream_server_config + content_by_lua_block { + -- ngx.sleep(0.001) + ngx.say("my_counter = ", _G.my_counter) + _G.my_counter = _G.my_counter + 1 + } +--- stream_response +my_counter = 1 +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): created timer: 1 +warn(): timer expired (premature: false; counter: 0) + +--- no_error_log +[error] + + + +=== TEST 4: timer.at + cosocket +--- stream_config + init_worker_by_lua_block { + _G.done = false + local function warn(...) + ngx.log(ngx.WARN, ...) + end + local function error(...) + ngx.log(ngx.ERR, ...) + end + local function handler(premature) + warn("timer expired (premature: ", premature, ")") + + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + error("failed to connect: ", err) + _G.done = true + return + end + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + error("failed to send request: ", err) + _G.done = true + return + end + + warn("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + warn("received: ", line) + else + error("failed to receive a line: ", err, " [", part, "]") + end + _G.done = true + end + + local ok, err = ngx.timer.at(0, handler) + if not ok then + error("failed to create timer: ", err) + end + warn("created timer: ", ok) + } +--- stream_server_config + content_by_lua_block { + local waited = 0 + local sleep = ngx.sleep + while not _G.done do + local delay = 0.001 + sleep(delay) + waited = waited + delay + if waited > 1 then + ngx.say("timed out") + return + end + end + ngx.say("ok") + } +--- stream_response +ok +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): created timer: 1 +warn(): timer expired (premature: false) +warn(): request sent: 11 +warn(): received: OK + +--- log_level: debug +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 60000 +lua tcp socket read timeout: 60000 +--- no_error_log +[error] + + + +=== TEST 5: init_worker_by_lua_file (simple global var) +--- stream_config + init_worker_by_lua_file html/foo.lua; +--- stream_server_config + content_by_lua_block { + ngx.say("foo = ", foo) + } +--- user_files +>>> foo.lua +foo = ngx.md5("hello world") +--- stream_response +foo = 5eb63bbbe01eeed093cb22bb8f5acdc3 +--- no_error_log +[error] + + + +=== TEST 6: timer.at + cosocket (by_lua_file) +--- main_config +env TEST_NGINX_MEMCACHED_PORT; +--- stream_config + init_worker_by_lua_file html/foo.lua; +--- user_files +>>> foo.lua +_G.done = false +local function warn(...) + ngx.log(ngx.WARN, ...) +end +local function error(...) + ngx.log(ngx.ERR, ...) +end +local function handler(premature) + warn("timer expired (premature: ", premature, ")") + + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", + os.getenv("TEST_NGINX_MEMCACHED_PORT")) + if not ok then + error("failed to connect: ", err) + _G.done = true + return + end + + local req = "flush_all\r\n" + + local bytes, err = sock:send(req) + if not bytes then + error("failed to send request: ", err) + _G.done = true + return + end + + warn("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + warn("received: ", line) + else + error("failed to receive a line: ", err, " [", part, "]") + end + _G.done = true +end + +local ok, err = ngx.timer.at(0, handler) +if not ok then + error("failed to create timer: ", err) +end +warn("created timer: ", ok) + +--- stream_server_config + content_by_lua_block { + local waited = 0 + local sleep = ngx.sleep + while not _G.done do + local delay = 0.001 + sleep(delay) + waited = waited + delay + if waited > 1 then + ngx.say("timed out") + return + end + end + ngx.say("ok") + } +--- stream_response +ok +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): created timer: 1 +warn(): timer expired (premature: false) +warn(): request sent: 11 +warn(): received: OK + +--- log_level: debug +--- error_log +lua tcp socket connect timeout: 60000 +lua tcp socket send timeout: 60000 +lua tcp socket read timeout: 60000 +--- no_error_log +[error] + + + +=== TEST 7: ngx.ctx +--- stream_config + init_worker_by_lua_block { + ngx.ctx.foo = "hello world" + local function warn(...) + ngx.log(ngx.WARN, ...) + end + warn("foo = ", ngx.ctx.foo) + } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): foo = hello world +--- no_error_log +[error] + + + +=== TEST 8: print +--- stream_config + init_worker_by_lua_block { + print("md5 = ", ngx.md5("hello world")) + } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] +--- error_log +md5 = 5eb63bbbe01eeed093cb22bb8f5acdc3 + + + +=== TEST 9: unescape_uri +--- stream_config + init_worker_by_lua_block { + local function warn(...) + ngx.log(ngx.WARN, ...) + end + + warn(ngx.unescape_uri("hello%20world")) + } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): hello world + + + +=== TEST 10: escape_uri +--- stream_config + init_worker_by_lua_block { + local function warn(...) + ngx.log(ngx.WARN, ...) + end + + warn(ngx.escape_uri("hello world")) + } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): hello%20world + + + +=== TEST 11: ngx.re +--- stream_config + init_worker_by_lua_block { + local function warn(...) + ngx.log(ngx.WARN, ...) + end + + warn((ngx.re.sub("hello world", "world", "XXX", "jo"))) + } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] +--- grep_error_log eval: qr/warn\(\): [^,]*/ +--- grep_error_log_out +warn(): hello XXX + + + +=== TEST 12: ngx.time +--- stream_config + init_worker_by_lua_block { + local function warn(...) + ngx.log(ngx.WARN, ...) + end + + warn("time: ", ngx.time()) + } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] +--- grep_error_log eval: qr/warn\(\): .*?(?=, context)/ +--- grep_error_log_out eval +qr/warn\(\): time: \d+/ + + + +=== TEST 13: cosocket with resolver +--- timeout: 10 +--- stream_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + init_worker_by_lua_block { + -- global + logs = "" + done = false + local function say(...) + logs = logs .. table.concat({...}) .. "\n" + end + + local function handler() + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("agentzh.org", port) + if not ok then + say("failed to connect: ", err) + done = true + return + end + + say("connected: ", ok) + + local req = "GET / HTTP/1.0\r\nHost: agentzh.org\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + say("failed to send request: ", err) + done = true + return + end + + say("request sent: ", bytes) + + local line, err = sock:receive() + if line then + say("first line received: ", line) + + else + say("failed to receive the first line: ", err) + end + + line, err = sock:receive() + if line then + say("second line received: ", line) + + else + say("failed to receive the second line: ", err) + end + + done = true + end + + local ok, err = ngx.timer.at(0, handler) + if not ok then + say("failed to create timer: ", err) + else + say("timer created") + end + } + +--- stream_server_config + content_by_lua_block { + local i = 0 + while not done and i < 3000 do + ngx.sleep(0.001) + i = i + 1 + end + ngx.print(logs) + } +--- stream_response_like +timer created +connected: 1 +request sent: 56 +first line received: HTTP\/1\.1 200 OK +second line received: (?:Date|Server): .*? +--- no_error_log +[error] +--- timeout: 10 + + + +=== TEST 14: connection refused (tcp) - log_errors on by default +--- stream_config + init_worker_by_lua_block { + logs = "" + done = false + local function say(...) + logs = logs .. table.concat{...} .. "\n" + end + + local function handler() + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", 16787) + if not ok then + say("failed to connect: ", err) + else + say("connect: ", ok, " ", err) + end + done = true + end + + local ok, err = ngx.timer.at(0, handler) + if not ok then + say("failed to create timer: ", err) + else + say("timer created") + end + } + +--- stream_server_config + content_by_lua_block { + local i = 0 + while not done and i < 1000 do + ngx.sleep(0.001) + i = i + 1 + end + ngx.print(logs) + } + +--- stream_response +timer created +failed to connect: connection refused +--- error_log eval +qr/connect\(\) failed \(\d+: Connection refused\), context: ngx\.timer$/ + + + +=== TEST 15: connection refused (tcp) - log_errors explicitly on +--- stream_config + lua_socket_log_errors on; + init_worker_by_lua_block { + logs = "" + done = false + local function say(...) + logs = logs .. table.concat{...} .. "\n" + end + + local function handler() + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", 16787) + if not ok then + say("failed to connect: ", err) + else + say("connect: ", ok, " ", err) + end + done = true + end + + local ok, err = ngx.timer.at(0, handler) + if not ok then + say("failed to create timer: ", err) + else + say("timer created") + end + } + +--- stream_server_config + content_by_lua_block { + local i = 0 + while not done and i < 1000 do + ngx.sleep(0.001) + i = i + 1 + end + ngx.print(logs) + } + +--- stream_response +timer created +failed to connect: connection refused +--- error_log eval +qr/connect\(\) failed \(\d+: Connection refused\)/ + + + +=== TEST 16: connection refused (tcp) - log_errors explicitly off +--- stream_config + lua_socket_log_errors off; + init_worker_by_lua_block { + logs = "" + done = false + local function say(...) + logs = logs .. table.concat{...} .. "\n" + end + + local function handler() + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", 16787) + if not ok then + say("failed to connect: ", err) + else + say("connect: ", ok, " ", err) + end + done = true + end + + local ok, err = ngx.timer.at(0, handler) + if not ok then + say("failed to create timer: ", err) + else + say("timer created") + end + } + +--- stream_server_config + content_by_lua_block { + local i = 0 + while not done and i < 1000 do + ngx.sleep(0.001) + i = i + 1 + end + ngx.print(logs) + } + +--- stream_response +timer created +failed to connect: connection refused +--- no_error_log eval +[ +'qr/connect\(\) failed \(\d+: Connection refused\)/', +'[error]', +] + + + +=== TEST 17: init_by_lua + proxy_temp_path which has side effects in cf->cycle->paths +--- SKIP +--- stream_config eval +qq! + proxy_temp_path $::ServerRoot/proxy_temp; + init_worker_by_lua_block { + local a = 2 + 3 + } +! +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 18: syslog error log +--- stream_config + #error_log syslog:server=127.0.0.1:12345 error; + init_worker_by_lua_block { + done = false + os.execute("sleep 0.1") + ngx.log(ngx.ERR, "Bad bad bad") + done = true + } +--- stream_server_config + content_by_lua_block { + while not done do + ngx.sleep(0.001) + end + ngx.say("ok") + } +--- log_level: error +--- error_log_file: syslog:server=127.0.0.1:12345 +--- udp_listen: 12345 +--- udp_query eval: qr/Bad bad bad/ +--- udp_reply: hello +--- wait: 0.1 +--- stream_response +ok +--- error_log +Bad bad bad +--- skip_nginx: 4: < 1.7.1 + + + +=== TEST 19: fake module calls ngx_stream_conf_get_module_srv_conf in its merge_srv_conf callback (GitHub issue #554) +This also affects merge_loc_conf +--- stream_config + init_worker_by_lua_block { return } +--- stream_server_config + content_by_lua_block { + ngx.say('ok') + } +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 20: lua_ssl_trusted_certificate +--- stream_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 2; + + init_worker_by_lua_block { + local semaphore = require "ngx.semaphore" + local sem = semaphore:new(0) + package.loaded.sem = sem + + local function test_ssl_verify() + local sock = ngx.socket.tcp() + sock:settimeout(2000) + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.WARN, "connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", true) + if not session then + ngx.log(ngx.ERR, "failed to do SSL handshake: ", err) + return + end + + ngx.log(ngx.WARN, "ssl handshake: ", type(session)) + + local ok, err = sock:close() + ngx.log(ngx.WARN, "close: ", ok, " ", err) + + sem:post(1) + end + + ngx.timer.at(0, test_ssl_verify) + } + +--- stream_server_config + content_by_lua_block { + local sem = package.loaded.sem + local ok, err = sem:wait(3) + if not ok then + ngx.say("wait test_ssl_verify failed: ", err) + end + + ngx.say('ok') + } +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +ok +--- no_error_log +[error] +--- error_log +connected: 1 +ssl handshake: userdata +close: 1 nil diff --git a/src/deps/src/stream-lua-nginx-module/t/125-configure-args.t b/src/deps/src/stream-lua-nginx-module/t/125-configure-args.t new file mode 100644 index 000000000..10f4d4116 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/125-configure-args.t @@ -0,0 +1,26 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: nginx configure +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.config.nginx_configure()) + } +--- stream_response_like chop +^\s*\-\-[^-]+ +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/126-shdict-frag.t b/src/deps/src/stream-lua-nginx-module/t/126-shdict-frag.t new file mode 100644 index 000000000..7502e34f6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/126-shdict-frag.t @@ -0,0 +1,1225 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +#repeat_each(2); + +plan tests => repeat_each() * 39; + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +run_tests(); + +__DATA__ + +=== TEST 1: merge 2 single-page free blocks (forcibly evicted, merge forward) +--- stream_config + lua_shared_dict dogs 20k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + for i = 1, 2 do + set_key("foo", string.rep("a", 4000)) + set_key("bar", string.rep("b", 4001)) + set_key("baz", string.rep("c", 8102)) + + check_key("foo") + check_key("bar") + check_key("baz") + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 4 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok +alloc pages: 1 NOT OK +free pages: 2 +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok + +--- stream_response +successfully set foo. +successfully set bar. +successfully set baz with force. +foo not found +bar not found +found baz: 8102 +successfully set foo with force. +successfully set bar. +successfully set baz with force. +foo not found +bar not found +found baz: 8102 + +--- no_error_log +[error] + + + +=== TEST 2: merge 2 single-page free slabs (forcibly evicted, merge backward) +--- stream_config + lua_shared_dict dogs 20k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + for i = 1, 2 do + set_key("foo", string.rep("a", 4000)) + set_key("bar", string.rep("b", 4001)) + check_key("foo") + set_key("baz", string.rep("c", 8102)) + + check_key("foo") + check_key("bar") + check_key("baz") + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 4 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok +alloc pages: 1 NOT OK +free pages: 2 +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok + +--- stream_response +successfully set foo. +successfully set bar. +found foo: 4000 +successfully set baz with force. +foo not found +bar not found +found baz: 8102 +successfully set foo with force. +successfully set bar. +found foo: 4000 +successfully set baz with force. +foo not found +bar not found +found baz: 8102 + +--- no_error_log +[error] + + + +=== TEST 3: merge 3 single-page free slabs (actively deleted, merge backward AND forward) +--- stream_config + lua_shared_dict dogs 25k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 2 do + set_key("foo", string.rep("a", 4000)) + set_key("bar", string.rep("b", 4001)) + set_key("baz", string.rep("c", 4002)) + + check_key("foo") + check_key("bar") + check_key("baz") + + dogs:delete("foo") + safe_set_key("blah", string.rep("a", 8100)) + dogs:delete("baz") + safe_set_key("blah", string.rep("a", 8100)) + dogs:delete("bar") + safe_set_key("blah", string.rep("a", 12010)) + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 5 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 3 ok +alloc pages: 1 NOT OK +free pages: 3 +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 3 ok + +--- stream_response +successfully set foo. +successfully set bar. +successfully set baz. +found foo: 4000 +found bar: 4001 +found baz: 4002 +failed to safe set blah: no memory +failed to safe set blah: no memory +successfully safe set blah +successfully set foo with force. +successfully set bar. +successfully set baz. +found foo: 4000 +found bar: 4001 +found baz: 4002 +failed to safe set blah: no memory +failed to safe set blah: no memory +successfully safe set blah + +--- no_error_log +[error] + + + +=== TEST 4: merge one single-page block backward, but no more +--- stream_config + lua_shared_dict dogs 25k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 1 do + set_key("foo", string.rep("a", 4000)) + set_key("bar", string.rep("b", 4001)) + set_key("baz", string.rep("c", 4002)) + + check_key("foo") + check_key("bar") + check_key("baz") + + dogs:delete("bar") + safe_set_key("blah", string.rep("a", 8100)) + dogs:delete("baz") + safe_set_key("blah", string.rep("a", 8100)) + check_key("foo") + dogs:delete("foo") + check_key("blah") + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 5 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok +free pages: 1 + +--- stream_response +successfully set foo. +successfully set bar. +successfully set baz. +found foo: 4000 +found bar: 4001 +found baz: 4002 +failed to safe set blah: no memory +successfully safe set blah +found foo: 4000 +found blah: 8100 + +--- no_error_log +[error] + + + +=== TEST 5: merge one single-page block forward, but no more +--- stream_config + lua_shared_dict dogs 25k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 1 do + set_key("foo", string.rep("a", 4000)) + set_key("bar", string.rep("b", 4001)) + set_key("baz", string.rep("c", 4002)) + + check_key("foo") + check_key("bar") + check_key("baz") + + dogs:delete("bar") + safe_set_key("blah", string.rep("a", 8100)) + dogs:delete("foo") + safe_set_key("blah", string.rep("a", 8100)) + check_key("baz") + dogs:delete("baz") + check_key("blah") + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 5 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok +free pages: 1 + +--- stream_response +successfully set foo. +successfully set bar. +successfully set baz. +found foo: 4000 +found bar: 4001 +found baz: 4002 +failed to safe set blah: no memory +successfully safe set blah +found baz: 4002 +found blah: 8100 + +--- no_error_log +[error] + + + +=== TEST 6: merge 2 multi-page blocks (forcibly evicted, merge backward) +--- stream_config + lua_shared_dict dogs 30k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 1 do + set_key("foo", string.rep("a", 8100)) + set_key("bar", string.rep("b", 8101)) + check_key("foo") + safe_set_key("baz", string.rep("c", 16300)) + dogs:delete("foo") + check_key("bar") + dogs:delete("bar") + safe_set_key("baz", string.rep("c", 16300)) + + check_key("foo") + check_key("bar") + check_key("baz") + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 6 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 2 ok +alloc pages: 2 ok +alloc pages: 4 NOT OK +free pages: 2 +free pages: 2 +alloc pages: 4 ok + +--- stream_response +successfully set foo. +successfully set bar. +found foo: 8100 +failed to safe set baz: no memory +found bar: 8101 +successfully safe set baz +foo not found +bar not found +found baz: 16300 + +--- no_error_log +[error] + + + +=== TEST 7: merge big slabs (less than max slab size) backward +--- stream_config + lua_shared_dict dogs 20k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 1 do + for j = 1, 50 do + dogs:set("foo" .. j, string.rep("a", 5)) + end + set_key("bar", string.rep("a", 4000)) + + for j = 1, 50 do + dogs:delete("foo" .. j) + end + + safe_set_key("baz", string.rep("b", 8100)) + check_key("bar") + + ngx.say("delete bar") + dogs:delete("bar") + + safe_set_key("baz", string.rep("b", 8100)) + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + //printf("slab max size: %d\n", @var("ngx_slab_max_size")) + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 4 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +free pages: 1 +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 ok + +--- stream_response +successfully set bar. +failed to safe set baz: no memory +found bar: 4000 +delete bar +successfully safe set baz + +--- no_error_log +[error] + + + +=== TEST 8: cannot merge in-used big slabs page (backward) +--- stream_config + lua_shared_dict dogs 20k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 1 do + for j = 1, 63 do + dogs:set("foo" .. j, string.rep("a", 5)) + end + set_key("bar", string.rep("a", 4000)) + + --[[ + for j = 1, 50 do + dogs:delete("foo" .. j) + end + ]] + + safe_set_key("baz", string.rep("b", 8100)) + check_key("bar") + + ngx.say("delete bar") + dogs:delete("bar") + + safe_set_key("baz", string.rep("b", 8100)) + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + //printf("slab max size: %d\n", @var("ngx_slab_max_size")) + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 4 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK + +--- stream_response +successfully set bar. +failed to safe set baz: no memory +found bar: 4000 +delete bar +failed to safe set baz: no memory + +--- no_error_log +[error] + + + +=== TEST 9: cannot merge in-used big slabs page (forward) +--- stream_config + lua_shared_dict dogs 20k; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + + local function check_key(key) + local res, err = dogs:get(key) + if res then + ngx.say("found ", key, ": ", #res) + else + if not err then + ngx.say(key, " not found") + else + ngx.say("failed to fetch key: ", err) + end + end + end + + local function set_key(key, value) + local ok, err, force = dogs:set(key, value) + if ok then + ngx.print("successfully set ", key) + if force then + ngx.say(" with force.") + else + ngx.say(".") + end + else + ngx.say("failed to set ", key, ": ", err) + end + end + + local function safe_set_key(key, value) + local ok, err = dogs:safe_set(key, value) + if ok then + ngx.say("successfully safe set ", key) + else + ngx.say("failed to safe set ", key, ": ", err) + end + end + + for i = 1, 1 do + set_key("bar", string.rep("a", 4000)) + for j = 1, 50 do + dogs:set("foo" .. j, string.rep("a", 5)) + end + + --[[ + for j = 1, 50 do + dogs:delete("foo" .. j) + end + ]] + + safe_set_key("baz", string.rep("b", 8100)) + check_key("bar") + + ngx.say("delete bar") + dogs:delete("bar") + + safe_set_key("baz", string.rep("b", 8100)) + end + + collectgarbage() + } +--- stap +global first_time = 1 +global active = 1 + +F(ngx_stream_lua_shdict_init_zone) { + active = 0 +} + +F(ngx_stream_lua_shdict_init_zone).return { + active = 1 +} + +F(ngx_slab_alloc_pages) { + if (first_time) { + //printf("slab max size: %d\n", @var("ngx_slab_max_size")) + printf("total pages: %d\n", $pool->pages->slab) + first_time = 0 + } + if (active) { + printf("alloc pages: %d", $pages) + //print_ubacktrace() + } else { + printf("init zone alloc pages: %d", $pages) + } +} + +F(ngx_slab_alloc_pages).return { + if ($return) { + printf(" ok\n") + + } else { + printf(" NOT OK\n") + } +} + +F(ngx_slab_free_pages) { + printf("free pages: %d\n", $pages) +} + +--- stap_out +total pages: 4 +init zone alloc pages: 1 ok +init zone alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 1 ok +alloc pages: 2 NOT OK +free pages: 1 +alloc pages: 2 NOT OK + +--- stream_response +successfully set bar. +failed to safe set baz: no memory +found bar: 4000 +delete bar +failed to safe set baz: no memory + +--- no_error_log +[error] + + + +=== TEST 10: fuzz testing +--- stream_config + lua_shared_dict dogs 200k; +--- stream_server_config + content_by_lua_block { + local rand = math.random + local dogs = ngx.shared.dogs + local maxsz = 9000 + local maxkeyidx = 30 + local rep = string.rep + + math.randomseed(ngx.time()) + for i = 1, 30000 do + local key = "mylittlekey" .. rand(maxkeyidx) + local ok, err = dogs:get(key) + if not ok or rand() > 0.6 then + sz = rand(maxsz) + val = rep("a", sz) + local ok, err, forcible = dogs:set(key, val) + if err then + ngx.log(ngx.ERR, "failed to set key: ", err) + -- return + end + if forcible then + -- error("forcible") + end + end + end + ngx.say("ok") + collectgarbage() + } +--- stream_response +ok + +--- no_error_log +[error] +--- timeout: 60 diff --git a/src/deps/src/stream-lua-nginx-module/t/127-uthread-kill.t b/src/deps/src/stream-lua-nginx-module/t/127-uthread-kill.t new file mode 100644 index 000000000..fdcead111 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/127-uthread-kill.t @@ -0,0 +1,315 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream;use t::StapThread; + +our $GCScript = $t::StapThread::GCScript; +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 5 + 1); + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; + +#no_shuffle(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: kill pending sleep +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello from f()") + ngx.sleep(1) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.say("failed to kill thread: ", err) + return + end + + ngx.say("killed") + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.say("failed to kill thread: ", err) + return + end + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +hello from f() +thread created: running +killed +failed to kill thread: already waited or killed + +--- no_error_log +[error] +--- error_log +lua clean up the timer for pending ngx.sleep + + + +=== TEST 2: already waited +--- stream_server_config + content_by_lua_block { + function f() + ngx.say("hello from f()") + ngx.sleep(0.001) + return 32 + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.say("failed to kill thread: ", res) + return + end + + ngx.say("waited: ", res) + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.say("failed to kill thread: ", err) + return + end + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +terminate 2: ok +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +hello from f() +thread created: running +waited: 32 +failed to kill thread: already waited or killed + +--- no_error_log +[error] +lua clean up the timer for pending ngx.sleep + + + +=== TEST 3: kill pending resolver +--- stream_server_config + resolver 127.0.0.2:12345; + resolver_timeout 5ms; + content_by_lua_block { + function f() + local sock = ngx.socket.tcp() + sock:connect("some.agentzh.org", 80) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.say("failed to kill thread: ", err) + return + end + + ngx.say("killed") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +thread created: running +killed + +--- no_error_log +[error] +--- error_log +lua tcp socket abort resolver + + + +=== TEST 4: kill pending connect +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local ready = false + function f() + local sock = ngx.socket.tcp() + sock:connect("agentzh.org", 80) + sock:close() + ready = true + sock:settimeout(10000) + sock:connect("127.0.0.2", 12345) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + while not ready do + ngx.sleep(0.001) + end + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.say("failed to kill thread: ", err) + return + end + + ngx.say("killed") + } +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +create 2 in 1 +spawn user thread 2 in 1 +delete thread 2 +terminate 1: ok +delete thread 1 + +--- stream_response +thread created: running +killed + +--- no_error_log +[error] +lua tcp socket abort resolver +--- grep_error_log: stream lua finalize socket +--- grep_error_log_out +stream lua finalize socket +stream lua finalize socket + +--- error_log + + + +=== TEST 5: kill a thread already terminated +--- stream_server_config + content_by_lua_block { + function f() + return + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + + collectgarbage() + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.say("failed to kill thread: ", err) + return + end + + ngx.say("killed") + } +--- stap2 eval: $::StapScript +--- stream_response +thread created: zombie +failed to kill thread: already terminated + +--- no_error_log +[error] +[alert] +lua tcp socket abort resolver +--- error_log + + + +=== TEST 6: kill self +--- stream_server_config + content_by_lua_block { + local ok, err = ngx.thread.kill(coroutine.running()) + if not ok then + ngx.say("failed to kill main thread: ", err) + else + ngx.say("killed main thread.") + end + + function f() + local ok, err = ngx.thread.kill(coroutine.running()) + if not ok then + ngx.say("failed to kill user thread: ", err) + else + ngx.say("user thread thread.") + end + + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.say("failed to spawn thread: ", err) + return + end + + ngx.say("thread created: ", coroutine.status(t)) + } +--- stap2 eval: $::StapScript +--- stream_response +failed to kill main thread: not user thread +failed to kill user thread: killer not parent +thread created: zombie + +--- no_error_log +[error] +[alert] +stream lua tcp socket abort resolver +--- error_log diff --git a/src/deps/src/stream-lua-nginx-module/t/128-duplex-tcp-socket.t b/src/deps/src/stream-lua-nginx-module/t/128-duplex-tcp-socket.t new file mode 100644 index 000000000..d9ee08f66 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/128-duplex-tcp-socket.t @@ -0,0 +1,600 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 2); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: pipelined memcached requests (sent one byte at a time) +--- stream_server_config + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\nget foo\r\nget bar\r\n" + -- req = "OK" + local send_idx = 1 + + local function writer() + local sub = string.sub + while send_idx <= #req do + local bytes, err = sock:send(sub(req, send_idx, send_idx)) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + -- if send_idx % 2 == 0 then + ngx.sleep(0.001) + -- end + send_idx = send_idx + 1 + end + -- ngx.say("request sent.") + end + + local ok, err = ngx.thread.spawn(writer) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + for i = 1, 3 do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:setkeepalive() + ngx.say("setkeepalive: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +received: OK +received: END +received: END +setkeepalive: 1 nil + +--- no_error_log +[error] + + + +=== TEST 2: read timeout errors won't affect writing +--- stream_server_config + lua_socket_log_errors off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_MEMCACHED_PORT + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + -- req = "OK" + local send_idx = 1 + + sock:settimeout(1) + + local function writer() + local sub = string.sub + while send_idx <= #req do + local bytes, err = sock:send(sub(req, send_idx, send_idx)) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.sleep(0.001) + send_idx = send_idx + 1 + end + -- ngx.say("request sent.") + end + + local ok, err = ngx.thread.spawn(writer) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + local data = "" + local ntm = 0 + local done = false + for i = 1, 300 do + local line, err, part = sock:receive() + if not line then + if part then + data = data .. part + end + if err ~= "timeout" then + ngx.say("failed to receive: ", err) + return + end + + ntm = ntm + 1 + + else + data = data .. line + ngx.say("received: ", data) + done = true + break + end + end + + if not done then + ngx.say("partial read: ", data) + end + + ngx.say("read timed out: ", ntm) + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response_like chop +^connected: 1 +(?:received: OK|failed to send request: timeout +partial read: ) +read timed out: [1-9]\d* +close: 1 nil$ + +--- no_error_log +[error] + + + +=== TEST 3: writes are rejected while reads are not +--- stream_server_config + lua_socket_log_errors off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_RAND_PORT_1 + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + -- req = "OK" + local send_idx = 1 + + local function writer() + local sub = string.sub + while send_idx <= #req do + local bytes, err = sock:send(sub(req, send_idx, send_idx)) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.sleep(0.001) + send_idx = send_idx + 1 + end + -- ngx.say("request sent.") + end + + local ok, err = ngx.thread.spawn(writer) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + local data = "" + local ntm = 0 + local done = false + for i = 1, 3 do + local res, err, part = sock:receive(1) + if not res then + ngx.say("failed to receive: ", err) + return + else + data = data .. res + end + ngx.sleep(0.001) + end + + ngx.say("received: ", data) + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response_like chop +^connected: 1 +received: OK! +close: (?:nil socket busy writing|1 nil +failed to send request: closed)$ + +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 +--- tcp_shutdown: 0 +--- tcp_reply: OK! +--- tcp_no_close: 1 +--- no_error_log +[error] + + + +=== TEST 4: reads are rejected while writes are not +--- stream_server_config + lua_socket_log_errors off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_RAND_PORT_2 + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "flush_all\r\n" + -- req = "OK" + local send_idx = 1 + + local function writer() + local sub = string.sub + while send_idx <= #req do + local bytes, err = sock:send(sub(req, send_idx, send_idx)) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + -- ngx.say("sent: ", bytes) + ngx.sleep(0.001) + send_idx = send_idx + 1 + end + ngx.say("request sent.") + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end + + local ok, err = ngx.thread.spawn(writer) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + local data = "" + local ntm = 0 + local done = false + for i = 1, 3 do + local res, err, part = sock:receive(1) + if not res then + ngx.say("failed to receive: ", err) + return + else + data = data .. res + end + ngx.sleep(0.001) + end + + ngx.say("received: ", data) + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +failed to receive: closed +request sent. +close: 1 nil + +--- stap2 +F(ngx_http_lua_socket_tcp_finalize_write_part) { + print_ubacktrace() +} +--- stap_out2 +--- tcp_listen: $TEST_NGINX_RAND_PORT_2 +--- tcp_shutdown: 1 +--- tcp_query eval: "flush_all\r\n" +--- tcp_query_len: 11 +--- no_error_log +[error] + + + +=== TEST 5: concurrent socket operations while connecting +--- stream_server_config + lua_socket_log_errors off; + resolver $TEST_NGINX_RESOLVER ipv6=off; + content_by_lua_block { + local sock = ngx.socket.tcp() + + local function f() + ngx.sleep(0.001) + local res, err = sock:receive(1) + ngx.say("receive: ", res, " ", err) + + local bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + + local ok, err = sock:getreusedtimes() + ngx.say("getreusedtimes: ", ok, " ", err) + + local ok, err = sock:setkeepalive() + ngx.say("setkeepalive: ", ok, " ", err) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + end + + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + sock:settimeout(300) + local ok, err = sock:connect("127.0.0.2", 12345) + ngx.say("connect: ", ok, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response +receive: nil socket busy connecting +send: nil socket busy connecting +close: nil socket busy connecting +getreusedtimes: 0 nil +setkeepalive: nil socket busy connecting +connect: nil socket busy connecting +connect: nil timeout +close: nil closed + +--- no_error_log +[error] + + + +=== TEST 6: concurrent operations while resolving +--- stream_server_config + lua_socket_log_errors off; + resolver 127.0.0.2:12345; + resolver_timeout 300ms; + content_by_lua_block { + local sock = ngx.socket.tcp() + + local function f() + ngx.sleep(0.001) + local res, err = sock:receive(1) + ngx.say("receive: ", res, " ", err) + + local bytes, err = sock:send("hello") + ngx.say("send: ", bytes, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + + local ok, err = sock:getreusedtimes() + ngx.say("getreusedtimes: ", ok, " ", err) + + local ok, err = sock:setkeepalive() + ngx.say("setkeepalive: ", ok, " ", err) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + end + + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + sock:settimeout(300) + local ok, err = sock:connect("some2.agentzh.org", 80) + ngx.say("connect: ", ok, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response +receive: nil closed +send: nil closed +close: nil closed +getreusedtimes: nil closed +setkeepalive: nil closed +connect: nil socket busy connecting +connect: nil some2.agentzh.org could not be resolved (110: Operation timed out) +close: nil closed + +--- no_error_log +[error] + + + +=== TEST 7: concurrent operations while reading (receive) +--- stream_server_config + lua_socket_log_errors off; + content_by_lua_block { + local sock = ngx.socket.tcp() + local ready = false + + local function f() + while not ready do + ngx.sleep(0.001) + end + + local res, err = sock:receive(1) + ngx.say("receive: ", res, " ", err) + + local bytes, err = sock:send("flush_all") + ngx.say("send: ", bytes, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + + local ok, err = sock:getreusedtimes() + ngx.say("getreusedtimes: ", ok, " ", err) + + local ok, err = sock:setkeepalive() + ngx.say("setkeepalive: ", ok, " ", err) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + end + + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + sock:settimeout(300) + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + + ready = true + + local res, err = sock:receive(1) + ngx.say("receive: ", res, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response +connect: 1 nil +receive: nil socket busy reading +send: 9 nil +close: nil socket busy reading +getreusedtimes: 0 nil +setkeepalive: nil socket busy reading +connect: nil socket busy reading +receive: nil timeout +close: 1 nil + +--- no_error_log +[error] + + + +=== TEST 8: concurrent operations while reading (receiveuntil) +--- stream_server_config + lua_socket_log_errors off; + content_by_lua_block { + local ready = false + local sock = ngx.socket.tcp() + + local function f() + while not ready do + ngx.sleep(0.001) + end + + local res, err = sock:receive(1) + ngx.say("receive: ", res, " ", err) + + local bytes, err = sock:send("flush_all") + ngx.say("send: ", bytes, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + + local ok, err = sock:getreusedtimes() + ngx.say("getreusedtimes: ", ok, " ", err) + + local ok, err = sock:setkeepalive() + ngx.say("setkeepalive: ", ok, " ", err) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + end + + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to spawn writer thread: ", err) + return + end + + sock:settimeout(300) + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + ngx.say("connect: ", ok, " ", err) + + ready = true + + local it, err = sock:receiveuntil("\r\n") + if not it then + ngx.say("receiveuntil() failed: ", err) + return + end + + local res, err = it() + ngx.say("receiveuntil() iterator: ", res, " ", err) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + +--- stream_response +connect: 1 nil +receive: nil socket busy reading +send: 9 nil +close: nil socket busy reading +getreusedtimes: 0 nil +setkeepalive: nil socket busy reading +connect: nil socket busy reading +receiveuntil() iterator: nil timeout +close: 1 nil + +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/129-ssl-socket.t b/src/deps/src/stream-lua-nginx-module/t/129-ssl-socket.t new file mode 100644 index 000000000..1a158b175 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/129-ssl-socket.t @@ -0,0 +1,2752 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +use Cwd qw(abs_path realpath); +use File::Basename; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 7 + 2); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_SERVER_SSL_PORT} ||= 12345; +$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__))); + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +sub read_file { + my $infile = shift; + open my $in, $infile + or die "cannot open $infile for reading: $!"; + my $cert = do { local $/; <$in> }; + close $in; + $cert; +} + +our $DSTRootCertificate = read_file("t/cert/root-ca.crt"); +our $GoogleRootCertificate = read_file("t/cert/google.crt"); +our $TestCertificate = read_file("t/cert/test.crt"); +our $TestCertificateKey = read_file("t/cert/test.key"); +our $TestCRL = read_file("t/cert/test.crl"); + +run_tests(); + +__DATA__ + +=== TEST 1: www.google.com +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua ' + -- avoid flushing google in "check leak" testing mode: + local counter = package.loaded.counter + if not counter then + counter = 1 + elseif counter >= 2 then + return ngx.exit(503) + else + counter = counter + 1 + end + package.loaded.counter = counter + + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + local ok, err = sock:connect("www.google.com", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake() + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET / HTTP/1.1\\r\\nHost: www.google.com\\r\\nConnection: close\\r\\n\\r\\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + '; +--- config + server_tokens off; +--- stream_response_like chop +\Aconnected: 1 +ssl handshake: userdata +sent http request: 59 bytes. +received: HTTP/1.1 (?:200 OK|302 Found) +close: 1 nil +\z +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 2: no SNI, no verify +--- stream_config + server { + listen $TEST_NGINX_SERVER_SSL_PORT ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "ping" then + ngx.say("pong") + end + } + } + +--- stream_server_config + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_SSL_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake() + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "ping" + local bytes, err = sock:send(req .. '\n') + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("sent: ", req) + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent: ping +received: pong +close: 1 nil + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 3: SNI, no verify +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 4: ssl session reuse +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + + local session + for i = 1, 2 do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + session, err = sock:sslhandshake(session, "openresty.org") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end + + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil +connected: 1 +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl set session: \1 +lua ssl save session: \1 +lua ssl free session: \1 +lua ssl free session: \1 +$/ + +--- error_log +SSL reused session +lua ssl free session + +--- log_level: debug +--- no_error_log +[error] +[alert] +--- timeout: 5 + + + +=== TEST 5: certificate does not match host name (verify) +The certificate of "openresty.org" does not contain the name "blah.openresty.org". +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 5; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "blah.openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + else + ngx.say("ssl handshake: ", type(session)) + end + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response_like chomp +\Aconnected: 1 +failed to do SSL handshake: (?:handshake failed|certificate host mismatch) +failed to send stream request: closed +\z + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +stream lua ssl server name: "blah.openresty.org" +--- no_error_log +SSL reused session +[alert] +--- timeout: 5 + + + +=== TEST 6: certificate does not match host name (verify, no log socket errors) +The certificate for "openresty.org" does not contain the name "blah.openresty.org". +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_socket_log_errors off; + lua_ssl_verify_depth 2; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "blah.openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + else + ngx.say("ssl handshake: ", type(session)) + end + + local req = "GET / HTTP/1.1\r\nHost: blah.openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response_like chomp +\Aconnected: 1 +failed to do SSL handshake: (?:handshake failed|certificate host mismatch) +failed to send stream request: closed +\z + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +lua ssl server name: "blah.openresty.org" +--- no_error_log +lua ssl certificate does not match host +SSL reused session +[alert] +--- timeout: 5 + + + +=== TEST 7: certificate does not match host name (no verify) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", false) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET /en/linux-packages.html HTTP/1.1\r\nHost: openresty.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +ssl handshake: userdata +sent http request: 80 bytes. +received: HTTP/1.1 404 Not Found +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ + +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 8: openresty.org: passing SSL verify +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 2; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ + +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 9: ssl verify depth not enough (with automatic error logging) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 0; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + else + ngx.say("ssl handshake: ", type(session)) + end + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response eval +qr{connected: 1 +failed to do SSL handshake: (22: certificate chain too long|20: unable to get local issuer certificate|21: unable to verify the first certificate) +failed to send stream request: closed +} + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log eval +['lua ssl server name: "openresty.org"', +qr/lua ssl certificate verify error: \((22: certificate chain too long|20: unable to get local issuer certificate|21: unable to verify the first certificate)\)/] +--- no_error_log +SSL reused session +[alert] +--- timeout: 5 + + + +=== TEST 10: ssl verify depth not enough (without automatic error logging) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 0; + lua_socket_log_errors off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(3000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + else + ngx.say("ssl handshake: ", type(session)) + end + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response eval +qr/connected: 1 +failed to do SSL handshake: (22: certificate chain too long|20: unable to get local issuer certificate|21: unable to verify the first certificate) +failed to send stream request: closed +/ + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +lua ssl certificate verify error +SSL reused session +[alert] +--- timeout: 7 + + + +=== TEST 11: www.google.com (SSL verify passes) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 3; + + content_by_lua ' + -- avoid flushing google in "check leak" testing mode: + local counter = package.loaded.counter + if not counter then + counter = 1 + elseif counter >= 2 then + return ngx.exit(503) + else + counter = counter + 1 + end + package.loaded.counter = counter + + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + local ok, err = sock:connect("www.google.com", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "www.google.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET / HTTP/1.1\\r\\nHost: www.google.com\\r\\nConnection: close\\r\\n\\r\\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + '; + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::GoogleRootCertificate" + +--- stream_response_like chop +\Aconnected: 1 +ssl handshake: userdata +sent http request: 59 bytes. +received: HTTP/1.1 (?:200 OK|302 Found) +close: 1 nil +\z +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log +lua ssl server name: "www.google.com" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 12: www.google.com (SSL verify enabled and no corresponding trusted certificates) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + -- avoid flushing google in "check leak" testing mode: + local counter = package.loaded.counter + if not counter then + counter = 1 + elseif counter >= 2 then + return ngx.exit(503) + else + counter = counter + 1 + end + package.loaded.counter = counter + + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + local ok, err = sock:connect("www.google.com", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "www.google.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET / HTTP/1.1\\r\\nHost: www.google.com\\r\\nConnection: close\\r\\n\\r\\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +connected: 1 +failed to do SSL handshake: 20: unable to get local issuer certificate + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +lua ssl server name: "www.google.com" +lua ssl certificate verify error: (20: unable to get local issuer certificate) +--- no_error_log +SSL reused session +[alert] +--- timeout: 5 + + + +=== TEST 13: openresty.org: passing SSL verify with multiple certificates +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 2; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ + +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 14: default cipher +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +[ +'lua ssl server name: "openresty.org"', +qr/SSL: TLSv1\.2, cipher: "(?:ECDHE-RSA-AES(?:256|128)-GCM-SHA(?:384|256)|ECDHE-(?:RSA|ECDSA)-CHACHA20-POLY1305) TLSv1\.2/, +] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 15: explicit cipher configuration +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- stream_server_config + lua_ssl_ciphers ECDHE-RSA-AES256-SHA; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +['lua ssl server name: "test.com"', +qr/SSL: TLSv\d(?:\.\d)?, cipher: "ECDHE-RSA-AES256-SHA (SSLv3|TLSv1)/] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 + + + +=== TEST 16: explicit ssl protocol configuration +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- stream_server_config + lua_ssl_protocols TLSv1; + + content_by_lua ' + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + '; +--- config + server_tokens off; +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +[ +'lua ssl server name: "test.com"', +qr/SSL: TLSv1, cipher: "ECDHE-RSA-AES256-SHA (SSLv3|TLSv1)/ +] +--- no_error_log +SSL reused session +[error] +[alert] + + + +=== TEST 17: unsupported ssl protocol +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_protocols SSLv2; + lua_socket_log_errors off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org") + if not session then + ngx.say("failed to do SSL handshake: ", err) + else + ngx.say("ssl handshake: ", type(session)) + end + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed +failed to send stream request: closed + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log eval +[ +qr/\[(crit|error)\] .*?SSL_do_handshake\(\) failed .*?(unsupported protocol|no protocols available)/, +'lua ssl server name: "openresty.org"', +] +--- no_error_log +SSL reused session +[alert] +[emerg] +--- timeout: 5 + + + +=== TEST 18: openresty.org: passing SSL verify: keepalive (reuse the ssl session) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 2; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + + local session + for i = 1, 3 do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + session, err = sock:sslhandshake(session, "openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local ok, err = sock:setkeepalive() + ngx.say("set keepalive: ", ok, " ", err) + end -- do + + end + collectgarbage() + } + +--- config + server_tokens off; + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +connected: 1 +ssl handshake: userdata +set keepalive: 1 nil +connected: 1 +ssl handshake: userdata +set keepalive: 1 nil +connected: 1 +ssl handshake: userdata +set keepalive: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: \1 +$/ + +--- error_log +lua tcp socket get keepalive peer: using connection +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 19: openresty.org: passing SSL verify: keepalive (no reusing the ssl session) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 2; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + + local sessions = {} + + for i = 1, 3 do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "openresty.org", true) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + sessions[i] = session + + ngx.say("ssl handshake: ", type(session)) + + local ok, err = sock:setkeepalive() + ngx.say("set keepalive: ", ok, " ", err) + ngx.sleep(0.001) + end -- do + + end + collectgarbage() + } + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +connected: 1 +ssl handshake: userdata +set keepalive: 1 nil +connected: 1 +ssl handshake: userdata +set keepalive: 1 nil +connected: 1 +ssl handshake: userdata +set keepalive: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/stream lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^stream lua ssl save session: ([0-9A-F]+) +stream lua ssl save session: \1 +stream lua ssl save session: \1 +stream lua ssl free session: \1 +stream lua ssl free session: \1 +stream lua ssl free session: \1 +$/ + +--- error_log +lua tcp socket get keepalive peer: using connection +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 20: downstream cosockets do not support ssl handshake +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/trusted.crt; + lua_ssl_verify_depth 2; + + content_by_lua_block { + local sock = ngx.req.socket() + local sess, err = sock:sslhandshake() + if not sess then + ngx.say("failed to do ssl handshake: ", err) + else + ngx.say("ssl handshake: ", type(sess)) + end + } + +--- user_files eval +">>> trusted.crt +$::DSTRootCertificate" + +--- stream_response +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +attempt to call method 'sslhandshake' (a nil value) +--- no_error_log +[alert] +--- timeout: 3 + + + +=== TEST 21: unix domain ssl cosocket (no verify) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(3000) + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake() + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "thunder!\n"; + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 9 bytes. +received: flash! +received: the end... +close: 1 nil + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 22: unix domain ssl cosocket (verify) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/test.crt; + + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(3000) + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "thunder!\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 9 bytes. +received: flash! +received: the end... +close: 1 nil + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log +lua ssl server name: "test.com" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 23: unix domain ssl cosocket (no ssl on server) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake() + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "thunder!\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- user_files eval +">>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log eval +qr/SSL_do_handshake\(\) failed .*?(unknown protocol|wrong version number)/ +--- no_error_log +lua ssl server name: +SSL reused session +[alert] +--- timeout: 3 + + + +=== TEST 24: lua_ssl_crl +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_crl ../html/test.crl; + lua_ssl_trusted_certificate ../html/test.crt; + lua_socket_log_errors off; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(3000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + else + ngx.say("ssl handshake: ", type(sess)) + end + + local req = "thunder!\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response eval +# Since nginx version 1.19.1, invalidity date is considerd a non-critical CRL +# entry extension, in other words, revoke still works even if CRL has expired. +$Test::Nginx::Util::NginxVersion >= 1.019001 ? + +"connected: 1 +failed to do SSL handshake: 23: certificate revoked +failed to send stream request: closed\n" : + +"connected: 1 +failed to do SSL handshake: 12: CRL has expired +failed to send stream request: closed\n"; + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate +>>> test.crl +$::TestCRL" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +lua ssl server name: "test.com" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 25: multiple handshake calls +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + for i = 1, 2 do + local session, err = sock:sslhandshake(nil, "openresty.org") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + end + + local req = "GET / HTTP/1.1\r\nHost: openresty.org\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +ssl handshake: userdata +ssl handshake: userdata +sent stream request: 58 bytes. +received: HTTP/1.1 302 Moved Temporarily +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 26: handshake timed out +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + do + local ok, err = sock:connect("openresty.org", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + sock:settimeout(1); -- should timeout immediately + local session, err = sock:sslhandshake(nil, "openresty.org") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +failed to do SSL handshake: timeout + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +lua ssl server name: "openresty.org" +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 27: unix domain ssl cosocket (no gen session) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(3000) + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", sess) + + sock:close() + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +ssl handshake: true + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 28: unix domain ssl cosocket (gen session, true) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(3000) + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + sock:close() + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 29: unix domain ssl cosocket (keepalive) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(3000) + for i = 1, 2 do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", sess) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set keepalive: ", err) + return + end + end -- do + collectgarbage() + } + +--- config + server_tokens off; + +--- stream_response +connected: 1 +ssl handshake: true +connected: 1 +ssl handshake: true + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 30: unix domain ssl cosocket (verify cert but no host name check, passed) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + ngx.say("the end...") + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_trusted_certificate ../html/test.crt; + + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(3000) + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, nil, true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "thunder!\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 9 bytes. +received: flash! +received: the end... +close: 1 nil + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 31: unix domain ssl cosocket (verify cert but no host name check, NOT passed) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../html/test.crt; + ssl_certificate_key ../html/test.key; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data = sock:receive() + if data == "thunder!" then + ngx.say("flash!") + else + ngx.say("boom!") + end + } + } +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + #lua_ssl_trusted_certificate ../html/test.crt; + + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(3000) + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, nil, true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "thunder" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +failed to do SSL handshake: 18: self signed certificate + +--- user_files eval +">>> test.key +$::TestCertificateKey +>>> test.crt +$::TestCertificate" + +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log +lua ssl certificate verify error: (18: self signed certificate) +--- no_error_log +SSL reused session +[alert] +--- timeout: 5 + + + +=== TEST 32: default cipher - TLSv1.3 +--- skip_openssl: 8: < 1.1.1 +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- stream_server_config + lua_ssl_protocols TLSv1.3; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +[ +'lua ssl server name: "test.com"', +qr/SSL: TLSv1.3, cipher: "TLS_AES_256_GCM_SHA384 TLSv1.3/, +] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 + + + +=== TEST 33: explicit cipher configuration - TLSv1.3 +--- skip_openssl: 8: < 1.1.1 +--- skip_nginx: 8: < 1.19.4 +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- stream_server_config + lua_ssl_protocols TLSv1.3; + lua_ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +sent stream request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +['lua ssl server name: "test.com"', +qr/SSL: TLSv1.3, cipher: "TLS_AES_128_GCM_SHA256 TLSv1.3/] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 + + + +=== TEST 34: explicit cipher configuration not in the default list - TLSv1.3 +--- skip_openssl: 8: < 1.1.1 +--- skip_nginx: 8: < 1.19.4 +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- stream_server_config + lua_ssl_protocols TLSv1.3; + lua_ssl_conf_command Ciphersuites TLS_AES_128_CCM_SHA256; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send stream request: ", err) + return + end + + ngx.say("sent stream request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to recieve response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log eval +[ +qr/\[info\] .*?SSL_do_handshake\(\) failed .*?no shared cipher/, +'lua ssl server name: "test.com"', +] +--- no_error_log +SSL reused session +[alert] +[emerg] +--- timeout: 10 diff --git a/src/deps/src/stream-lua-nginx-module/t/130-internal-api.t b/src/deps/src/stream-lua-nginx-module/t/130-internal-api.t new file mode 100644 index 000000000..7f202fcd5 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/130-internal-api.t @@ -0,0 +1,34 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * 3; + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +run_tests(); + +__DATA__ + +=== TEST 1: req +--- stream_server_config + content_by_lua_block { + local ffi = require "ffi" + local function tonum(ud) + return tonumber(ffi.cast("uintptr_t", ud)) + end + ngx.say(string.format("content req=%#x", tonum(exdata()))) + } +--- stream_response_like chop +^content req=0x[a-f0-9]{4,} +$ +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/131-duplex-req-socket.t b/src/deps/src/stream-lua-nginx-module/t/131-duplex-req-socket.t new file mode 100644 index 000000000..ff09f3fb9 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/131-duplex-req-socket.t @@ -0,0 +1,138 @@ +# 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 ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'slow'; +} + +use Test::Nginx::Socket::Lua::Stream; +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4); + +run_tests(); + +__DATA__ + +=== TEST 1: raw downstream cosocket used in two different threads. See issue #481 +--- stream_server_config + lua_socket_read_timeout 1ms; + lua_socket_send_timeout 1s; + lua_socket_log_errors off; + #lua_lingering_timeout 1ms; + + content_by_lua_block { + local function reader(req_socket) + -- First we receive in a blocking fashion so that ctx->downstream_co_ctx will be changed + local data, err, partial = req_socket:receive(1) + if err ~= "timeout" then + ngx.log(ngx.ERR, "Did not get timeout in the receiving thread!") + return + end + + -- Now, sleep so that coctx->data is changed to sleep handler + ngx.sleep(1) + end + + local function writer(req_socket) + -- send in a slow manner with a low timeout, so that the timeout handler will be + local bytes, err = req_socket:send("slow!!!") + if err ~= "timeout" then + return error("Did not get timeout in the sending thread!") + end + end + + local req_socket, err = ngx.req.socket(true) + if req_socket == nil then + return error("Unable to get request socket:" .. (err or "nil")) + end + + local writer_thread = ngx.thread.spawn(writer, req_socket) + local reader_thread = ngx.thread.spawn(reader, req_socket) + + ngx.thread.wait(writer_thread) + ngx.thread.wait(reader_thread) + print("The two threads finished") + } + +--- no_error_log +[error] +--- error_log: The two threads finished +--- wait: 0.1 +--- log_stream_response +--- stream_response_like chomp +^received \d+ bytes of response data\.$ +--- timeout: 10 + + + +=== TEST 2: normal downstream cosocket used in two different threads. See issue #481 +--- stream_server_config + lua_socket_read_timeout 1ms; + lua_socket_send_timeout 300ms; + lua_socket_log_errors off; + + content_by_lua_block { + local function reader(req_socket) + -- First we receive in a blocking fashion so that ctx->downstream_co_ctx will be changed + local data, err, partial = req_socket:receive(1) + if err ~= "timeout" then + ngx.log(ngx.ERR, "Did not get timeout in the receiving thread!") + return + end + + -- Now, sleep so that coctx->data is changed to sleep handler + ngx.sleep(0.3) + end + + local function writer(req_socket) + -- send in a slow manner with a low timeout, so that the timeout handler will be + print("sleep 0.3") + ngx.sleep(0.1) + print("say slow") + ngx.say("slow!!!") + print("flushing") + local ok, err = ngx.flush(true) + if not ok then + print("flushing failed: ", err) + end + end + + local req_socket, err = ngx.req.socket() + if req_socket == nil then + return error("Unable to get request socket:" .. (err or "nil")) + end + + local writer_thread = ngx.thread.spawn(writer, req_socket) + local reader_thread = ngx.thread.spawn(reader, req_socket) + + ngx.thread.wait(writer_thread) + ngx.thread.wait(reader_thread) + print("The two threads finished") + } + +--- no_error_log +[error] +--- error_log: The two threads finished +--- wait: 0.1 +--- log_stream_response +--- stream_response +received 4 bytes of response data. +--- timeout: 3 diff --git a/src/deps/src/stream-lua-nginx-module/t/132-lua-blocks.t b/src/deps/src/stream-lua-nginx-module/t/132-lua-blocks.t new file mode 100644 index 000000000..eeba068a7 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/132-lua-blocks.t @@ -0,0 +1,399 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * ((blocks() * 3) + 1); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: content_by_lua_block (nested curly braces) +--- stream_server_config + content_by_lua_block { + local a = { + dogs = {32, 78, 96}, + cat = "kitty", + } + ngx.say("a.dogs[1] = ", a.dogs[1]) + ngx.say("a.dogs[2] = ", a.dogs[2]) + ngx.say("a.dogs[3] = ", a.dogs[3]) + ngx.say("a.cat = ", a.cat) + } + +--- config +--- stream_response +a.dogs[1] = 32 +a.dogs[2] = 78 +a.dogs[3] = 96 +a.cat = kitty + +--- no_error_log +[error] + + + +=== TEST 2: content_by_lua_block (curly braces in strings) +--- stream_server_config + content_by_lua_block { + ngx.say("}1, 2)") + ngx.say('{1, 2)') + } + +--- config +--- stream_response +}1, 2) +{1, 2) + +--- no_error_log +[error] + + + +=== TEST 3: content_by_lua_block (curly braces in strings, with escaped terminators) +--- stream_server_config + content_by_lua_block { + ngx.say("\"}1, 2)") + ngx.say('\'{1, 2)') + } + +--- config +--- stream_response +"}1, 2) +'{1, 2) + +--- no_error_log +[error] + + + +=== TEST 4: content_by_lua_block (curly braces in long brackets) +--- stream_server_config + content_by_lua_block { + --[[ + {{{ + + } + ]] + --[==[ + }}} + + { + ]==] + ngx.say("ok") + } + +--- config +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 5: content_by_lua_block ("nested" long brackets) +--- stream_server_config + content_by_lua_block { + --[[ + ]=] + ' " + } + ]] + ngx.say("ok") + } + +--- config +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 6: content_by_lua_block (curly braces in line comments) +--- stream_server_config + content_by_lua_block { + --}} {} + ngx.say("ok") + } + +--- config +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 7: content_by_lua_block (cosockets) +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + local ok, err = sock:connect('127.0.0.1', port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say('connected: ', ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + +--- config + server_tokens off; + location = /foo { + content_by_lua_block { ngx.say("foo") } + more_clear_headers Date; + } + +--- stream_response +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 8: all in one +--- stream_config + init_by_lua_block { + glob = "init by lua }here{" + } + + init_worker_by_lua_block { + glob = glob .. ", init worker }here{" + } +--- stream_server_config +#access_by_lua_block { + #local s = ngx.var.a + #s = s .. '}access{\n' + #ngx.var.a = s + #} + content_by_lua_block { + s = [[}content{]] + ngx.ctx.a = s + ngx.say(s) + ngx.say("glob: ", glob) + } + log_by_lua_block { + print("log by lua running \"}{!\"") + } + +--- config +--- stream_response +}content{ +glob: init by lua }here{, init worker }here{ + +--- error_log +log by lua running "}{!" +--- no_error_log +[error] + + + +=== TEST 9: missing ]] (string) +--- stream_server_config + content_by_lua_block { + ngx.say([[hello, world") + } + +--- config +--- stream_response +hello, world +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]" in .*?\bnginx\.conf:22/ + + + +=== TEST 10: missing ]==] (string) +--- stream_server_config + content_by_lua_block { + ngx.say([==[hello, world") + } + +--- config +--- stream_response +hello, world +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]==]" in .*?\bnginx\.conf:22/ + + + +=== TEST 11: missing ]] (comment) +--- stream_server_config + content_by_lua_block { + ngx.say(--[[hello, world") + } + +--- config +--- stream_response +hello, world +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]" in .*?\bnginx\.conf:22/ + + + +=== TEST 12: missing ]=] (comment) +--- stream_server_config + content_by_lua_block { + ngx.say(--[=[hello, world") + } + +--- config +--- stream_response +hello, world +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]=]" in .*?\bnginx\.conf:22/ + + + +=== TEST 13: missing } +FIXME: we need better diagnostics by actually loading the inlined Lua code while parsing +the *_by_lua_block directive. + +--- stream_server_config + content_by_lua_block { + ngx.say("hello") +--- stream_response +hello, world +--- no_error_log +[error] +--- error_log eval +qr/\[emerg\] .*? "http" directive is not allowed here/ +--- must_die + + + +=== TEST 14: content_by_lua_block (compact) +--- stream_server_config + content_by_lua_block {ngx.say("hello, world", {"!"})} + +--- config +--- stream_response +hello, world! +--- no_error_log +[error] + + + +=== TEST 15: content_by_lua_block unexpected closing long brackets ignored (GitHub #748) +--- config + location = /t { + content_by_lua_block { + local t1, t2 = {"hello world"}, {1} + ngx.say(t1[t2[1]]) + } + } +--- request +GET /t +--- response_body +hello world +--- no_error_log +[error] + + + +=== TEST 16: ambiguous line comments inside a long bracket string (GitHub #596) +--- stream_server_config + content_by_lua_block { + ngx.say([[ok--]]) + ngx.say([==[ok--]==]) + ngx.say([==[ok-- ]==]) + --[[ --]] ngx.say("done") + } + +--- config +--- stream_response +ok-- +ok-- +ok-- +done +--- no_error_log +[error] + + + +=== TEST 17: double quotes in long brackets +TODO +--- SKIP +--- stream_server_config + access_by_lua_block { print([[Hey, it is "!]]) } content_by_lua_block { ngx.say([["]]) } + +--- config +--- stream_response +" +--- error_log +Hey, it is "! +--- no_error_log +[error] + + + +=== TEST 18: single quotes in long brackets +TODO +--- SKIP +--- stream_server_config + access_by_lua_block { print([[Hey, it is '!]]) } content_by_lua_block { ngx.say([[']]) } + +--- config +--- stream_response +' +--- error_log +Hey, it is '! +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/133-worker-count.t b/src/deps/src/stream-lua-nginx-module/t/133-worker-count.t new file mode 100644 index 000000000..77a03c71b --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/133-worker-count.t @@ -0,0 +1,27 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: content_by_lua +--- stream_server_config + content_by_lua_block { + ngx.say("workers: ", ngx.worker.count()) + } +--- stream_response +workers: 1 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/134-worker-count-5.t b/src/deps/src/stream-lua-nginx-module/t/134-worker-count-5.t new file mode 100644 index 000000000..5180e2142 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/134-worker-count-5.t @@ -0,0 +1,27 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +workers(5); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + ngx.say("worker count: ", ngx.worker.count()) + } +--- stream_response +worker count: 5 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/135-worker-id.t b/src/deps/src/stream-lua-nginx-module/t/135-worker-id.t new file mode 100644 index 000000000..5bc80a511 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/135-worker-id.t @@ -0,0 +1,28 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + ngx.say("worker id: ", ngx.worker.id()) + } +--- stream_response_like chop +^worker id: [0-1]$ +--- no_error_log +[error] +--- skip_nginx: 3: <=1.9.0 diff --git a/src/deps/src/stream-lua-nginx-module/t/136-timer-counts.t b/src/deps/src/stream-lua-nginx-module/t/136-timer-counts.t new file mode 100644 index 000000000..22d6cbd3f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/136-timer-counts.t @@ -0,0 +1,86 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(1); + +plan tests => blocks() * (repeat_each() * 3); + +run_tests(); + +__DATA__ + +=== TEST 1: running count with no running timers +--- stream_server_config + content_by_lua_block { ngx.say(ngx.timer.running_count()) } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 2: running count with no pending timers +--- stream_server_config + content_by_lua_block { ngx.say(ngx.timer.pending_count()) } +--- stream_response +0 +--- no_error_log +[error] + + + +=== TEST 3: pending count with one pending timer +--- stream_server_config + content_by_lua_block { + ngx.timer.at(3, function() end) + ngx.say(ngx.timer.pending_count()) + } +--- stream_response +1 +--- no_error_log +[error] + + + +=== TEST 4: pending count with 3 pending timers +--- stream_server_config + content_by_lua_block { + ngx.timer.at(4, function() end) + ngx.timer.at(2, function() end) + ngx.timer.at(1, function() end) + ngx.say(ngx.timer.pending_count()) + } +--- stream_response +3 +--- no_error_log +[error] + + + +=== TEST 5: one running timer +--- stream_server_config + content_by_lua_block { + ngx.timer.at(0.1, function() ngx.sleep(0.3) end) + ngx.sleep(0.2) + ngx.say(ngx.timer.running_count()) + } +--- stream_response +1 +--- no_error_log +[error] + + + +=== TEST 6: 3 running timers +--- stream_server_config + content_by_lua_block { + ngx.timer.at(0.1, function() ngx.sleep(0.3) end) + ngx.timer.at(0.11, function() ngx.sleep(0.3) end) + ngx.timer.at(0.09, function() ngx.sleep(0.3) end) + ngx.sleep(0.2) + ngx.say(ngx.timer.running_count()) + } +--- stream_response +3 +--- no_error_log +[error] diff --git a/src/deps/src/stream-lua-nginx-module/t/138-balancer.t b/src/deps/src/stream-lua-nginx-module/t/138-balancer.t new file mode 100644 index 000000000..c863c2196 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/138-balancer.t @@ -0,0 +1,272 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 10); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: simple logging +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + print("hello from balancer by lua!") + } + } +--- stream_server_config + proxy_pass backend; +--- error_log eval +[ +'[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream,', +qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:1234 failed .*?, upstream: "0\.0\.0\.1:1234"}, +] +--- no_error_log +[warn] + + + +=== TEST 2: exit 403 +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + print("hello from balancer by lua!") + ngx.exit(403) + } + } +--- stream_server_config + proxy_pass backend; +--- error_log +[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream, +lua exit with code 403 +proxy connect: 403 +finalize stream proxy: 403 +finalize stream session: 403 +--- no_error_log eval +[ +'[warn]', +qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:1234 failed .*?, upstream: "0\.0\.0\.1:1234"}, +] + + + +=== TEST 3: exit OK +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + print("hello from balancer by lua!") + ngx.exit(ngx.OK) + } + } +--- stream_server_config + proxy_pass backend; +--- error_log eval +[ +'[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream,', +qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:1234 failed .*?, upstream: "0\.0\.0\.1:1234"}, +] +--- no_error_log +[warn] + + + +=== TEST 4: ngx.var works +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + print("1: variable remote_addr = ", ngx.var.remote_addr) + } + } +--- stream_server_config + proxy_pass backend; +--- error_log eval +[ +"1: variable remote_addr = 127.0.0.1", +qr/\[crit\] .* connect\(\) .*? failed/, +] +--- no_error_log +[warn] + + + +=== TEST 5: simple logging (by_lua_file) +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_file html/a.lua; + } +--- stream_server_config + proxy_pass backend; +--- user_files +>>> a.lua +print("hello from balancer by lua!") +--- error_log eval +[ +'[lua] a.lua:1: hello from balancer by lua! while connecting to upstream,', +qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:1234 failed .*?, upstream: "0\.0\.0\.1:1234"}, +] +--- no_error_log +[warn] + + + +=== TEST 6: cosockets are disabled +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + local sock, err = ngx.socket.tcp() + } + } +--- stream_server_config + proxy_pass backend; +--- error_log eval +qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua:2: API disabled in the context of balancer_by_lua\*/ + + + +=== TEST 7: ngx.sleep is disabled +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + ngx.sleep(0.1) + } + } +--- stream_server_config + proxy_pass backend; +--- error_log eval +qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua:2: API disabled in the context of balancer_by_lua\*/ + + + +=== TEST 8: get_phase +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + print("I am in phase ", ngx.get_phase()) + } + } +--- stream_server_config + proxy_pass backend; +--- grep_error_log eval: qr/I am in phase \w+/ +--- grep_error_log_out +I am in phase balancer +--- error_log eval +qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:1234 failed .*?, upstream: "0\.0\.0\.1:1234"} +--- no_error_log +[error] + + + +=== TEST 9: ngx.log(ngx.ERR, ...) github #816 +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + ngx.log(ngx.ERR, "hello from balancer by lua!") + } + } +--- stream_server_config + proxy_pass backend; +--- error_log eval +[ +'[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream,', +qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:1234 failed .*?, upstream: "0\.0\.0\.1:1234"}, +] +--- no_error_log +[warn] + + + +=== TEST 10: test if execeed proxy_next_upstream_limit +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + proxy_next_upstream_tries 5; + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + local b = require "ngx.balancer" + + if not ngx.ctx.tries then + ngx.ctx.tries = 0 + end + + if ngx.ctx.tries >= 6 then + ngx.log(ngx.ERR, "retry count exceed limit") + ngx.exit(500) + end + + ngx.ctx.tries = ngx.ctx.tries + 1 + print("retry counter: ", ngx.ctx.tries) + + local ok, err = b.set_more_tries(2) + if not ok then + return error("failed to set more tries: ", err) + elseif err then + ngx.log(ngx.WARN, "set more tries: ", err) + end + + assert(b.set_current_peer("127.0.0.1", 81)) + } + } +--- stream_server_config + proxy_pass backend; +--- grep_error_log eval: qr/\bretry counter: \w+/ +--- grep_error_log_out +retry counter: 1 +retry counter: 2 +retry counter: 3 +retry counter: 4 +retry counter: 5 + +--- error_log +set more tries: reduced tries due to limit + + + +=== TEST 11: set_more_tries bugfix +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + proxy_next_upstream_tries 0; + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + local balancer = require "ngx.balancer" + local ctx = ngx.ctx + if not ctx.has_run then + ctx.has_run = true + local _, err = balancer.set_more_tries(3) + if err then + ngx.log(ngx.ERR, "failed to set more tries: ", err) + end + end + balancer.set_current_peer("127.0.0.1", 81) + } + } +--- stream_server_config + proxy_pass backend; +--- grep_error_log: stream proxy next upstream +--- grep_error_log_out +stream proxy next upstream +stream proxy next upstream +stream proxy next upstream +stream proxy next upstream +--- no_error_log +failed to set more tries: reduced tries due to limit +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/139-ssl-cert-by.t b/src/deps/src/stream-lua-nginx-module/t/139-ssl-cert-by.t new file mode 100644 index 000000000..7007329c6 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/139-ssl-cert-by.t @@ -0,0 +1,1789 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { + plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1"); +} else { + plan tests => repeat_each() * (blocks() * 6 + 5); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: simple logging +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/ssl_certificate_by_lua:.*?,|\bssl cert: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log_out eval +qr/reusable connection: 1 +reusable connection: 0 +ssl cert: connection reusable: 0 +reusable connection: 0 +ssl_certificate_by_lua:1: ssl cert by lua is running!, +reusable connection: 0 +reusable connection: 0 +reusable connection: 0 +reusable connection: 0 +reusable connection: 0 +/ + + + +=== TEST 2: sleep +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl cert by lua: ", ngx.now() - begin) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log eval +[ +'lua ssl server name: "test.com"', +qr/elapsed in ssl cert by lua: 0.(?:09|1\d)\d+,/, +] + +--- no_error_log +[error] +[alert] + + + +=== TEST 3: timer +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + local function f() + print("my timer run!") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +my timer run! + +--- no_error_log +[error] +[alert] + + + +=== TEST 4: cosocket +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send flush_all command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("received memc reply: ", res) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +received memc reply: OK + +--- no_error_log +[error] +[alert] + + + +=== TEST 5: ngx.exit(0) - no yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_certificate_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 6: ngx.exit(ngx.ERROR) - no yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_certificate_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua_certificate_by_lua: handler return value: -1, cert cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 7: ngx.exit(0) - yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_certificate_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(ngx.ERROR) - yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_certificate_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua_certificate_by_lua: cert cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 9: lua exception - no yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_certificate_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_certificate_by_lua:2: bad bad bad', +'lua_certificate_by_lua: handler return value: 500, cert cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +qr/context: ssl_certificate_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 10: lua exception - yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_certificate_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_certificate_by_lua:3: bad bad bad', +'lua_certificate_by_lua: cert cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 11: get phase +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block {print("get_phase: ", ngx.get_phase())} + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata + +--- error_log +lua ssl server name: "test.com" +get_phase: ssl_cert + +--- no_error_log +[error] +[alert] + + + +=== TEST 12: connection aborted prematurely +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + ngx.sleep(0.3) + -- local ssl = require "ngx.ssl" + -- ssl.clear_certs() + print("ssl-cert-by-lua: after sleeping") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(150) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +failed to do SSL handshake: timeout + +--- error_log +lua ssl server name: "test.com" +ssl-cert-by-lua: after sleeping + +--- no_error_log +[error] +[alert] +--- wait: 0.6 + + + +=== TEST 13: simple logging (by_lua_file) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_file html/a.lua; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } + +--- user_files +>>> a.lua +print("ssl cert by lua is running!") + +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +a.lua:1: ssl cert by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 14: coroutine API +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function f() + local cnt = 0 + for i = 1, 20 do + print("co yield: ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i = 1, 3 do + print("co resume, status: ", coroutine.status(c)) + cr(c) + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/ +--- grep_error_log_out +co resume, status: suspended +co yield: 0 +co resume, status: suspended +co yield: 1 +co resume, status: suspended +co yield: 2 + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 15: simple user thread wait with yielding +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + function f() + ngx.sleep(0.01) + print("uthread: hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "uthread: failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + print("uthread: thread created: ", coroutine.status(t)) + + local ok, res = ngx.thread.wait(t) + if not ok then + print("uthread: failed to wait thread: ", res) + return + end + + print("uthread: ", res) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: thread created: running +uthread: hello in thread +uthread: done + + + +=== TEST 16: simple logging - use ssl_certificate_by_lua* on the server {} level +GitHub openresty/lua-resty-core#42 +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_certificate_by_lua:1: ssl cert by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 17: simple logging - use ssl_certificate_by_lua* on the stream {} level +--- stream_config + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_certificate_by_lua:1: ssl cert by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 18: simple logging - use ssl_certificate_by_lua* on the stream {} level and server {} level +--- stream_config + ssl_certificate_by_lua_block { print("ssl cert by lua on stream level is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { print("ssl cert by lua on server level is running!") } + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_certificate_by_lua:1: ssl cert by lua on server level is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 19: use ssl_certificate_by_lua* on the server {} level with non-ssl server +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +received: it works! +close: 1 nil + +--- no_error_log +ssl_certificate_by_lua:1: ssl cert by lua is running! +[error] +[alert] + + + +=== TEST 20: use ssl_certificate_by_lua* on the stream {} level with non-ssl server +--- stream_config + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +received: it works! +close: 1 nil + +--- no_error_log +ssl_certificate_by_lua:1: ssl cert by lua is running! +[error] +[alert] + + + +=== TEST 21: listen two ports (one for ssl and one for non-ssl) in one server - connect ssl port +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_2; + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_certificate_by_lua:1: ssl cert by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 22: listen two ports (one for ssl and one for non-ssl) in one server - connect non-ssl port +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_2; + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_2) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +received: it works! +close: 1 nil + +--- no_error_log +ssl_certificate_by_lua:1: ssl cert by lua is running! +[error] +[alert] + + + +=== TEST 23: simple logging (syslog) +github issue #723 +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + error_log syslog:server=127.0.0.1:12345 debug; + + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log eval +[ +qr/\[error\] .*? send\(\) failed/, +'lua ssl server name: "test.com"', +] +--- no_error_log +[alert] +ssl_certificate_by_lua:1: ssl cert by lua is running! + + + +=== TEST 24: check the count of running timers +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { print("ssl cert by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + content_by_lua_block { + ngx.timer.at(0.1, function() ngx.sleep(0.3) end) + ngx.timer.at(0.11, function() ngx.sleep(0.3) end) + ngx.timer.at(0.09, function() ngx.sleep(0.3) end) + ngx.sleep(0.2) + ngx.say(ngx.timer.running_count()) + } + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: 3 +close: 1 nil + +--- error_log eval +[ +'ssl_certificate_by_lua:1: ssl cert by lua is running!', +'lua ssl server name: "test.com"', +] +--- no_error_log +[error] +[alert] + + + +=== TEST 25: get raw_client_addr - IPv4 +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen 127.0.0.1:12346 ssl; + + ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local byte = string.byte + local addr, addrtype, err = ssl.raw_client_addr() + local ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2), + byte(addr, 3), byte(addr, 4)) + print("client ip: ", ip) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", 12346) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +client ip: 127.0.0.1 + +--- no_error_log +[error] +[alert] + + + +=== TEST 26: get raw_client_addr - unix domain socket +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local addr, addrtyp, err = ssl.raw_client_addr() + print("client socket file: ", addr) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +client socket file: + +--- no_error_log +[error] +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/140-ssl-c-api.t b/src/deps/src/stream-lua-nginx-module/t/140-ssl-c-api.t new file mode 100644 index 000000000..e7cbea6bf --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/140-ssl-c-api.t @@ -0,0 +1,992 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { + plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1"); +} else { + plan tests => repeat_each() * (blocks() * 5 + 1); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->user_files) { + $block->set_value("user_files", <<'_EOC_'); +>>> defines.lua +local ffi = require "ffi" + +ffi.cdef[[ + 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_set_der_certificate(void *r, + const char *data, size_t len, char **err); + + int ngx_stream_lua_ffi_ssl_set_der_private_key(void *r, + const char *data, size_t len, char **err); + + int ngx_stream_lua_ffi_ssl_clear_certs(void *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); + +]] +_EOC_ + } + + my $stream_config = $block->stream_config || ''; + $stream_config .= <<'_EOC_'; +lua_package_path "$prefix/html/?.lua;../lua-resty-core/lib/?.lua;;"; +_EOC_ + $block->set_value("stream_config", $stream_config); +}); + +run_tests(); + +__DATA__ + +=== TEST 1: simple cert + private key +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + collectgarbage() + + require "defines" + local ffi = require "ffi" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_stream_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test.crt", "rb")) + local cert = f:read("*all") + f:close() + + local out = ffi.new("char [?]", #cert) + + local rc = ffi.C.ngx_stream_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local cert_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER cert: ", + ffi.string(errmsg[0])) + return + end + + f = assert(io.open("t/cert/test.key", "rb")) + local pkey = f:read("*all") + f:close() + + out = ffi.new("char [?]", #pkey) + + local rc = ffi.C.ngx_stream_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + return + end + + local pkey_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER priv key: ", + ffi.string(errmsg[0])) + return + end + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 2: ECDSA cert + private key +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_stream_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test_ecdsa.crt", "rb")) + local cert = f:read("*all") + f:close() + + local out = ffi.new("char [?]", #cert) + + local rc = ffi.C.ngx_stream_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local cert_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER cert: ", + ffi.string(errmsg[0])) + return + end + + f = assert(io.open("t/cert/test_ecdsa.key", "rb")) + local pkey = f:read("*all") + f:close() + + out = ffi.new("char [?]", #pkey) + + local rc = ffi.C.ngx_stream_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + return + end + + local pkey_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER priv key: ", + ffi.string(errmsg[0])) + return + end + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test_ecdsa.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 3: Handshake continue when cert_pem_to_der errors +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + local cert = "garbage data" + + local out = ffi.new("char [?]", #cert) + + local rc = ffi.C.ngx_stream_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + end + + local pkey = "garbage key data" + + out = ffi.new("char [?]", #pkey) + + local rc = ffi.C.ngx_stream_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + end + } + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +failed to parse PEM cert: PEM_read_bio_X509_AUX() +failed to parse PEM priv key: PEM_read_bio_PrivateKey() failed + +--- no_error_log +[alert] + + + +=== TEST 4: simple cert + private key cdata +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_stream_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local cert = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not cert then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_set_cert(r, cert, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata cert: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_stream_lua_ffi_free_cert(cert) + + f = assert(io.open("t/cert/test.key", "rb")) + local pkey_data = f:read("*all") + f:close() + + local pkey = ffi.C.ngx_stream_lua_ffi_parse_pem_priv_key(pkey_data, #pkey_data, errmsg) + if pkey == nil then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_set_priv_key(r, pkey, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata priv key: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_stream_lua_ffi_free_priv_key(pkey) + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 5: ECDSA cert + private key cdata +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_stream_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test_ecdsa.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local cert = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not cert then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_set_cert(r, cert, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata cert: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_stream_lua_ffi_free_cert(cert) + + f = assert(io.open("t/cert/test_ecdsa.key", "rb")) + local pkey_data = f:read("*all") + f:close() + + local pkey = ffi.C.ngx_stream_lua_ffi_parse_pem_priv_key(pkey_data, #pkey_data, errmsg) + if pkey == nil then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_set_priv_key(r, pkey, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata priv key: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_stream_lua_ffi_free_priv_key(pkey) + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test_ecdsa.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 6: verify client with CA certificates +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + local f = assert(io.open("t/cert/test.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local cert = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not cert then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, -1, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata cert: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_stream_lua_ffi_free_cert(cert) + } + + content_by_lua_block { + print('client certificate subject: ', ngx.var.ssl_client_s_dn) + ngx.say(ngx.var.ssl_client_verify) + } + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl on; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + +--- stream_response +SUCCESS + +--- error_log +client certificate subject: emailAddress=agentzh@gmail.com,CN=test.com + +--- no_error_log +[error] +[alert] + + + +=== TEST 7: verify client without CA certificates +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, nil, -1, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata cert: ", + ffi.string(errmsg[0])) + return + end + } + + content_by_lua_block { + print('client certificate subject: ', ngx.var.ssl_client_s_dn) + ngx.say(ngx.var.ssl_client_verify) + } + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl on; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + +--- stream_response +FAILED:self signed certificate + +--- error_log +client certificate subject: emailAddress=agentzh@gmail.com,CN=test.com + +--- no_error_log +[error] +[alert] + + + +=== TEST 8: verify client but client provides no certificate +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + local f = assert(io.open("t/cert/test.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local cert = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not cert then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, 1, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata cert: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_stream_lua_ffi_free_cert(cert) + } + + content_by_lua_block { + print('client certificate subject: ', ngx.var.ssl_client_s_dn) + ngx.say(ngx.var.ssl_client_verify) + } + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl on; + proxy_ssl_session_reuse off; + +--- stream_response +NONE + +--- error_log +client certificate subject: nil + +--- no_error_log +[error] +[alert] + + + +=== TEST 9: private key protected by passphrase +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate_by_lua_block { + collectgarbage() + + require "defines" + local ffi = require "ffi" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_stream_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test.crt", "rb")) + local cert = f:read("*all") + f:close() + + local out = ffi.new("char [?]", #cert) + + local rc = ffi.C.ngx_stream_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local cert_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER cert: ", + ffi.string(errmsg[0])) + return + end + + f = assert(io.open("t/cert/test.key", "rb")) + local pkey = f:read("*all") + f:close() + + out = ffi.new("char [?]", #pkey) + + local rc = ffi.C.ngx_stream_lua_ffi_priv_key_pem_to_der(pkey, #pkey, "123456", out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + return + end + + local pkey_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_stream_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER priv key: ", + ffi.string(errmsg[0])) + return + end + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] diff --git a/src/deps/src/stream-lua-nginx-module/t/141-add-variable.t b/src/deps/src/stream-lua-nginx-module/t/141-add-variable.t new file mode 100644 index 000000000..4798d215f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/141-add-variable.t @@ -0,0 +1,90 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4 + 1); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_config + lua_add_variable $foo; +--- stream_server_config + content_by_lua_block { + ngx.say(ngx.var.foo) + ngx.var.foo = "bar" + ngx.say(ngx.var.foo) + } +--- stream_response +nil +bar +--- no_error_log +[warn] +[error] + + + +=== TEST 2: works with C code +--- stream_config + lua_add_variable $foo; +--- stream_server_config + preread_by_lua_block { + ngx.var.foo = "bar" + } + + return $foo\n; +--- stream_response +bar +--- no_error_log +[warn] +[error] + + + +=== TEST 3: multiple add with same name works +--- stream_config + lua_add_variable $foo; + lua_add_variable $foo; +--- stream_server_config + preread_by_lua_block { + ngx.var.foo = "bar" + } + + return $foo\n; +--- stream_response +bar +--- no_error_log +[warn] +[error] + + + +=== TEST 4: accessible in log phase +--- stream_config + lua_add_variable $foo; + + log_format test "access log: $foo"; + access_log logs/error.log test; +--- stream_server_config + preread_by_lua_block { + ngx.var.foo = "bar" + } + + return $foo\n; +--- stream_response +bar +--- no_error_log +[warn] +[error] +--- error_log +access log: bar diff --git a/src/deps/src/stream-lua-nginx-module/t/142-req-udp-socket.t b/src/deps/src/stream-lua-nginx-module/t/142-req-udp-socket.t new file mode 100644 index 000000000..c28781ebb --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/142-req-udp-socket.t @@ -0,0 +1,285 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Dgram; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 1); + +our $HtmlDir = html_dir; + +#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +no_long_string(); +#no_diff(); +#log_level 'warn'; +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- dgram_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + if not sock then + ngx.log(ngx.ERR, "failed to get the request socket: ", err) + return ngx.exit(ngx.ERROR) + end + + local data, err = sock:receive() + if not data then + ngx.log(ngx.ERR, "failed to receive: ", err) + return ngx.exit(ngx.ERROR) + end + + -- print("data: ", data) + + local ok, err = sock:send("received: " .. data) + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return ngx.exit(ngx.ERROR) + end + } +--- dgram_request chomp +hello world! my +--- dgram_response chomp +received: hello world! my +--- no_error_log +[error] + + + +=== TEST 2: pipelined POST requests +--- dgram_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- dgram_server_config + content_by_lua_block { + local test = require "test" + test.go() + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go() + local sock, err = ngx.req.socket() + if sock then + ngx.ctx.sock = sock + else + sock:send("failed to get the request socket: ", err) + return + end + + local resp = "got the request socket\n" + + for i = 1, 5 do + local data, err, part = sock:receive(4) + if data then + resp = resp .. "received: " .. data .. "\n" + else + resp = resp .. "failed to receive: " .. err .. " [" .. part .. "]" .. "\n" + return + end + end + + sock:send(resp) +end +--- dgram_request chomp +hello, worldhiya, wo +--- dgram_response +got the request socket +received: hell +received: o, w +received: orld +received: hiya +received: , wo +--- no_error_log +[error] + + + +=== TEST 3: pipelined requests, big buffer, small steps +--- dgram_server_config + lua_socket_buffer_size 5; + content_by_lua_block { + local resp = "" + + local sock, err = ngx.req.socket() + if sock then + resp = "got the request socket\n" + else + resp = "failed to get the request socket: " .. err .. "\n" + end + + for i = 1, 11 do + local data, err, part = sock:receive(2) + if data then + resp = resp .. "received: " .. data .. "\n" + else + resp = resp .. "failed to receive: " .. err .. " [" .. part .. "]" .. "\n" + end + end + + sock:send(resp) + } +--- stap2 +M(http-lua-req-socket-consume-preread) { + println("preread: ", user_string_n($arg2, $arg3)) +} + +--- dgram_request chomp +hello world +hiya globe +--- dgram_response +got the request socket +received: he +received: ll +received: o +received: wo +received: rl +received: d + +received: hi +received: ya +received: g +received: lo +received: be +--- no_error_log +[error] + + + +=== TEST 4: failing reread after reading timeout happens +--- dgram_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + local resp = "" + + if not sock then + resp = "failed to get socket: " .. err + sock:send(resp) + + return nil + end + + local data, err, partial = sock:receive(4096) + if data then + resp = resp .. "data: " .. (data or "nil") .. ", err: " .. (err or "nil") .. ", partial: " .. (partial or "nil") .. "\n" + end + + local data, err, partial = sock:receive(4096) + if err then + resp = resp .. "data: " .. (data or "nil") .. ", err: " .. (err or "nil") .. ", partial: " .. (partial or "nil") .. "\n" + end + + sock:send(resp) + } + +--- dgram_request chomp +hello +--- dgram_response +data: hello, err: nil, partial: nil +data: nil, err: no more data, partial: nil + +--- no_error_log +[error] + + + +=== TEST 5: req socket GC'd +--- dgram_server_config + content_by_lua_block { + do + local sock, err = ngx.req.socket() + if sock then + sock:send("got the request socket") + else + sock:send("failed to get the request socket: ", err) + end + end + collectgarbage() + ngx.log(ngx.WARN, "GC cycle done") + + } +--- dgram_response chomp +got the request socket +--- no_error_log +[error] +--- grep_error_log eval: qr/stream lua finalize socket|GC cycle done/ +--- grep_error_log_out +stream lua finalize socket +GC cycle done + + + +=== TEST 6: ngx.req.socket with raw argument, argument is ignored +--- dgram_server_config + content_by_lua_block { + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "failed to get the request socket: ", err) + return ngx.exit(ngx.ERROR) + end + + local data, err = sock:receive() + if not data then + ngx.log(ngx.ERR, "failed to receive: ", err) + return ngx.exit(ngx.ERROR) + end + + -- print("data: ", data) + + local ok, err = sock:send("received: " .. data) + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return ngx.exit(ngx.ERROR) + end + } +--- dgram_request chomp +hello world! my +--- dgram_response chomp +received: hello world! my +--- no_error_log +[error] + + + +=== TEST 7: request on secondary ip address +sudo ip addr add 10.254.254.1/24 dev lo +sudo ip addr add 10.254.254.2/24 dev lo +nmap will be blocked by travis , use dig to send dns request. +--- dgram_server_config + content_by_lua_block { + local sock, err = ngx.req.socket() + if not sock then + ngx.log(ngx.ERR,"ngx.req.socket error : ", err) + return ngx.exit(ngx.ERROR) + end + + local data = sock:receive() + local ok, err = sock:send(data) + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + return ngx.exit(ngx.ERROR) + end + } +--- config + location = /dns { + content_by_lua_block { + local cmd = "dig -b 10.254.254.1 @10.254.254.2 openresty.org -p " .. tostring(ngx.var.server_port + 1) + local f = io.popen(cmd, "r") + ngx.sleep(0.2) + local result = f:read("*a") + f:close() + ngx.say("hello") + } + } +--- request +GET /dns +--- response_body +hello +--- grep_error_log eval: qr/sendto: fd.*$/ +--- grep_error_log_out eval +qr/sendto: fd:\d+ \d+ of \d+ to "10.254.254.1"/ diff --git a/src/deps/src/stream-lua-nginx-module/t/154-semaphore.t b/src/deps/src/stream-lua-nginx-module/t/154-semaphore.t new file mode 100644 index 000000000..81f2b183e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/154-semaphore.t @@ -0,0 +1,134 @@ +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3) + 1; + +log_level("debug"); +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: timer + shutdown error log +--- stream_server_config + content_by_lua_block { + local function test(pre) + + local semaphore = require "ngx.semaphore" + local sem = semaphore.new() + + local function sem_wait() + + local ok, err = sem:wait(10) + if not ok then + ngx.log(ngx.ERR, "err: ", err) + else + ngx.log(ngx.info, "wait success") + end + end + + while not ngx.worker.exiting() do + local co = ngx.thread.spawn(sem_wait) + ngx.thread.wait(co) + end + end + + local ok, err = ngx.timer.at(0, test) + ngx.log(ngx.INFO, "hello, world") + ngx.say("time: ", ok) + } +--- request +GET /t +--- stream_response_like eval +time: 1 +--- grep_error_log eval: qr/hello, world|semaphore gc wait queue is not empty/ +--- grep_error_log_out +hello, world +--- shutdown_error_log +--- no_shutdown_error_log +semaphore gc wait queue is not empty + + + +=== TEST 2: timer + shutdown error log (lua code cache off) +FIXME: this test case leaks memory. +--- stream_server_config + lua_code_cache off; + content_by_lua_block { + local function test(pre) + + local semaphore = require "ngx.semaphore" + local sem = semaphore.new() + + local function sem_wait() + + local ok, err = sem:wait(10) + if not ok then + ngx.log(ngx.ERR, "err: ", err) + else + ngx.log(ngx.ERR, "wait success") + end + end + + while not ngx.worker.exiting() do + local co = ngx.thread.spawn(sem_wait) + ngx.thread.wait(co) + end + end + + local ok, err = ngx.timer.at(0, test) + ngx.log(ngx.ERR, "hello, world") + ngx.say("time: ", ok) + } +--- request +GET /test +--- stream_response_like eval +time: 1 +--- grep_error_log eval: qr/hello, world|semaphore gc wait queue is not empty/ +--- grep_error_log_out +hello, world +--- shutdown_error_log +--- no_shutdown_error_log +semaphore gc wait queue is not empty +--- SKIP + + + +=== TEST 3: exit before post_handler was called +If gc is called before the ngx_http_lua_sema_handler and free the sema memory +ngx_http_lua_sema_handler would use the freed memory. +--- stream_server_config + content_by_lua_block { + local semaphore = require "ngx.semaphore" + local sem = semaphore.new() + + local function sem_wait() + ngx.log(ngx.INFO, "ngx.sem wait start") + local ok, err = sem:wait(10) + if not ok then + ngx.log(ngx.ERR, "ngx.sem wait err: ", err) + else + ngx.log(ngx.INFO, "ngx.sem wait success") + end + end + local co = ngx.thread.spawn(sem_wait) + ngx.log(ngx.INFO, "ngx.sem post start") + sem:post() + ngx.log(ngx.INFO, "ngx.sem post end") + ngx.say("hello") + ngx.exit(200) + ngx.say("not reach here") + } +--- request +GET /t +--- stream_response_like +hello +--- grep_error_log eval: qr/(ngx.sem .*?,|close stream connection|semaphore handler: wait queue: empty, resource count: 1)/ +--- grep_error_log_out +ngx.sem wait start, +ngx.sem post start, +ngx.sem post end, +close stream connection +semaphore handler: wait queue: empty, resource count: 1 diff --git a/src/deps/src/stream-lua-nginx-module/t/156-slow-network.t b/src/deps/src/stream-lua-nginx-module/t/156-slow-network.t new file mode 100644 index 000000000..c153d4918 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/156-slow-network.t @@ -0,0 +1,131 @@ +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 ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; +} + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4); + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + + +log_level("debug"); +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: receiveany returns anything once socket receives +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = { + '1', + 'hello', + } + + local length = 0 + for _, v in ipairs(resp) do + length = length + #v + end + + -- flush http header + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + -- send http body bytes by bytes + for _, v in ipairs(resp) do + ngx.print(v) + ngx.flush(true) + ngx.sleep(0.01) + end + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + + -- skip http header + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ' .. err) + return + end + if #data == 0 then -- read last line of head + break + end + end + + -- receive http body + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + end + ngx.say(data) + end + + sock:close() + } +--- stream_response +1 +h +e +l +l +o +--- grep_error_log eval +qr/lua tcp socket read any/ +--- grep_error_log_out +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any diff --git a/src/deps/src/stream-lua-nginx-module/t/157-socket-keepalive-hup.t b/src/deps/src/stream-lua-nginx-module/t/157-socket-keepalive-hup.t new file mode 100644 index 000000000..6cfc4fc05 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/157-socket-keepalive-hup.t @@ -0,0 +1,81 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : (); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 8); + +worker_connections(1024); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: exiting +--- stream_server_config + content_by_lua_block { + local pidfile = ngx.config.prefix() .. "/logs/nginx.pid" + local f, err = io.open(pidfile, "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local i = 0 + + local function f(premature) + print("timer prematurely expired: ", premature) + + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + print("failed to connect: ", err) + return + end + + local ok, err = sock:setkeepalive() + if not ok then + print("failed to setkeepalive: ", err) + return + end + + print("setkeepalive successfully") + end + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + os.execute("kill -HUP " .. pid) + } +--- stream_response +registered timer +--- wait: 0.3 +--- no_error_log +[error] +[alert] +[crit] +--- error_log +timer prematurely expired: true +setkeepalive successfully +lua tcp socket set keepalive while process exiting, closing connection diff --git a/src/deps/src/stream-lua-nginx-module/t/158-global-var.t b/src/deps/src/stream-lua-nginx-module/t/158-global-var.t new file mode 100644 index 000000000..6188ee440 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/158-global-var.t @@ -0,0 +1,266 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 8); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +no_long_string(); + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->error_log) { + $block->set_value("no_error_log", "[error]"); + } + +}); + +run_tests(); + +__DATA__ + +=== TEST 1: init_by_lua +--- stream_config + init_by_lua_block { + foo = 1 + } +--- stream_server_config + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } +--- stream_response_like eval +qr/^(2|3)$/ +--- grep_error_log eval: qr/old foo: \d+/ +--- grep_error_log_out eval +["old foo: 1\n", "old foo: 2\n"] + + + +=== TEST 2: init_worker_by_lua +--- stream_config + init_worker_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } +--- stream_server_config + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } +--- stream_response_like eval +qr/^(2|3)$/ +--- grep_error_log eval: qr/old foo: \d+/ +--- grep_error_log_out eval +["old foo: 1\n", "old foo: 2\n"] + + + +=== TEST 3: preread_by_lua +--- stream_server_config + preread_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + content_by_lua_block { + } +--- stream_response_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +preread_by_lua\(nginx\.conf:\d+\):3: in main chunk/, "old foo: 1\n"] + + + +=== TEST 4: content_by_lua +--- stream_server_config + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } +--- stream_response_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +content_by_lua\(nginx\.conf:\d+\):3: in main chunk, \n\z/, "old foo: 1\n"] + + + +=== TEST 5: log_by_lua +--- stream_server_config + content_by_lua_block { + ngx.say(foo) + } + log_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } +--- stream_response_like chomp +\A(?:nil|1)\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +log_by_lua\(nginx\.conf:\d+\):3: in main chunk/, "old foo: 1\n"] + + + +=== TEST 6: timer +--- stream_server_config + content_by_lua_block { + local function f() + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.sleep(0.01) + ngx.say(foo) + } +--- stream_response_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in\b)/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +content_by_lua\(nginx\.conf:\d+\):4: in\n\z/, "old foo: 1\n"] + + + +=== TEST 7: uthread +--- stream_server_config + content_by_lua_block { + local function f() + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + end + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.sleep(0.01) + ngx.say(foo) + } +--- stream_response_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|writing a global Lua variable \('\w+'\))/ +--- grep_error_log_out eval +["writing a global Lua variable \('foo'\)\n", "old foo: 1\n"] + + + +=== TEST 8: balancer_by_lua +--- stream_config + upstream backend { + server 0.0.0.1:1234; + balancer_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + } +--- stream_server_config + proxy_pass backend; +--- grep_error_log eval: qr/(old foo: \d+|writing a global Lua variable \('\w+'\))/ +--- grep_error_log_out eval +["writing a global Lua variable \('foo'\)\n", "old foo: 1\n"] +--- error_log +connect() to 0.0.0.1:1234 failed + + + +=== TEST 9: warn messages for polluting _G table when handling request +--- stream_server_config + content_by_lua_block { + if not foo then + foo = 0 + + elseif not foo1 then + _G[1] = 2 + end + + ngx.say(foo) + } +--- stream_response +0 +--- grep_error_log eval: qr/writing a global Lua variable \('\w+'\)/ +--- grep_error_log_out eval +["writing a global Lua variable \('foo'\)\n", "writing a global Lua variable \('1'\)\n"] + + + +=== TEST 10: don't show warn messages in init/init_worker +--- stream_config + init_by_lua_block { + foo = 1 + } + + init_worker_by_lua_block { + bar = 2 + } +--- stream_server_config + content_by_lua_block { + ngx.say(foo) + ngx.say(bar) + } +--- stream_response +1 +2 +--- no_error_log +setting global variable diff --git a/src/deps/src/stream-lua-nginx-module/t/159-sa-restart.t b/src/deps/src/stream-lua-nginx-module/t/159-sa-restart.t new file mode 100644 index 000000000..bb4e9a4f8 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/159-sa-restart.t @@ -0,0 +1,125 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +add_block_preprocessor(sub { + my $block = shift; + + my $stream_config = $block->stream_config || ''; + + $stream_config .= <<_EOC_; + init_by_lua_block { + function test_sa_restart() + local signals = { + --"HUP", + --"INFO", + --"XCPU", + --"USR1", + --"USR2", + "ALRM", + --"INT", + "IO", + "CHLD", + --"WINCH", + } + + for _, signame in ipairs(signals) do + local cmd = string.format("kill -s %s %d && sleep 0.01", + signame, ngx.worker.pid()) + local err = select(2, io.popen(cmd):read("*a")) + if err then + error("SIG" .. signame .. " caused: " .. err) + end + end + end + } +_EOC_ + + $block->set_value("stream_config", $stream_config); + + if (!defined $block->stream_server_config) { + my $stream_config = <<_EOC_; + content_by_lua_block { + ngx.say("ok") + } +_EOC_ + + $block->set_value("stream_server_config", $stream_config); + } + + if (!defined $block->no_error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + +plan tests => repeat_each() * (blocks() * 2 + 1); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: lua_sa_restart default - sets SA_RESTART in init_worker_by_lua* +--- stream_config + init_worker_by_lua_block { + test_sa_restart() + } + + + +=== TEST 2: lua_sa_restart off - does not set SA_RESTART +We must specify the lua_sa_restart directive in the http block as well, since +otherwise, ngx_lua's own default of lua_sa_restart is 'on', and ngx_lua +re-enables the SA_RESTART flag. +--- http_config + lua_sa_restart off; +--- stream_config + lua_sa_restart off; + + init_worker_by_lua_block { + test_sa_restart() + } +--- no_error_log +[crit] +--- error_log +Interrupted system call + + + +=== TEST 3: lua_sa_restart on (default) - sets SA_RESTART if no init_worker_by_lua* phase is defined +--- stream_server_config + content_by_lua_block { + test_sa_restart() + } + + + +=== TEST 4: lua_sa_restart on (default) - SA_RESTART is effective in content_by_lua* +--- stream_server_config + content_by_lua_block { + test_sa_restart() + } + + + +=== TEST 5: lua_sa_restart on (default) - SA_RESTART is effective in log_by_lua* +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } + + log_by_lua_block { + test_sa_restart() + } + + + +=== TEST 6: lua_sa_restart on (default) - SA_RESTART is effective in timer phase +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } + + log_by_lua_block { + ngx.timer.at(0, test_sa_restart) + } diff --git a/src/deps/src/stream-lua-nginx-module/t/160-disable-init-by-lua.t b/src/deps/src/stream-lua-nginx-module/t/160-disable-init-by-lua.t new file mode 100644 index 000000000..d8c3cbda3 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/160-disable-init-by-lua.t @@ -0,0 +1,184 @@ +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +my $html_dir = $ENV{TEST_NGINX_HTML_DIR}; +my $stream_config = <<_EOC_; + init_by_lua_block { + function set_up_ngx_tmp_conf(conf) + if conf == nil then + conf = [[ + events { + worker_connections 64; + } + stream { + init_by_lua_block { + ngx.log(ngx.ERR, "run init_by_lua") + } + } + ]] + end + + assert(os.execute("mkdir -p $html_dir/logs")) + + local conf_file = "$html_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)) + + return conf_file + end + + function get_ngx_bin_path() + local ffi = require "ffi" + ffi.cdef[[char **ngx_argv;]] + return ffi.string(ffi.C.ngx_argv[0]) + end + } +_EOC_ + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->stream_config) { + $block->set_value("stream_config", $stream_config); + } +}); + +env_to_nginx("PATH"); +log_level("warn"); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: ensure init_by_lua* is not run in signaller process +--- stream_server_config + content_by_lua_block { + local conf_file = set_up_ngx_tmp_conf() + local nginx = get_ngx_bin_path() + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -s reopen" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + } +--- error_log +failed (2: No such file or directory) +--- no_error_log eval +qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ + + + +=== TEST 2: init_by_lua* does not run when testing Nginx configuration +--- stream_server_config + content_by_lua_block { + local conf_file = set_up_ngx_tmp_conf() + local nginx = get_ngx_bin_path() + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -t" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -T" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + } +--- error_log +test is successful +--- no_error_log eval +qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ + + + +=== TEST 3: init_by_lua* does not run when testing Nginx configuration which contains 'lua_shared_dict' (GitHub #1462) +--- stream_server_config + content_by_lua_block { + local conf = [[ + events { + worker_connections 64; + } + stream { + lua_shared_dict test 64k; + init_by_lua_block { + ngx.log(ngx.ERR, "run init_by_lua with lua_shared_dict") + } + } + ]] + local conf_file = set_up_ngx_tmp_conf(conf) + local nginx = get_ngx_bin_path() + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -t" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -T" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + } +--- error_log +test is successful +--- no_error_log eval +qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ diff --git a/src/deps/src/stream-lua-nginx-module/t/161-load-resty-core.t b/src/deps/src/stream-lua-nginx-module/t/161-load-resty-core.t new file mode 100644 index 000000000..c23949242 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/161-load-resty-core.t @@ -0,0 +1,149 @@ +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->no_error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: lua_load_resty_core is automatically loaded in the Lua VM +--- stream_server_config + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + } +--- stream_response +resty.core loaded: true + + + +=== TEST 2: resty.core is automatically loaded in the Lua VM when 'lua_shared_dict' is used +--- stream_config + lua_shared_dict dogs 128k; +--- stream_server_config + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + } +--- stream_response +resty.core loaded: true + + + +=== TEST 3: resty.core is automatically loaded in the Lua VM with 'lua_code_cache off' +--- stream_config + lua_code_cache off; +--- stream_server_config + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core ~= nil) + } +--- stream_response +resty.core loaded: true + + + +=== TEST 4: resty.core loading honors the lua_package_path directive +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;;';" +--- stream_server_config + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + + resty_core.go() + } +--- stream_response +resty.core loaded: true +loaded from html dir +--- user_files +>>> resty/core.lua +return { + go = function () + ngx.say("loaded from html dir") + end +} + + + +=== TEST 5: resty.core not loading aborts the initialization +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;';" +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } +--- must_die +--- error_log eval +qr/\[alert\] .*? failed to load the 'resty\.core' module .*? \(reason: module 'resty\.core' not found:/ + + + +=== TEST 6: resty.core not loading produces an error with 'lua_code_cache off' +--- stream_config + lua_code_cache off; + + init_by_lua_block { + package.path = "" + } +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } +--- error_log eval +qr/\[error\] .*? failed to load the 'resty\.core' module .*? \(reason: module 'resty\.core' not found:/ +--- no_error_log eval +qr/\[alert\] .*? failed to load the 'resty\.core' module/ + + + +=== TEST 7: lua_load_resty_core logs a deprecation warning when specified (on) +--- stream_config + lua_load_resty_core on; +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } +--- grep_error_log eval: qr/\[warn\] .*? lua_load_resty_core is deprecated.*/ +--- grep_error_log_out eval +[ +qr/\[warn\] .*? lua_load_resty_core is deprecated \(the lua-resty-core library is required since ngx_stream_lua v0\.0\.8\) in .*?nginx\.conf:\d+/, +"" +] + + + +=== TEST 8: lua_load_resty_core logs a deprecation warning when specified (off) +--- stream_config + lua_load_resty_core off; +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } +--- grep_error_log eval: qr/\[warn\] .*? lua_load_resty_core is deprecated.*/ +--- grep_error_log_out eval +[ +qr/\[warn\] .*? lua_load_resty_core is deprecated \(the lua-resty-core library is required since ngx_stream_lua v0\.0\.8\) in .*?nginx\.conf:\d+/, +"" +] diff --git a/src/deps/src/stream-lua-nginx-module/t/162-ssl-client-hello-by.t b/src/deps/src/stream-lua-nginx-module/t/162-ssl-client-hello-by.t new file mode 100644 index 000000000..d05676e41 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/162-ssl-client-hello-by.t @@ -0,0 +1,1808 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (0\S*|1\.0\S*|1\.1\.0\S*)/) { + plan(skip_all => "too old OpenSSL, need 1.1.1, was $1"); +} else { + plan tests => repeat_each() * (blocks() * 6 + 5); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: simple logging +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/ssl_client_hello_by_lua:.*?,|\bssl client hello: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log_out eval +qr/reusable connection: 1 +reusable connection: 0 +ssl client hello: connection reusable: 0 +reusable connection: 0 +ssl_client_hello_by_lua:1: ssl client hello by lua is running!, +reusable connection: 0 +reusable connection: 0 +reusable connection: 0 +reusable connection: 0 +reusable connection: 0 +/ + + + +=== TEST 2: sleep +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl client hello by lua: ", ngx.now() - begin) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log eval +[ +'lua ssl server name: "test.com"', +qr/elapsed in ssl client hello by lua: 0.(?:09|1\d)\d+,/, +] + +--- no_error_log +[error] +[alert] + + + +=== TEST 3: timer +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + local function f() + print("my timer run!") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +my timer run! + +--- no_error_log +[error] +[alert] + + + +=== TEST 4: cosocket +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send flush_all command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("received memc reply: ", res) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +received memc reply: OK + +--- no_error_log +[error] +[alert] + + + +=== TEST 5: ngx.exit(0) - no yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_client_hello_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 6: ngx.exit(ngx.ERROR) - no yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_client_hello_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua_client_hello_by_lua: handler return value: -1, client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 7: ngx.exit(0) - yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(ngx.ERROR) - yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua_client_hello_by_lua: client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 9: lua exception - no yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_client_hello_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_client_hello_by_lua:2: bad bad bad', +'lua_client_hello_by_lua: handler return value: 500, client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +qr/context: ssl_client_hello_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 10: lua exception - yield +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + +--- stream_response +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_client_hello_by_lua:3: bad bad bad', +'lua_client_hello_by_lua: client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 11: get phase +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block {print("get_phase: ", ngx.get_phase())} + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end + collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata + +--- error_log +lua ssl server name: "test.com" +get_phase: ssl_client_hello + +--- no_error_log +[error] +[alert] + + + +=== TEST 12: connection aborted prematurely +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + ngx.sleep(0.3) + -- local ssl = require "ngx.ssl" + -- ssl.clear_certs() + print("ssl-client-hello-by-lua: after sleeping") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(150) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +failed to do SSL handshake: timeout + +--- error_log +lua ssl server name: "test.com" +ssl-client-hello-by-lua: after sleeping + +--- no_error_log +[error] +[alert] +--- wait: 0.6 + + + +=== TEST 13: simple logging (by_lua_file) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_file html/a.lua; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } + +--- user_files +>>> a.lua +print("ssl client hello by lua is running!") + +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +a.lua:1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 14: coroutine API +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function f() + local cnt = 0 + for i = 1, 20 do + print("co yield: ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i = 1, 3 do + print("co resume, status: ", coroutine.status(c)) + cr(c) + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/ +--- grep_error_log_out +co resume, status: suspended +co yield: 0 +co resume, status: suspended +co yield: 1 +co resume, status: suspended +co yield: 2 + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 15: simple user thread wait with yielding +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + function f() + ngx.sleep(0.01) + print("uthread: hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "uthread: failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + print("uthread: thread created: ", coroutine.status(t)) + + local ok, res = ngx.thread.wait(t) + if not ok then + print("uthread: failed to wait thread: ", res) + return + end + + print("uthread: ", res) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: thread created: running +uthread: hello in thread +uthread: done + + + +=== TEST 16: simple logging - use ssl_client_hello_by_lua* on the stream {} level +--- stream_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua:1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 17: simple logging - use ssl_client_hello_by_lua* on the stream {} level and server {} level +--- stream_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua on the stream level is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { print("ssl client hello by lua on the server level is running!") } + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua:1: ssl client hello by lua on the server level is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 18: use ssl_client_hello_by_lua* on the server {} level with non-ssl server +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +received: it works! +close: 1 nil + +--- no_error_log +ssl_client_hello_by_lua:1: ssl client hello by lua is running! +[error] +[alert] + + + +=== TEST 19: use ssl_client_hello_by_lua* on the stream {} level with non-ssl server +--- stream_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +received: it works! +close: 1 nil + +--- no_error_log +ssl_client_hello_by_lua:1: ssl client hello by lua is running! +[error] +[alert] + + + +=== TEST 20: listen two ports (one for ssl and one for non-ssl) in one server - connect ssl port +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_2; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua:1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 21: listen two ports (one for ssl and one for non-ssl) in one server - connect non-ssl port +--- stream_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_2; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_2) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +received: it works! +close: 1 nil + +--- no_error_log +ssl_client_hello_by_lua:1: ssl client hello by lua is running! +[error] +[alert] + + + +=== TEST 22: simple logging (syslog) +github issue #723 +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + error_log syslog:server=127.0.0.1:12345 debug; + + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log eval +[ +qr/\[error\] .*? send\(\) failed/, +'lua ssl server name: "test.com"', +] +--- no_error_log +[alert] +ssl_client_hello_by_lua:1: ssl client hello by lua is running! + + + +=== TEST 23: get raw_client_addr - IPv4 +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen 127.0.0.1:12346 ssl; + + ssl_client_hello_by_lua_block { + local ssl = require "ngx.ssl" + local byte = string.byte + local addr, addrtype, err = ssl.raw_client_addr() + local ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2), + byte(addr, 3), byte(addr, 4)) + print("client ip: ", ip) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", 12346) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +client ip: 127.0.0.1 + +--- no_error_log +[error] +[alert] + + + +=== TEST 24: get raw_client_addr - unix domain socket +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_client_hello_by_lua_block { + local ssl = require "ngx.ssl" + local addr, addrtyp, err = ssl.raw_client_addr() + print("client socket file: ", addr) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +received: it works! +close: 1 nil + +--- error_log +client socket file: + +--- no_error_log +[error] +[alert] + + + +=== TEST 25: cosocket (UDP) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + local sock = ngx.socket.udp() + + sock:settimeout(2000) + + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = sock:send(req) + if not ok then + ngx.log(ngx.ERR, "failed to send flush_all to memc: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + ngx.log(ngx.INFO, "received memc reply of ", #res, " bytes") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +--- no_error_log +[error] +[alert] +[emerg] +--- grep_error_log eval: qr/received memc reply of \d+ bytes/ +--- grep_error_log_out eval +[ +'received memc reply of 12 bytes +', +'received memc reply of 12 bytes +', +'received memc reply of 12 bytes +', +'received memc reply of 12 bytes +', +] + + + +=== TEST 26: uthread (kill) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { + local function f() + ngx.log(ngx.INFO, "uthread: hello from f()") + ngx.sleep(1) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + local ok, res = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.ERR, "failed to kill thread: ", res) + return + end + + ngx.log(ngx.INFO, "uthread: killed") + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.INFO, "uthread: failed to kill: ", err) + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + return 'it works!\n'; + } +--- stream_server_config + lua_ssl_trusted_certificate ../../cert/test.crt; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + end -- do + -- collectgarbage() + } + +--- stream_response +connected: 1 +ssl handshake: userdata +--- no_error_log +[error] +[alert] +[emerg] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: hello from f() +uthread: killed +uthread: failed to kill: already waited or killed diff --git a/src/deps/src/stream-lua-nginx-module/t/StapThread.pm b/src/deps/src/stream-lua-nginx-module/t/StapThread.pm new file mode 100644 index 000000000..e95886344 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/StapThread.pm @@ -0,0 +1,282 @@ +package t::StapThread; + +use strict; +use warnings; + +our $GCScript = <<'_EOC_'; +global ids, cur +global in_req = 0 +global alive_reqs + +function gen_id(k) { + if (ids[k]) return ids[k] + ids[k] = ++cur + return cur +} + +F(ngx_http_handler) { + if (!alive_reqs[$r] && $r == $r->main) { + in_req++ + alive_reqs[$r] = 1 + + if (in_req == 1) { + delete ids + cur = 0 + } + } +} + +F(ngx_http_free_request) { + if (alive_reqs[$r]) { + in_req-- + delete alive_reqs[$r] + } +} + +F(ngx_http_terminate_request) { + if (alive_reqs[$r]) { + in_req-- + delete alive_reqs[$r] + } +} + +M(http-lua-user-thread-spawn) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("spawn user thread %x in %x\n", c, p) +} + +M(http-lua-thread-delete) { + t = gen_id($arg2) + printf("delete thread %x\n", t) +} + +M(http-lua-user-coroutine-create) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("create %x in %x\n", c, p) +} + +M(http-lua-coroutine-done) { + t = gen_id($arg2) + printf("terminate %d: %s\n", t, $arg3 ? "ok" : "fail") + #print_ubacktrace() +} + +_EOC_ + +our $StapScript = <<'_EOC_'; +global ids, cur +global timers +global in_req = 0 +global co_status +global alive_reqs + +function gen_id(k) { + if (ids[k]) return ids[k] + ids[k] = ++cur + return cur +} + +F(ngx_http_handler) { + if (!alive_reqs[$r] && $r == $r->main) { + in_req++ + alive_reqs[$r] = 1 + + printf("in req: %d\n", in_req) + + if (in_req == 1) { + delete ids + cur = 0 + co_status[0] = "running" + co_status[1] = "suspended" + co_status[2] = "normal" + co_status[3] = "dead" + } + } +} + +F(ngx_http_free_request) { + if (alive_reqs[$r]) { + in_req-- + println("free request") + delete alive_reqs[$r] + } +} + +F(ngx_http_terminate_request) { + if (alive_reqs[$r]) { + in_req-- + println("terminate request") + delete alive_reqs[$r] + } +} + +F(ngx_http_lua_post_thread) { + id = gen_id($coctx->co) + printf("post thread %d\n", id) +} + +M(timer-add) { + timers[$arg1] = $arg2 + printf("add timer %d\n", $arg2) +} + +M(timer-del) { + printf("delete timer %d\n", timers[$arg1]) + delete timers[$arg1] +} + +M(timer-expire) { + printf("expire timer %d\n", timers[$arg1]) + delete timers[$arg1] +} + +F(ngx_http_lua_sleep_handler) { + printf("sleep handler called\n") +} + +F(ngx_http_lua_run_thread) { + id = gen_id($ctx->cur_co_ctx->co) + printf("run thread %d\n", id) + #if (id == 1) { + #print_ubacktrace() + #} +} + +probe process("/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2").function("lua_resume") { + id = gen_id($L) + printf("lua resume %d\n", id) +} + +M(http-lua-user-thread-spawn) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("spawn uthread %x in %x\n", c, p) +} + +M(http-lua-thread-delete) { + t = gen_id($arg2) + uthreads = @cast($arg3, "ngx_http_lua_ctx_t")->uthreads + printf("delete thread %x (uthreads %d)\n", t, uthreads) + #print_ubacktrace() +} + +M(http-lua-run-posted-thread) { + t = gen_id($arg2) + printf("run posted thread %d (status %s)\n", t, co_status[$arg3]) +} + +M(http-lua-user-coroutine-resume) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("resume %x in %x\n", c, p) +} + +M(http-lua-thread-yield) { + t = gen_id($arg2) + printf("thread %d yield\n", t) +} + +/* +F(ngx_http_lua_coroutine_yield) { + printf("yield %x\n", gen_id($L)) +} +*/ + +M(http-lua-user-coroutine-yield) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("yield %x in %x\n", c, p) +} + +F(ngx_http_lua_atpanic) { + printf("lua atpanic(%d):", gen_id($L)) + print_ubacktrace(); +} + +F(ngx_http_lua_run_posted_threads) { + printf("run posted threads\n") +} + +F(ngx_http_finalize_request) { + printf("finalize request %s: rc:%d c:%d a:%d\n", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main); + #if ($rc == -1) { + #print_ubacktrace() + #} +} +F(ngx_http_lua_post_subrequest) { + printf("post subreq: %s rc=%d, status=%d a=%d\n", ngx_http_req_uri($r), $rc, + $r->headers_out->status, $r == $r->main) + #print_ubacktrace() +} +M(http-subrequest-done) { + printf("subrequest %s done\n", ngx_http_req_uri($r)) +} +M(http-subrequest-wake-parent) { + printf("subrequest wake parent %s\n", ngx_http_req_uri($r->parent)) +} +M(http-lua-user-coroutine-create) { + p = gen_id($arg2) + c = gen_id($arg3) + printf("create %x in %x\n", c, p) +} + +F(ngx_http_lua_ngx_exec) { println("exec") } + +F(ngx_http_lua_ngx_exit) { println("exit") } +F(ngx_http_lua_ffi_exit) { println("exit") } + +F(ngx_http_lua_req_body_cleanup) { + println("lua req body cleanup") +} + +F(ngx_http_read_client_request_body) { + println("read client request body") +} + +F(ngx_http_lua_finalize_coroutines) { + println("finalize coroutines") +} + +F(ngx_http_lua_ngx_exit) { + println("ngx.exit() called") +} + +F(ngx_http_lua_ffi_exit) { + println("ngx.exit() called") +} + +F(ngx_http_lua_sleep_resume) { + println("lua sleep resume") +} + +M(http-lua-coroutine-done) { + t = gen_id($arg2) + printf("terminate coro %d: %s, waited by parent:%d, child cocotx: %p\n", t, $arg3 ? "ok" : "fail", $ctx->cur_co_ctx->waited_by_parent, $ctx->cur_co_ctx) + //print_ubacktrace() +} + +F(ngx_http_lua_ngx_echo) { + println("ngx.print or ngx.say") +} + +F(ngx_http_lua_del_all_threads) { + println("del all threads") +} + +/* +M(http-lua-info) { + msg = user_string($arg1) + printf("lua info: %s\n", msg) +} +*/ + +M(http-lua-user-thread-wait) { + p = gen_id($arg1) + c = gen_id($arg2) + printf("lua thread %d waiting on %d, child coctx: %p\n", p, c, $sub_coctx) +} +_EOC_ + +1; diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/google.crt b/src/deps/src/stream-lua-nginx-module/t/cert/google.crt new file mode 100644 index 000000000..0d3e4c844 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/google.crt @@ -0,0 +1,1939 @@ +# Operating CA: Comodo Group +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Low-Value Services Root" +# Serial: 1 +# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc +# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d +# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7 +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw +MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD +VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul +CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n +tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl +dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch +PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC ++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O +BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X +7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz +43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl +pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA +WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Public Services Root" +# Serial: 1 +# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f +# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5 +# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx +MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB +ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV +BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV +6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX +GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP +dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH +1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF +62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW +BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL +MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU +cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv +b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 +IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ +iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh +4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm +XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Qualified Certificates Root" +# Serial: 1 +# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb +# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf +# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16 +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 +MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK +EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh +BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq +xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G +87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i +2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U +WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 +0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G +A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr +pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL +ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm +aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv +hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm +hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 +P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y +iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no +xqE= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=Secure Certificate Services O=Comodo CA Limited +# Subject: CN=Secure Certificate Services O=Comodo CA Limited +# Label: "Comodo Secure Services root" +# Serial: 1 +# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd +# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1 +# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8 +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited +# Subject: CN=Trusted Certificate Services O=Comodo CA Limited +# Label: "Comodo Trusted Services root" +# Serial: 1 +# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27 +# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd +# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Operating CA: Comodo Group +# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN USERFirst Hardware Root CA" +# Serial: 91374294542884704022267039221184531197 +# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39 +# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7 +# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37 +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Operating CA: DigiCert +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Operating CA: Entrust Datacard +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Operating CA: Entrust Datacard +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Operating CA: Entrust Datacard +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Operating CA: Entrust Datacard +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Operating CA: GlobalSign +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Operating CA: GlobalSign +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Operating CA: GlobalSign +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Operating CA: GlobalSign +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Operating CA: GlobalSign +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Operating CA: GlobalSign +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Note: "GlobalSign Root CA - R7" not added on purpose. It is P-521. + +# Operating CA: GlobalSign +# Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA - R8 +# Subject: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA - R8 +# Label: "GlobalSign Root CA - R8" +# Serial: 1462505469299036457243287072048861 +# MD5 Fingerprint: 26:15:db:de:38:b4:45:5e:19:3f:1b:57:af:53:2b:36 +# SHA1 Fingerprint: 62:01:ff:ce:4f:09:cd:c7:e0:2f:e1:10:f4:fd:67:f0:37:1a:2f:2a +# SHA256 Fingerprint: ae:48:51:ff:42:03:9b:ad:e0:58:27:91:51:d8:26:83:04:1d:25:98:e2:40:68:3c:c5:6d:76:fb:8c:f5:3d:42 +-----BEGIN CERTIFICATE----- +MIICMzCCAbmgAwIBAgIOSBtqCfT5YHE6/oHMht0wCgYIKoZIzj0EAwMwXDELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExIDAeBgNVBAMTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFI4MB4XDTE2MDYx +NTAwMDAwMFoXDTM2MDYxNTAwMDAwMFowXDELMAkGA1UEBhMCQkUxGTAXBgNVBAoT +EEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExIDAeBgNVBAMTF0ds +b2JhbFNpZ24gUm9vdCBDQSAtIFI4MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuO58 +MIfYlB9Ua22Ynfx1+1uIq0K6jX05ft1EPTk84QWhSmRgrDemc7D5yUVLCwbQOuDx +bV/6XltaUrV240bb1R6MdHpCyUE1T8bU4ihgqzSKzrFAI0alrhkkUnyQVUTOo0Iw +QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQULzoS +JoDoisJQeG0GxDR+4kk5V3YwCgYIKoZIzj0EAwMDaAAwZQIxAMehPbKSkPrKXeAn +hII7Icz0jfiUVvIgXxHArLxfFaULyBZDp/jFf40goH9e/BYcJwIwHoz1Vr8425zm +pteEKebfDVMu6CsBt30JPLEyahqauArq6K0I8nQ51SsiNtzvRmbY +-----END CERTIFICATE----- + +# Operating CA: GoDaddy +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Operating CA: GoDaddy +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Operating CA: GoDaddy +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Operating CA: GoDaddy +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: O=Equifax OU=Equifax Secure Certificate Authority +# Subject: O=Equifax OU=Equifax Secure Certificate Authority +# Label: "Equifax Secure CA" +# Serial: 903804111 +# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4 +# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a +# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78 +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Global CA 2" +# Serial: 1 +# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9 +# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d +# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85 +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Operating CA: Symantec (GeoTrust) +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Operating CA: Symantec (Thawte) +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Operating CA: Symantec (Thawte) +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Operating CA: Symantec (Thawte) +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Operating CA: Symantec (VeriSign) +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Operating CA: Symantec (VeriSign) +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Operating CA: Symantec (VeriSign) +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Operating CA: Symantec (VeriSign) +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Operating CA: Trend Micro +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Operating CA: Trend Micro +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Operating CA: Trend Micro +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Operating CA: Trend Micro +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Operating CA: Google Trust Services LLC +# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R1 +# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R1 +# Label: "GTS Root R1" +# Serial: 6e:47:a9:c5:4b:47:0c:0d:ec:33:d0:89:b9:1c:f4:e1 +# MD5 Fingerprint: 82:1A:EF:D4:D2:4A:F2:9F:E2:3D:97:06:14:70:72:85 +# SHA1 Fingerprint: E1:C9:50:E6:EF:22:F8:4C:56:45:72:8B:92:20:60:D7:D5:A7:A3:E8 +# SHA256 Fingerprint: 2A:57:54:71:E3:13:40:BC:21:58:1C:BD:2C:F1:3E:15:84:63:20:3E:CE:94:BC:F9:D3:CC:19:6B:F0:9A:54:72 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy +MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl +cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM +f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX +mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 +zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P +fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc +vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 +Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp +zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO +Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW +k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ +DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF +lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW +Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z +XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR +gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 +d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv +J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg +DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM ++SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy +F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 +SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws +E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl +-----END CERTIFICATE----- + +# Operating CA: Google Trust Services LLC +# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R2 +# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R2 +# Label: "GTS Root R2" +# Serial: 6e:47:a9:c6:5a:b3:e7:20:c5:30:9a:3f:68:52:f2:6f +# MD5 Fingerprint: 44:ED:9A:0E:A4:09:3B:00:F2:AE:4C:A3:C6:61:B0:8B +# SHA1 Fingerprint: D2:73:96:2A:2A:5E:39:9F:73:3F:E1:C7:1E:64:3F:03:38:34:FC:4D +# SHA256 Fingerprint: C4:5D:7B:B0:8E:6D:67:E6:2E:42:35:11:0B:56:4E:5F:78:FD:92:EF:05:8C:84:0A:EA:4E:64:55:D7:58:5C:60 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy +MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl +cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv +CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg +GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu +XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd +re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu +PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1 +mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K +8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj +x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR +nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0 +kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok +twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp +8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT +z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA +pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb +pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB +R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R +RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk +0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC +5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF +izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn +yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC +-----END CERTIFICATE----- + +# Operating CA: Google Trust Services LLC +# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R3 +# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R3 +# Label: "GTS Root R3" +# Serial: 6e:47:a9:c7:6c:a9:73:24:40:89:0f:03:55:dd:8d:1d +# MD5 Fingerprint: 1A:79:5B:6B:04:52:9C:5D:C7:74:33:1B:25:9A:F9:25 +# SHA1 Fingerprint: 30:D4:24:6F:07:FF:DB:91:89:8A:0B:E9:49:66:11:EB:8C:5E:46:E5 +# SHA256 Fingerprint: 15:D5:B8:77:46:19:EA:7D:54:CE:1C:A6:D0:B0:C4:03:E0:37:A9:17:F1:31:E8:A0:4E:1E:6B:7A:71:BA:BC:E5 +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout +736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A +DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk +fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA +njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +# Operating CA: Google Trust Services LLC +# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R4 +# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R4 +# Label: "GTS Root R4" +# Serial: 6e:47:a9:c8:8b:94:b6:e8:bb:3b:2a:d8:a2:b2:c1:99 +# MD5 Fingerprint: 5D:B6:6A:C4:60:17:24:6A:1A:99:A8:4B:EE:5E:B4:26 +# SHA1 Fingerprint: 2A:1D:60:27:D9:4A:B1:0A:1C:4D:91:5C:CD:33:A0:CB:3E:2D:54:CB +# SHA256 Fingerprint: 71:CC:A5:39:1F:9E:79:4B:04:80:25:30:B3:63:E1:21:DA:8A:30:43:BB:26:66:2F:EA:4D:CA:7F:C9:51:A4:BD +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu +hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l +xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0 +CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx +sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/root-ca.crt b/src/deps/src/stream-lua-nginx-module/t/cert/root-ca.crt new file mode 100644 index 000000000..a6075b5cb --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/root-ca.crt @@ -0,0 +1,59 @@ +# Comodo AAA Services root +# This is a backup for openresty.org +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- +# ISRG Root X1 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/startcom.crt b/src/deps/src/stream-lua-nginx-module/t/cert/startcom.crt new file mode 100644 index 000000000..a5185ca10 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/startcom.crt @@ -0,0 +1,87 @@ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test.crl b/src/deps/src/stream-lua-nginx-module/t/cert/test.crl new file mode 100644 index 000000000..098fd54bf --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test.crl @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBjzCB+QIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMxEzARBgNV +BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoM +CU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNv +bTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20XDTE0MDcyMTIxNDEy +MloXDTE0MDgyMDIxNDEyMlowHDAaAgkApQ5tVpK3luIXDTE0MDcyMTIxNDEwMlqg +DzANMAsGA1UdFAQEAgIQATANBgkqhkiG9w0BAQUFAAOBgQBDZ6UY0Qg7qDoLrXXl +gJElFilZ7LiKPqjE3+Rfx7XkgdbPxjGCr77TfMm+smdvawk7WHv1AOvRH7kGrgGT +kGJZwqJ4vKa/NpEWJIMAZ1Gq9BIH/Ig6ffmPk+S9ozcVHKJDW7x4nMuotyj1hILN +EePv78DZCYMZgf8WwMElNgz6Hw== +-----END X509 CRL----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test.crt b/src/deps/src/stream-lua-nginx-module/t/cert/test.crt new file mode 100644 index 000000000..a69a01141 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqTCCAhICCQClDm1WkreW4jANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC +VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x +EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQD +DAh0ZXN0LmNvbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20wIBcN +MTQwNzIxMDMyMzQ3WhgPMjE1MTA2MTMwMzIzNDdaMIGXMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG +A1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRl +c3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNvbTCBnzANBgkq +hkiG9w0BAQEFAAOBjQAwgYkCgYEA6P18zUvtmaKQK2xePy8ZbFwSyTLw+jW6t9eZ +aiTec8X3ibN9WemrxHzkTRikxP3cAQoITRuZiQvF4Q7DO6wMkz/b0zwfgX5uedGq +047AJP6n/mwlDOjGSNomBLoXQzo7tVe60ikEm3ZyDUqnJPJMt3hImO5XSop4MPMu +Za9WhFcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA4OBb9bOyWB1//93nSXX1mdENZ +IQeyTK0Dd6My76lnZxnZ4hTWrvvd0b17KLDU6JnS2N5ee3ATVkojPidRLWLIhnh5 +0eXrcKalbO2Ce6nShoFvQCQKXN2Txmq2vO/Mud2bHAWwJALg+qi1Iih/gVYB9sct +FLg8zFOzRlYiU+6Mmw== +-----END CERTIFICATE----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test.key b/src/deps/src/stream-lua-nginx-module/t/cert/test.key new file mode 100644 index 000000000..6c1352711 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDo/XzNS+2ZopArbF4/LxlsXBLJMvD6Nbq315lqJN5zxfeJs31Z +6avEfORNGKTE/dwBCghNG5mJC8XhDsM7rAyTP9vTPB+Bfm550arTjsAk/qf+bCUM +6MZI2iYEuhdDOju1V7rSKQSbdnINSqck8ky3eEiY7ldKingw8y5lr1aEVwIDAQAB +AoGBANgB66sKMga2SKN5nQdHS3LDCkevCutu1OWM5ZcbB4Kej5kC57xsf+tzPtab +emeIVGhCPOAALqB4YcT+QtMX967oM1MjcFbtH7si5oq6UYyp3i0G9Si6jIoVHz3+ +8yOUaqwKbK+bRX8VS0YsHZmBsPK5ryN50iUwsU08nemoA94BAkEA9GS9Q5OPeFkM +tFxsIQ1f2FSsZAuN/1cpZgJqY+YaAN7MSPGTWyfd7nWG/Zgk3GO9/2ihh4gww+7B +To09GkmW4QJBAPQOHC2V+t2TA98+6Lj6+TYwcGEkhOENfVpH25mQ+kXgF/1Bd6rA +nosT1bdAY+SnmWXbSw6Kv5C20Em+bEX8WjcCQCSRRjhsRdVODbaW9Z7kb2jhEoJN +sEt6cTlQNzcHYPCsZYisjM3g4zYg47fiIfHQAsfKkhDDcfh/KvFj9LaQOEECQQCH +eBWYEDpSJ7rsfqT7mQQgWj7nDThdG/nK1TxGP71McBmg0Gg2dfkLRhVJRQqt74Is +kc9V4Rp4n6F6baL4Lh19AkEA6pZZer0kg3Kv9hjhaITIKUYdfIp9vYnDRWbQlBmR +atV8V9u9q2ETZvqfHpN+9Lu6NYR4yXIEIRf1bnIZ/mr9eQ== +-----END RSA PRIVATE KEY----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test2.crt b/src/deps/src/stream-lua-nginx-module/t/cert/test2.crt new file mode 100644 index 000000000..edc3b0df3 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test2.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAfACCQDjCkJpJUtZmjANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMC +VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x +EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZI +hvcNAQkBFhNvcGVucmVzdHlAZ21haWwuY29tMCAXDTE0MDkxMzAwMTgxMFoYDzIx +MTQwODIwMDAxODEwWjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju +aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5SZXN0eTES +MBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZIhvcNAQkBFhNvcGVucmVzdHlAZ21h +aWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDy+OVI2u5NBOeB2Cyz +Gnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7f +JrKFziJxMy4g4Kdn9G659vE7CWu/UAVjRUtc+mTBAEfjdbumizmHLG7DmnNhGl3R +NGiVNLsUInSMGfUlJRzZJXhI4QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEMmRvyN +N7uE24Tc6TR19JadNHK8g3YGktRoXWiqd/y0HY4NRPgvnK/nX7CY/wXa1j+uDO8K +e6/Ldm5RZrjtvfHJmTSAu8zkqTJz8bqRDH7kzL5Ni2Ky2x8r9dtB0ImpOiSlwvZN +snMvbrxEdwBiqlC9prV2f9aG+ACo1KnPL0j6 +-----END CERTIFICATE----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test2.key b/src/deps/src/stream-lua-nginx-module/t/cert/test2.key new file mode 100644 index 000000000..82ce6ce91 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test2.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDy+OVI2u5NBOeB2CyzGnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4 +PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7fJrKFziJxMy4g4Kdn9G659vE7CWu/UAVj +RUtc+mTBAEfjdbumizmHLG7DmnNhGl3RNGiVNLsUInSMGfUlJRzZJXhI4QIDAQAB +AoGAEqBB83PVENJvbOTFiHVfUAjGtr3R/Wnwd4jOcjHHZB3fZ9sjVoxJntxfp3s1 +dwZir2rxlqVS6i3VAFiGiVTOGo2Vvzhw2J7f58twCECmnLb2f863AkGEYe4dAndD +GHGD0WI0CBMD1sT18YCj561o0Wol5deWH0gM9pr2N3HkeIECQQD6hUKFlFhrpaHP +WNJsl6BxgE6pB5kxLcMcpIQ7P+kHUvtyvCJl5QZJqPrpPGjRsAI5Ph92rpsp/zDp +/IZNWGVjAkEA+Ele31Rt+XbV32MrLKZgBDBk+Pzss5LTn9fZ5v1k/7hrMk2VVWvk +AD6n5QiGe/g59woANpPb1T9l956SBf0d6wJABTXOS17pc9uvANP1FGMW6CVl/Wf2 +DKrJ+weE5IKQwyE7r4gwIvRfbBrClSU3fNzvPueG2f4JphbzmnoxBNzIxwJAYivY +mGNwzHehXx99/byXMHDWK+EN0n8WsBgP75Z3rekEcbJdfpYXY8Via1vwmOnwOW65 +4NqbzHix37PSNw37GwJBALxaGNpREO2Tk+oWOvsD2QyviMVae3mXAJHc6nLVdKDM +q0YvDT6VdeNYYFTkAuzJacsVXOpn6AnUMFj0OBedMhc= +-----END RSA PRIVATE KEY----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test_ecdsa.crt b/src/deps/src/stream-lua-nginx-module/t/cert/test_ecdsa.crt new file mode 100644 index 000000000..b3e1e9fbc --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test_ecdsa.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtDCCAVoCCQD0QJnL8zpA0jAKBggqhkjOPQQDAjBhMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG +A1UECgwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xNTA3MTcyMTUx +NDFaGA8yMTE1MDYyMzIxNTE0MVowYTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5S +ZXN0eTERMA8GA1UEAwwIdGVzdC5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AAT/OtGmlIlbtvvJ3OP0dm5lyEMCrMnpDTDjwBPnUZ2f+16LCmNsdtEJ0r0Sd4GM +o4Lss2JpwzPy2SLGEj3KwGKSMAoGCCqGSM49BAMCA0gAMEUCIQDbNwDkq1FiqcRD +XdbP1MPAc33N2IK9EDIfMgJ0nTL82wIgNZiL4xvCQe9UA0zC+JqHLnVCQHYAM9kI +BbvzNrt0hEM= +-----END CERTIFICATE----- diff --git a/src/deps/src/stream-lua-nginx-module/t/cert/test_ecdsa.key b/src/deps/src/stream-lua-nginx-module/t/cert/test_ecdsa.key new file mode 100644 index 000000000..46eb72c5e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/cert/test_ecdsa.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0vwBPGgv1hE6RnQo +3imyoceR+5dLsKegodOlBwnWtbuhRANCAAT/OtGmlIlbtvvJ3OP0dm5lyEMCrMnp +DTDjwBPnUZ2f+16LCmNsdtEJ0r0Sd4GMo4Lss2JpwzPy2SLGEj3KwGKS +-----END PRIVATE KEY----- diff --git a/src/deps/src/stream-lua-nginx-module/t/lib/CRC32.lua b/src/deps/src/stream-lua-nginx-module/t/lib/CRC32.lua new file mode 100755 index 000000000..70f4215f2 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/lib/CRC32.lua @@ -0,0 +1,173 @@ +--Copyright (c) 2007-2008 Neil Richardson (nrich@iinet.net.au) +-- +--Permission is hereby granted, free of charge, to any person obtaining a copy +--of this software and associated documentation files (the "Software"), to deal +--in the Software without restriction, including without limitation the rights +--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +--copies of the Software, and to permit persons to whom the Software is +--furnished to do so, subject to the following conditions: +-- +--The above copyright notice and this permission notice shall be included in all +--copies or substantial portions of the Software. +-- +--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +--IN THE SOFTWARE. + +module('CRC32', package.seeall) + +local max = 2^32 -1 + +local CRC32 = { + 0,79764919,159529838,222504665,319059676, + 398814059,445009330,507990021,638119352, + 583659535,797628118,726387553,890018660, + 835552979,1015980042,944750013,1276238704, + 1221641927,1167319070,1095957929,1595256236, + 1540665371,1452775106,1381403509,1780037320, + 1859660671,1671105958,1733955601,2031960084, + 2111593891,1889500026,1952343757,2552477408, + 2632100695,2443283854,2506133561,2334638140, + 2414271883,2191915858,2254759653,3190512472, + 3135915759,3081330742,3009969537,2905550212, + 2850959411,2762807018,2691435357,3560074640, + 3505614887,3719321342,3648080713,3342211916, + 3287746299,3467911202,3396681109,4063920168, + 4143685023,4223187782,4286162673,3779000052, + 3858754371,3904687514,3967668269,881225847, + 809987520,1023691545,969234094,662832811, + 591600412,771767749,717299826,311336399, + 374308984,453813921,533576470,25881363, + 88864420,134795389,214552010,2023205639, + 2086057648,1897238633,1976864222,1804852699, + 1867694188,1645340341,1724971778,1587496639, + 1516133128,1461550545,1406951526,1302016099, + 1230646740,1142491917,1087903418,2896545431, + 2825181984,2770861561,2716262478,3215044683, + 3143675388,3055782693,3001194130,2326604591, + 2389456536,2200899649,2280525302,2578013683, + 2640855108,2418763421,2498394922,3769900519, + 3832873040,3912640137,3992402750,4088425275, + 4151408268,4197601365,4277358050,3334271071, + 3263032808,3476998961,3422541446,3585640067, + 3514407732,3694837229,3640369242,1762451694, + 1842216281,1619975040,1682949687,2047383090, + 2127137669,1938468188,2001449195,1325665622, + 1271206113,1183200824,1111960463,1543535498, + 1489069629,1434599652,1363369299,622672798, + 568075817,748617968,677256519,907627842, + 853037301,1067152940,995781531,51762726, + 131386257,177728840,240578815,269590778, + 349224269,429104020,491947555,4046411278, + 4126034873,4172115296,4234965207,3794477266, + 3874110821,3953728444,4016571915,3609705398, + 3555108353,3735388376,3664026991,3290680682, + 3236090077,3449943556,3378572211,3174993278, + 3120533705,3032266256,2961025959,2923101090, + 2868635157,2813903052,2742672763,2604032198, + 2683796849,2461293480,2524268063,2284983834, + 2364738477,2175806836,2238787779,1569362073, + 1498123566,1409854455,1355396672,1317987909, + 1246755826,1192025387,1137557660,2072149281, + 2135122070,1912620623,1992383480,1753615357, + 1816598090,1627664531,1707420964,295390185, + 358241886,404320391,483945776,43990325, + 106832002,186451547,266083308,932423249, + 861060070,1041341759,986742920,613929101, + 542559546,756411363,701822548,3316196985, + 3244833742,3425377559,3370778784,3601682597, + 3530312978,3744426955,3689838204,3819031489, + 3881883254,3928223919,4007849240,4037393693, + 4100235434,4180117107,4259748804,2310601993, + 2373574846,2151335527,2231098320,2596047829, + 2659030626,2470359227,2550115596,2947551409, + 2876312838,2788305887,2733848168,3165939309, + 3094707162,3040238851,2985771188, +} + +local function xor(a, b) + local calc = 0 + + for i = 32, 0, -1 do + local val = 2 ^ i + local aa = false + local bb = false + + if a == 0 then + calc = calc + b + break + end + + if b == 0 then + calc = calc + a + break + end + + if a >= val then + aa = true + a = a - val + end + + if b >= val then + bb = true + b = b - val + end + + if not (aa and bb) and (aa or bb) then + calc = calc + val + end + end + + return calc +end + +local function lshift(num, left) + local res = num * (2 ^ left) + return res % (2 ^ 32) +end + +local function rshift(num, right) + local res = num / (2 ^ right) + return math.floor(res) +end + +function Hash(str) + local count = string.len(tostring(str)) + local crc = max + + local i = 1 + while count > 0 do + local byte = string.byte(str, i) + + crc = xor(lshift(crc, 8), CRC32[xor(rshift(crc, 24), byte) + 1]) + + i = i + 1 + count = count - 1 + end + + return crc +end + + +-- +-- CRC32.lua +-- +-- A pure Lua implementation of a CRC32 hashing algorithm. Slower than using a C implemtation, +-- but useful having no other dependancies. +-- +-- +-- Synopsis +-- +-- require('CRC32') +-- +-- crchash = CRC32.Hash('a string') +-- +-- Methods: +-- +-- hashval = CRC32.Hash(val) +-- Calculates and returns (as an integer) the CRC32 hash of the parameter 'val'. + diff --git a/src/deps/src/stream-lua-nginx-module/t/lib/Memcached.lua b/src/deps/src/stream-lua-nginx-module/t/lib/Memcached.lua new file mode 100755 index 000000000..e3288ac4f --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/lib/Memcached.lua @@ -0,0 +1,567 @@ +--Copyright (c) 2006-2008 Neil Richardson (nrich@iinet.net.au) +-- +--Permission is hereby granted, free of charge, to any person obtaining a copy +--of this software and associated documentation files (the "Software"), to deal +--in the Software without restriction, including without limitation the rights +--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +--copies of the Software, and to permit persons to whom the Software is +--furnished to do so, subject to the following conditions: +-- +--The above copyright notice and this permission notice shall be included in all +--copies or substantial portions of the Software. +-- +--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +--IN THE SOFTWARE. + +module('Memcached', package.seeall) + +require('socket') +require('CRC32') + +local SERVER_RETRIES = 10 + +local STATS_KEYS = { + malloc = true, + sizes = true, + slabs = true, + items = true, +} + +local FLAGS = { + 'STORABLE', + 'COMPRESSED', + 'SERIALISED', +} + +local function warn(str) + io.stderr:write(string.format('Warning: %s\n', tostring(str))) +end + +local function _select_server(cache, key) + local server_count = #cache.servers + + local hashfunc = cache.hash or CRC32.Hash + + if server_count == 1 then + return cache.servers[1].socket + else + local serverhash = hashfunc(key) + + for i = 0, SERVER_RETRIES do + local index = (serverhash % server_count) + 1 + local server = cache.servers[index].socket + + if not server then + serverhash = hashfunc(serverhash .. i) + else + return server + end + end + end + + error('No servers found') + return nil +end + +local function _retrieve(cache, key, str) + local server = _select_server(cache, key) + + server:send(str .. '\r\n') + + local function toboolean(value) + if type(value) == 'string' then + if value == 'true' then + return true + elseif value == 'false' then + return false + end + end + + return nil + end + + local function extract_flags(str) + local num = tonumber(str) + local flags = {} + + for i = table.maxn(FLAGS), 1, -1 do + local bf = 2 ^ (i - 1) + + if num >= bf then + flags[FLAGS[i]] = true + num = num - bf + end + end + + return flags + end + + local returndata = {} + while true do + local line, err = server:receive() + + if line == 'END' then + break + elseif string.sub(line, 1, 5) == 'VALUE' then + local key,flagstr,size,cas = string.match(line, 'VALUE (%S+) (%d+) (%d+)') + flags = extract_flags(flagstr) + + local data = server:receive(size) + + if flags.COMPRESSED and cache.compress_enabled then + data = cache.decompress(data) + end + + if flags.SERIALISED then + returndata[key] = cache.decode(data) + else + local ldata = tonumber(data) or toboolean(data) + + if ldata == nil then + if data == 'nil' then + returndata[key] = nil + else + returndata[key] = data + end + else + returndata[key] = ldata + end + end + end + end + + return returndata +end + +local function _send(cache, key, str) + local server = _select_server(cache, key) + + server:send(str .. "\r\n") + local line, err = server:receive() + + if not err then return line end +end + +local function _store(cache, op, key, value, expiry) + local str + local flags = 0 + + if type(value) == 'table' then + str = cache.encode(value) + -- TODO lookup rather than hard code + flags = flags + 4 + else + str = tostring(value) + end + + if cache.compress_enabled and string.len(str) > cache.compress_threshold then + local cstr = cache.compress(str) + + if string.len(cstr) < (string.len(str) * 0.8) then + str = cstr + + -- TODO lookup rather than hard code + flags = flags + 2 + end + end + + local len = string.len(str) + + expiry = expiry or 0 + + local cmd = op .. ' ' .. key .. ' ' .. flags .. ' ' .. expiry .. ' ' .. len .. '\r\n' .. str + + local res = _send(cache, key, cmd) + + if res ~= 'STORED' then + return false, res + end + + return true +end + +local function set(cache, key, value, expiry) + return _store(cache, 'set', key, value, expiry) +end + +local function add(cache, key, value, expiry) + return _store(cache, 'add', key, value, expiry) +end + +local function replace(cache, key, value, expiry) + return _store(cache, 'replace', key, value, expiry) +end + +local function get(cache, key) + local dataset = _retrieve(cache, key, 'get ' .. key) + return dataset[key] +end + +local function delete(cache, key) + local res = _send(cache, key, 'delete ' .. key) + + if res == 'NOT_FOUND' then + return false + end + + if res ~= 'DELETED' then + return false, res + end + + return true +end + +local function incr(cache, key, val) + val = val or 1 + + local res = _send(cache, key, 'incr ' .. key .. ' ' .. val) + + if res == 'ERROR' or res == 'CLIENT_ERROR' then + return false, res + end + + return res +end + +local function decr(cache, key, val) + val = val or 1 + + local res = _send(cache, key, 'decr ' .. key .. ' ' .. val) + + if res == 'ERROR' or res == 'CLIENT_ERROR' then + return false, res + end + + return res +end + +local function stats(cache, key) + local servers = {} + + key = key or '' + + if string.len(key) > 0 and not STATS_KEYS[key] then + error(string.format("Unknown stats key '%s'", key)) + end + + for i,server in pairs(cache.servers) do + server.socket:send('stats ' .. key .. '\r\n') + + local stats = {} + + while true do + local line, err = server.socket:receive() + + if line == 'END' or line == 'ERROR' then + break + end + + local k,v = string.match(line, 'STAT (%S+) (%S+)') + + if k then + stats[k] = v + end + end + + servers[server.name] = stats + end + + return servers +end + +local function get_multi(cache, ...) + local dataset = nil + + if table.maxn(cache.servers) > 1 then + dataset = {} + + for i,k in ipairs(arg) do + local data = _retrieve(cache, k, 'get ' .. k) + dataset[k] = data[k] + end + else + local keys = table.concat(arg, ' ') + dataset = _retrieve(cache, keys, 'get ' .. keys) + end + + return dataset +end + +local function flush_all(cache) + local success = true + + for i,server in ipairs(cache.servers) do + server.socket:send('flush_all\r\n') + local res = assert(server.socket:receive()) + + if res ~= 'OK' then + success = false + end + end + + return success +end + +local function disconnect_all(cache) + while true do + local server = table.remove(cache.servers) + + if not server then + break + end + + server.socket:close() + end +end + +local function set_hash(cache, hashfunc) + cache.hash = hashfunc +end + +local function set_encode(cache, func) + cache.encode = func +end + +local function set_decode(cache, func) + cache.decode = func +end + +local function set_compress(cache, func) + cache.compress = func +end + +local function set_decompress(cache, func) + cache.decompress = func +end + +function Connect(hostlist, port) + local servers = {} + + if type(hostlist) == 'table' then + for i,host in pairs(hostlist) do + local h, p + + if type(host) == 'table' then + h = host[1] + p = host[2] + elseif type(host) == 'string' then + h = host + elseif type(host) == 'number' then + p = host + h = nil + end + + if not h then + h = '127.0.0.1' + end + + if not p then + p = 11211 + end + + local server = socket.connect(h, p) + + if not server then + warn('Could not connect to ' .. h .. ':' .. p) + else + table.insert(servers, {socket = server, name = string.format('%s:%d', h, p)}) + end + end + else + local address = hostlist + + if type(address) == 'number' then + port = address + address = nil + end + + if address == nil then + address = '127.0.0.1' + end + + if port == nil then + port = 11211 + end + + local server = socket.connect(address, port) + + if not server then + warn('Could not connect to ' .. address .. ':' .. port) + else + servers = {{socket = server, name = string.format('%s:%d', address, port)}} + end + end + + if table.maxn(servers) < 1 then + error('No servers available') + end + + local cache = { + servers = servers, + + set_hash = set_hash, + set_encode = set_encode, + set_decode = set_decode, + set_decompress = set_decompress, + set_compress = set_compress, + + compress_enabled = false, + enable_compression = function(self, on) + self.compress_enabled = on + end, + + hash = nil, + encode = function() + error('No encode function set') + end, + + decode = function() + error('No decode function set') + end, + + compress = function() + error('No compress function set') + end, + + decompress = function() + error('No decompress function set') + end, + + -- 10K default + compress_threshold = 10240, + set_compress_threshold = function(self, threshold) + if threshold == nil then + self:enable_compression(false) + else + self.compress_threshold = threshold + end + end, + + set = set, + add = add, + replace = replace, + get = get, + delete = delete, + incr = incr, + decr = decr, + + get_multi = get_multi, + stats = stats, + flush_all = flush_all, + disconnect_all = disconnect_all, + } + + return cache +end + +function New(hostlist, port) + return Connect(hostlist, port) +end + +-- +-- Memcached.lua +-- +-- A pure Lua implementation of a simple memcached client. 1 or more memcached server(s) are currently supported. Requires the luasocket library. +-- See http://www.danga.com/memcached/ for more information about memcached. +-- +-- +-- +-- Synopsis +-- +-- require('Memcached') +-- +-- memcache = Memcached.Connect('some.host.com', 11000) +-- OR +-- memcache = Memcached.New('some.host.com', 11000) +-- +-- memcache:set('some_key', 1234) +-- memcache:add('new_key', 'add new value') +-- memcache:replace('existing_key', 'replace old value') +-- +-- cached_data = memcache:get('some_key') +-- +-- memcache:delete('old_key') +-- +-- +-- +-- Methods: +-- +-- memcache = Memcached.Connect() +-- Connect to memcached server at localhost on port number 11211. +-- +-- memcache = Memcached.Connect(host[, port]) +-- Connect to memcached server at 'host' on port number 'port'. If port is not provided, port 11211 is used. +-- +---memcache = Memcached.Connect(port) +-- Connect to memcached server at localhost on port number 'port'. +-- +-- memcache = Memcached.Connect({{'host', port}, 'host', port}) +-- Connect to multiple memcached servers. +-- +-- memcache:set(key, value[, expiry]) +-- Unconditionally sets a key to a given value in the memcache. The value for 'expiry' is the expiration +-- time (default is 0, never expire). +-- +-- memcache:add(key, value[, expiry]) +-- Like set, but only stores in memcache if the key doesn't already exist. +-- +-- memcache:replace(key, value[, expiry]) +-- Like set, but only stores in memcache if the key already exists. The opposite of add. +-- +-- value = memcache:get(key) +-- Retrieves a key from the memcache. Returns the value or nil +-- +-- values = memcache:get_multi(...) +-- Retrieves multiple keys from the memcache doing just one query. Returns a table of key/value pairs that were available. +-- +-- memcache:delete(key) +-- Deletes a key. Returns true on deletion, false if the key was not found. +-- +-- value = memcache:incr(key[, value]) +-- Sends a command to the server to atomically increment the value for key by value, or by 1 if value is nil. +-- Returns nil if key doesn't exist on server, otherwise it returns the new value after incrementing. Value should be zero or greater. +-- +-- value = memcache:decr(key[, value]) +-- Like incr, but decrements. Unlike incr, underflow is checked and new values are capped at 0. If server value is 1, a decrement of 2 returns 0, not -1. +-- +-- servers = memcache:stats([key]) +-- Returns a table of statistical data regarding the memcache server(s). Allowed keys are: +-- '', 'malloc', 'sizes', 'slabs', 'items' +-- +-- success = memcache:flush_all() +-- Runs the memcached "flush_all" command on all configured hosts, emptying all their caches. +-- +-- memcache:disconnect_all() +-- Closes all cached sockets to all memcached servers. +-- +-- memcache:set_hash(hashfunc) +-- Sets a custom hash function for key values. The default is a CRC32 hashing function. +-- 'hashfunc' should be defined receiving a single string parameter and returing a single integer value. +-- +-- memcache:set_encode(func) +-- Sets a custom encode function for serialising table values. 'func' should be defined receiving a single +-- table value and returning a single string value. +-- +-- memcache:set_decode(func) +-- Sets a custom decode function for deserialising table values. 'func' should be defined receiving a +-- single single and returning a single table value +-- +-- memcache:enable_compression(onflag) +-- Turns data compression support on or off. +-- +-- memcache:set_compress_threshold(size) +-- Set the compression threshold. If the value to be stored is larger than `size' bytes (and compression +-- is enabled), compress before storing. +-- +-- memcache:set_compress(func) +-- Sets a custom data compression function. 'func' should be defined receiving a single string value and +-- returning a single string value. +-- +-- memcache:set_decompress(func) +-- Sets a custom data decompression function. 'func' should be defined receiving a single string value and +-- returning a single string value. diff --git a/src/deps/src/stream-lua-nginx-module/t/lib/Redis.lua b/src/deps/src/stream-lua-nginx-module/t/lib/Redis.lua new file mode 100644 index 000000000..8bc28048d --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/lib/Redis.lua @@ -0,0 +1,1120 @@ +--[[ +Copyright (c) 2009-2011 Daniele Alessandri + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +module('Redis', package.seeall) + +local commands, network, request, response = {}, {}, {}, {} + +local defaults = { + host = '127.0.0.1', + port = 6379, + tcp_nodelay = true, + path = nil +} + +local protocol = { + newline = '\r\n', + ok = 'OK', + err = 'ERR', + queued = 'QUEUED', + null = 'nil' +} + +local lua_error = error +local function default_error_fn(message, level) + lua_error(message, (level or 1) + 1) +end + +local function merge_defaults(parameters) + if parameters == nil then + parameters = {} + end + for k, v in pairs(defaults) do + if parameters[k] == nil then + parameters[k] = defaults[k] + end + end + return parameters +end + +local function parse_boolean(v) + if v == '1' or v == 'true' or v == 'TRUE' then + return true + elseif v == '0' or v == 'false' or v == 'FALSE' then + return false + else + return nil + end +end + +local function toboolean(value) return value == 1 end + +local function fire_and_forget(client, command) + -- let's fire and forget! the connection is closed as soon + -- as the SHUTDOWN command is received by the server. + client.network.write(client, command .. protocol.newline) + return false +end + +local function zset_range_request(client, command, ...) + local args, opts = {...}, { } + + if #args >= 1 and type(args[#args]) == 'table' then + local options = table.remove(args, #args) + if options.withscores then + table.insert(opts, 'WITHSCORES') + end + end + + for _, v in pairs(opts) do table.insert(args, v) end + request.multibulk(client, command, args) +end + +local function zset_range_byscore_request(client, command, ...) + local args, opts = {...}, { } + + if #args >= 1 and type(args[#args]) == 'table' then + local options = table.remove(args, #args) + if options.limit then + table.insert(opts, 'LIMIT') + table.insert(opts, options.limit.offset or options.limit[1]) + table.insert(opts, options.limit.count or options.limit[2]) + end + if options.withscores then + table.insert(opts, 'WITHSCORES') + end + end + + for _, v in pairs(opts) do table.insert(args, v) end + request.multibulk(client, command, args) +end + +local function zset_range_reply(reply, command, ...) + local args = {...} + local opts = args[4] + if opts and (opts.withscores or string.lower(tostring(opts)) == 'withscores') then + local new_reply = { } + for i = 1, #reply, 2 do + table.insert(new_reply, { reply[i], reply[i + 1] }) + end + return new_reply + else + return reply + end +end + +local function zset_store_request(client, command, ...) + local args, opts = {...}, { } + + if #args >= 1 and type(args[#args]) == 'table' then + local options = table.remove(args, #args) + if options.weights and type(options.weights) == 'table' then + table.insert(opts, 'WEIGHTS') + for _, weight in ipairs(options.weights) do + table.insert(opts, weight) + end + end + if options.aggregate then + table.insert(opts, 'AGGREGATE') + table.insert(opts, options.aggregate) + end + end + + for _, v in pairs(opts) do table.insert(args, v) end + request.multibulk(client, command, args) +end + +local function mset_filter_args(client, command, ...) + local args, arguments = {...}, {} + if (#args == 1 and type(args[1]) == 'table') then + for k,v in pairs(args[1]) do + table.insert(arguments, k) + table.insert(arguments, v) + end + else + arguments = args + end + request.multibulk(client, command, arguments) +end + +local function hash_multi_request_builder(builder_callback) + return function(client, command, ...) + local args, arguments = {...}, { } + if #args == 2 then + table.insert(arguments, args[1]) + for k, v in pairs(args[2]) do + builder_callback(arguments, k, v) + end + else + arguments = args + end + request.multibulk(client, command, arguments) + end +end + +local function parse_info(response) + local info = {} + response:gsub('([^\r\n]*)\r\n', function(kv) + local k,v = kv:match(('([^:]*):([^:]*)'):rep(1)) + if (k:match('db%d+')) then + info[k] = {} + v:gsub(',', function(dbkv) + local dbk,dbv = kv:match('([^:]*)=([^:]*)') + info[k][dbk] = dbv + end) + else + info[k] = v + end + end) + return info +end + +local function parse_info_new(response) + local info, current = {}, nil + response:gsub('([^\r\n]*)\r\n', function(kv) + if kv == '' then return end + + local section = kv:match(('^# (%w+)'):rep(1)) + if section then + current = section:lower() + info[current] = {} + return + end + + local k,v = kv:match(('([^:]*):([^:]*)'):rep(1)) + if (k:match('db%d+')) then + info[current][k] = {} + v:gsub(',', function(dbkv) + local dbk,dbv = kv:match('([^:]*)=([^:]*)') + info[current][dbk] = dbv + end) + else + info[current][k] = v + end + end) + return info +end + +local function load_methods(proto, methods) + local redis = setmetatable ({}, getmetatable(proto)) + for i, v in pairs(proto) do redis[i] = v end + for i, v in pairs(methods) do redis[i] = v end + return redis +end + +local function create_client(proto, client_socket, methods) + local redis = load_methods(proto, methods) + redis.network = { + socket = client_socket, + read = network.read, + write = network.write, + } + redis.requests = { + multibulk = request.multibulk, + } + return redis +end + +-- ############################################################################ + +function network.write(client, buffer) + local _, err = client.network.socket:send(buffer) + if err then client.error(err) end +end + +function network.read(client, len) + if len == nil then len = '*l' end + local line, err = client.network.socket:receive(len) + if not err then return line else client.error('connection error: ' .. err) end +end + +-- ############################################################################ + +function response.read(client) + local res = client.network.read(client) + local prefix = res:sub(1, -#res) + local handler = protocol.prefixes[prefix] + if not handler then + client.error('unknown response prefix: '..prefix) + end + return handler(client, res) +end + +function response.status(client, data) + local sub = data:sub(2) + + if sub == protocol.ok then + return true + elseif sub == protocol.queued then + return { queued = true } + else + return sub + end +end + +function response.error(client, data) + local err_line = data:sub(2) + + if err_line:sub(1, 3) == protocol.err then + client.error('redis error: ' .. err_line:sub(5)) + else + client.error('redis error: ' .. err_line) + end +end + +function response.bulk(client, data) + local str = data:sub(2) + local len = tonumber(str) + if not len then + client.error('cannot parse ' .. str .. ' as data length') + end + + if len == -1 then return nil end + local next_chunk = client.network.read(client, len + 2) + return next_chunk:sub(1, -3); +end + +function response.multibulk(client, data) + local str = data:sub(2) + local list_count = tonumber(str) + + if list_count == -1 then + return nil + else + local list = {} + if list_count > 0 then + for i = 1, list_count do + table.insert(list, i, response.read(client)) + end + end + return list + end +end + +function response.integer(client, data) + local res = data:sub(2) + local number = tonumber(res) + + if not number then + if res == protocol.null then + return nil + end + client.error('cannot parse '..res..' as a numeric response.') + end + + return number +end + +protocol.prefixes = { + ['+'] = response.status, + ['-'] = response.error, + ['$'] = response.bulk, + ['*'] = response.multibulk, + [':'] = response.integer, +} + +-- ############################################################################ + +function request.raw(client, buffer) + local bufferType = type(buffer) + + if bufferType == 'table' then + client.network.write(client, table.concat(buffer)) + elseif bufferType == 'string' then + client.network.write(client, buffer) + else + client.error('argument error: ' .. bufferType) + end +end + +function request.multibulk(client, command, ...) + local args = {...} + local args_len = #args + local buffer = { true, true } + local proto_nl = protocol.newline + + if args_len == 1 and type(args[1]) == 'table' then + args_len, args = #args[1], args[1] + end + + buffer[1] = '*' .. tostring(args_len + 1) .. proto_nl + buffer[2] = '$' .. #command .. proto_nl .. command .. proto_nl + + for _, argument in pairs(args) do + s_argument = tostring(argument) + table.insert(buffer, '$' .. #s_argument .. proto_nl .. s_argument .. proto_nl) + end + + request.raw(client, buffer) +end + +-- ############################################################################ + +local function custom(command, send, parse) + return function(client, ...) + local has_reply = send(client, command, ...) + if has_reply == false then return end + local reply = response.read(client) + + if type(reply) == 'table' and reply.queued then + reply.parser = parse + return reply + else + if parse then + return parse(reply, command, ...) + else + return reply + end + end + end +end + +function command(command, opts) + if opts == nil or type(opts) == 'function' then + return custom(command, request.multibulk, opts) + else + return custom(command, opts.request or request.multibulk, opts.response) + end +end + +local define_command_impl = function(target, name, opts) + local opts = opts or {} + target[string.lower(name)] = custom( + opts.command or string.upper(name), + opts.request or request.multibulk, + opts.response or nil + ) +end + +function define_command(name, opts) + define_command_impl(commands, name, opts) +end + +local undefine_command_impl = function(target, name) + target[string.lower(name)] = nil +end + +function undefine_command(name) + undefine_command_impl(commands, name) +end + +-- ############################################################################ + +local client_prototype = {} + +client_prototype.raw_cmd = function(client, buffer) + request.raw(client, buffer .. protocol.newline) + return response.read(client) +end + +client_prototype.define_command = function(client, name, opts) + define_command_impl(client, name, opts) +end + +client_prototype.undefine_command = function(client, name) + undefine_command_impl(client, name) +end + +-- Command pipelining + +client_prototype.pipeline = function(client, block) + local requests, replies, parsers = {}, {}, {} + local socket_write, socket_read = client.network.write, client.network.read + + client.network.write = function(_, buffer) + table.insert(requests, buffer) + end + + -- TODO: this hack is necessary to temporarily reuse the current + -- request -> response handling implementation of redis-lua + -- without further changes in the code, but it will surely + -- disappear when the new command-definition infrastructure + -- will finally be in place. + client.network.read = function() return '+QUEUED' end + + local pipeline = setmetatable({}, { + __index = function(env, name) + local cmd = client[name] + if not cmd then + client.error('unknown redis command: ' .. name, 2) + end + return function(self, ...) + local reply = cmd(client, ...) + table.insert(parsers, #requests, reply.parser) + return reply + end + end + }) + + local success, retval = pcall(block, pipeline) + + client.network.write, client.network.read = socket_write, socket_read + if not success then client.error(retval, 0) end + + client.network.write(client, table.concat(requests, '')) + + for i = 1, #requests do + local reply, parser = response.read(client), parsers[i] + if parser then + reply = parser(reply) + end + table.insert(replies, i, reply) + end + + return replies, #requests +end + +-- Publish/Subscribe + +do + local channels = function(channels) + if type(channels) == 'string' then + channels = { channels } + end + return channels + end + + local subscribe = function(client, ...) + request.multibulk(client, 'subscribe', ...) + end + local psubscribe = function(client, ...) + request.multibulk(client, 'psubscribe', ...) + end + local unsubscribe = function(client, ...) + request.multibulk(client, 'unsubscribe') + end + local punsubscribe = function(client, ...) + request.multibulk(client, 'punsubscribe') + end + + local consumer_loop = function(client) + local aborting, subscriptions = false, 0 + + local abort = function() + if not aborting then + unsubscribe(client) + punsubscribe(client) + aborting = true + end + end + + return coroutine.wrap(function() + while true do + local message + local response = response.read(client) + + if response[1] == 'pmessage' then + message = { + kind = response[1], + pattern = response[2], + channel = response[3], + payload = response[4], + } + else + message = { + kind = response[1], + channel = response[2], + payload = response[3], + } + end + + if string.match(message.kind, '^p?subscribe$') then + subscriptions = subscriptions + 1 + end + if string.match(message.kind, '^p?unsubscribe$') then + subscriptions = subscriptions - 1 + end + + if aborting and subscriptions == 0 then + break + end + coroutine.yield(message, abort) + end + end) + end + + client_prototype.pubsub = function(client, subscriptions) + if type(subscriptions) == 'table' then + if subscriptions.subscribe then + subscribe(client, channels(subscriptions.subscribe)) + end + if subscriptions.psubscribe then + psubscribe(client, channels(subscriptions.psubscribe)) + end + end + return consumer_loop(client) + end +end + +-- Redis transactions (MULTI/EXEC) + +do + local function identity(...) return ... end + local emptytable = {} + + local function initialize_transaction(client, options, block, queued_parsers) + local coro = coroutine.create(block) + + if options.watch then + local watch_keys = {} + for _, key in pairs(options.watch) do + table.insert(watch_keys, key) + end + if #watch_keys > 0 then + client:watch(unpack(watch_keys)) + end + end + + local transaction_client = setmetatable({}, {__index=client}) + transaction_client.exec = function(...) + client.error('cannot use EXEC inside a transaction block') + end + transaction_client.multi = function(...) + coroutine.yield() + end + transaction_client.commands_queued = function() + return #queued_parsers + end + + assert(coroutine.resume(coro, transaction_client)) + + transaction_client.multi = nil + transaction_client.discard = function(...) + local reply = client:discard() + for i, v in pairs(queued_parsers) do + queued_parsers[i]=nil + end + coro = initialize_transaction(client, options, block, queued_parsers) + return reply + end + transaction_client.watch = function(...) + client.error('WATCH inside MULTI is not allowed') + end + setmetatable(transaction_client, { __index = function(t, k) + local cmd = client[k] + if type(cmd) == "function" then + local function queuey(self, ...) + local reply = cmd(client, ...) + assert((reply or emptytable).queued == true, 'a QUEUED reply was expected') + table.insert(queued_parsers, reply.parser or identity) + return reply + end + t[k]=queuey + return queuey + else + return cmd + end + end + }) + client:multi() + return coro + end + + local function transaction(client, options, coroutine_block, attempts) + local queued_parsers, replies = {}, {} + local retry = tonumber(attempts) or tonumber(options.retry) or 2 + local coro = initialize_transaction(client, options, coroutine_block, queued_parsers) + + local success, retval + if coroutine.status(coro) == 'suspended' then + success, retval = coroutine.resume(coro) + else + -- do not fail if the coroutine has not been resumed (missing t:multi() with CAS) + success, retval = true, 'empty transaction' + end + if #queued_parsers == 0 or not success then + client:discard() + assert(success, retval) + return replies, 0 + end + + local raw_replies = client:exec() + if not raw_replies then + if (retry or 0) <= 0 then + client.error("MULTI/EXEC transaction aborted by the server") + else + --we're not quite done yet + return transaction(client, options, coroutine_block, retry - 1) + end + end + + for i, parser in pairs(queued_parsers) do + table.insert(replies, i, parser(raw_replies[i])) + end + + return replies, #queued_parsers + end + + client_prototype.transaction = function(client, arg1, arg2) + local options, block + if not arg2 then + options, block = {}, arg1 + elseif arg1 then --and arg2, implicitly + options, block = type(arg1)=="table" and arg1 or { arg1 }, arg2 + else + client.error("Invalid parameters for redis transaction.") + end + + if not options.watch then + watch_keys = { } + for i, v in pairs(options) do + if tonumber(i) then + table.insert(watch_keys, v) + options[i] = nil + end + end + options.watch = watch_keys + elseif not (type(options.watch) == 'table') then + options.watch = { options.watch } + end + + if not options.cas then + local tx_block = block + block = function(client, ...) + client:multi() + return tx_block(client, ...) --can't wrap this in pcall because we're in a coroutine. + end + end + + return transaction(client, options, block) + end +end + +-- ############################################################################ + +local function connect_tcp(socket, parameters) + local host, port = parameters.host, tonumber(parameters.port) + local ok, err = socket:connect(host, port) + if not ok then + default_error_fn('could not connect to '..host..':'..port..' ['..err..']') + end + socket:setoption('tcp-nodelay', parameters.tcp_nodelay) + return socket +end + +local function connect_unix(socket, parameters) + local ok, err = socket:connect(parameters.path) + if not ok then + default_error_fn('could not connect to '..parameters.path..' ['..err..']') + end + return socket +end + +local function create_connection(parameters) + local perform_connection, socket + + if parameters.scheme == 'unix' then + perform_connection, socket = connect_unix, require('socket.unix') + assert(socket, 'your build of LuaSocket does not support UNIX domain sockets') + else + if parameters.scheme then + local scheme = parameters.scheme + assert(scheme == 'redis' or scheme == 'tcp', 'invalid scheme: '..scheme) + end + perform_connection, socket = connect_tcp, require('socket').tcp + end + + return perform_connection(socket(), parameters) +end + +function connect(...) + local args, parameters = {...}, nil + + if #args == 1 then + if type(args[1]) == 'table' then + parameters = args[1] + else + local uri = require('socket.url') + parameters = uri.parse(select(1, ...)) + if parameters.scheme then + if parameters.query then + for k, v in parameters.query:gmatch('([-_%w]+)=([-_%w]+)') do + if k == 'tcp_nodelay' or k == 'tcp-nodelay' then + parameters.tcp_nodelay = parse_boolean(v) + end + end + end + else + parameters.host = parameters.path + end + end + elseif #args > 1 then + local host, port = unpack(args) + parameters = { host = host, port = port } + end + + local socket = create_connection(merge_defaults(parameters)) + local client = create_client(client_prototype, socket, commands) + + client.error = default_error_fn + + return client +end + +-- ############################################################################ + +commands = { + -- commands operating on the key space + exists = command('EXISTS', { + response = toboolean + }), + del = command('DEL'), + type = command('TYPE'), + rename = command('RENAME'), + renamenx = command('RENAMENX', { + response = toboolean + }), + expire = command('EXPIRE', { + response = toboolean + }), + pexpire = command('PEXPIRE', { -- >= 2.6 + response = toboolean + }), + expireat = command('EXPIREAT', { + response = toboolean + }), + pexpireat = command('PEXPIREAT', { -- >= 2.6 + response = toboolean + }), + ttl = command('TTL'), + pttl = command('PTTL'), -- >= 2.6 + move = command('MOVE', { + response = toboolean + }), + dbsize = command('DBSIZE'), + persist = command('PERSIST', { -- >= 2.2 + response = toboolean + }), + keys = command('KEYS', { + response = function(response) + if type(response) == 'string' then + -- backwards compatibility path for Redis < 2.0 + local keys = {} + response:gsub('[^%s]+', function(key) + table.insert(keys, key) + end) + response = keys + end + return response + end + }), + randomkey = command('RANDOMKEY', { + response = function(response) + if response == '' then + return nil + else + return response + end + end + }), + sort = command('SORT', { + request = function(client, command, key, params) + --[[ params = { + by = 'weight_*', + get = 'object_*', + limit = { 0, 10 }, + sort = 'desc', + alpha = true, + } --]] + local query = { key } + + if params then + if params.by then + table.insert(query, 'BY') + table.insert(query, params.by) + end + + if type(params.limit) == 'table' then + -- TODO: check for lower and upper limits + table.insert(query, 'LIMIT') + table.insert(query, params.limit[1]) + table.insert(query, params.limit[2]) + end + + if params.get then + if (type(params.get) == 'table') then + for _, getarg in pairs(params.get) do + table.insert(query, 'GET') + table.insert(query, getarg) + end + else + table.insert(query, 'GET') + table.insert(query, params.get) + end + end + + if params.sort then + table.insert(query, params.sort) + end + + if params.alpha == true then + table.insert(query, 'ALPHA') + end + + if params.store then + table.insert(query, 'STORE') + table.insert(query, params.store) + end + end + + request.multibulk(client, command, query) + end + }), + + -- commands operating on string values + set = command('SET'), + setnx = command('SETNX', { + response = toboolean + }), + setex = command('SETEX'), -- >= 2.0 + psetex = command('PSETEX'), -- >= 2.6 + mset = command('MSET', { + request = mset_filter_args + }), + msetnx = command('MSETNX', { + request = mset_filter_args, + response = toboolean + }), + get = command('GET'), + mget = command('MGET'), + getset = command('GETSET'), + incr = command('INCR'), + incrby = command('INCRBY'), + incrbyfloat = command('INCRBYFLOAT', { -- >= 2.6 + response = function(reply, command, ...) + return tonumber(reply) + end, + }), + decr = command('DECR'), + decrby = command('DECRBY'), + append = command('APPEND'), -- >= 2.0 + substr = command('SUBSTR'), -- >= 2.0 + strlen = command('STRLEN'), -- >= 2.2 + setrange = command('SETRANGE'), -- >= 2.2 + getrange = command('GETRANGE'), -- >= 2.2 + setbit = command('SETBIT'), -- >= 2.2 + getbit = command('GETBIT'), -- >= 2.2 + + -- commands operating on lists + rpush = command('RPUSH'), + lpush = command('LPUSH'), + llen = command('LLEN'), + lrange = command('LRANGE'), + ltrim = command('LTRIM'), + lindex = command('LINDEX'), + lset = command('LSET'), + lrem = command('LREM'), + lpop = command('LPOP'), + rpop = command('RPOP'), + rpoplpush = command('RPOPLPUSH'), + blpop = command('BLPOP'), -- >= 2.0 + brpop = command('BRPOP'), -- >= 2.0 + rpushx = command('RPUSHX'), -- >= 2.2 + lpushx = command('LPUSHX'), -- >= 2.2 + linsert = command('LINSERT'), -- >= 2.2 + brpoplpush = command('BRPOPLPUSH'), -- >= 2.2 + + -- commands operating on sets + sadd = command('SADD', { + response = toboolean + }), + srem = command('SREM', { + response = toboolean + }), + spop = command('SPOP'), + smove = command('SMOVE', { + response = toboolean + }), + scard = command('SCARD'), + sismember = command('SISMEMBER', { + response = toboolean + }), + sinter = command('SINTER'), + sinterstore = command('SINTERSTORE'), + sunion = command('SUNION'), + sunionstore = command('SUNIONSTORE'), + sdiff = command('SDIFF'), + sdiffstore = command('SDIFFSTORE'), + smembers = command('SMEMBERS'), + srandmember = command('SRANDMEMBER'), + + -- commands operating on sorted sets + zadd = command('ZADD', { + response = toboolean + }), + zincrby = command('ZINCRBY'), + zrem = command('ZREM', { + response = toboolean + }), + zrange = command('ZRANGE', { + request = zset_range_request, + response = zset_range_reply, + }), + zrevrange = command('ZREVRANGE', { + request = zset_range_request, + response = zset_range_reply, + }), + zrangebyscore = command('ZRANGEBYSCORE', { + request = zset_range_byscore_request, + response = zset_range_reply, + }), + zrevrangebyscore = command('ZREVRANGEBYSCORE', { -- >= 2.2 + request = zset_range_byscore_request, + response = zset_range_reply, + }), + zunionstore = command('ZUNIONSTORE', { -- >= 2.0 + request = zset_store_request + }), + zinterstore = command('ZINTERSTORE', { -- >= 2.0 + request = zset_store_request + }), + zcount = command('ZCOUNT'), + zcard = command('ZCARD'), + zscore = command('ZSCORE'), + zremrangebyscore = command('ZREMRANGEBYSCORE'), + zrank = command('ZRANK'), -- >= 2.0 + zrevrank = command('ZREVRANK'), -- >= 2.0 + zremrangebyrank = command('ZREMRANGEBYRANK'), -- >= 2.0 + + -- commands operating on hashes + hset = command('HSET', { -- >= 2.0 + response = toboolean + }), + hsetnx = command('HSETNX', { -- >= 2.0 + response = toboolean + }), + hmset = command('HMSET', { -- >= 2.0 + request = hash_multi_request_builder(function(args, k, v) + table.insert(args, k) + table.insert(args, v) + end), + }), + hincrby = command('HINCRBY'), -- >= 2.0 + hincrbyfloat = command('HINCRBYFLOAT', {-- >= 2.6 + response = function(reply, command, ...) + return tonumber(reply) + end, + }), + hget = command('HGET'), -- >= 2.0 + hmget = command('HMGET', { -- >= 2.0 + request = hash_multi_request_builder(function(args, k, v) + table.insert(args, v) + end), + }), + hdel = command('HDEL', { -- >= 2.0 + response = toboolean + }), + hexists = command('HEXISTS', { -- >= 2.0 + response = toboolean + }), + hlen = command('HLEN'), -- >= 2.0 + hkeys = command('HKEYS'), -- >= 2.0 + hvals = command('HVALS'), -- >= 2.0 + hgetall = command('HGETALL', { -- >= 2.0 + response = function(reply, command, ...) + local new_reply = { } + for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end + return new_reply + end + }), + + -- connection related commands + ping = command('PING', { + response = function(response) return response == 'PONG' end + }), + echo = command('ECHO'), + auth = command('AUTH'), + select = command('SELECT'), + quit = command('QUIT', { + request = fire_and_forget + }), + + -- transactions + multi = command('MULTI'), -- >= 2.0 + exec = command('EXEC'), -- >= 2.0 + discard = command('DISCARD'), -- >= 2.0 + watch = command('WATCH'), -- >= 2.2 + unwatch = command('UNWATCH'), -- >= 2.2 + + -- publish - subscribe + subscribe = command('SUBSCRIBE'), -- >= 2.0 + unsubscribe = command('UNSUBSCRIBE'), -- >= 2.0 + psubscribe = command('PSUBSCRIBE'), -- >= 2.0 + punsubscribe = command('PUNSUBSCRIBE'), -- >= 2.0 + publish = command('PUBLISH'), -- >= 2.0 + + -- redis scripting + eval = command('EVAL'), -- >= 2.6 + evalsha = command('EVALSHA'), -- >= 2.6 + script = command('SCRIPT'), -- >= 2.6 + + -- remote server control commands + bgrewriteaof = command('BGREWRITEAOF'), + config = command('CONFIG', { -- >= 2.0 + response = function(reply, command, ...) + if (type(reply) == 'table') then + local new_reply = { } + for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end + return new_reply + end + + return reply + end + }), + client = command('CLIENT'), -- >= 2.4 + slaveof = command('SLAVEOF'), + save = command('SAVE'), + bgsave = command('BGSAVE'), + lastsave = command('LASTSAVE'), + flushdb = command('FLUSHDB'), + flushall = command('FLUSHALL'), + shutdown = command('SHUTDOWN', { + request = fire_and_forget + }), + slowlog = command('SLOWLOG', { -- >= 2.2.13 + response = function(reply, command, ...) + if (type(reply) == 'table') then + local structured = { } + for index, entry in ipairs(reply) do + structured[index] = { + id = tonumber(entry[1]), + timestamp = tonumber(entry[2]), + duration = tonumber(entry[3]), + command = entry[4], + } + end + return structured + end + + return reply + end + }), + info = command('INFO', { + response = function(response) + if string.find(response, '^# ') then + return parse_info_new(response) + end + return parse_info(response) + end + }), +} diff --git a/src/deps/src/stream-lua-nginx-module/t/lib/ljson.lua b/src/deps/src/stream-lua-nginx-module/t/lib/ljson.lua new file mode 100644 index 000000000..108472493 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/t/lib/ljson.lua @@ -0,0 +1,89 @@ +local ngx_null = ngx.null +local tostring = tostring +local byte = string.byte +local gsub = string.gsub +local sort = table.sort +local pairs = pairs +local ipairs = ipairs +local concat = table.concat + +local ok, new_tab = pcall(require, "table.new") +if not ok then + new_tab = function (narr, nrec) return {} end +end + +local _M = {} + +local metachars = { + ['\t'] = '\\t', + ["\\"] = "\\\\", + ['"'] = '\\"', + ['\r'] = '\\r', + ['\n'] = '\\n', +} + +local function encode_str(s) + -- XXX we will rewrite this when string.buffer is implemented + -- in LuaJIT 2.1 because string.gsub cannot be JIT compiled. + return gsub(s, '["\\\r\n\t]', metachars) +end + +local function is_arr(t) + local exp = 1 + for k, _ in pairs(t) do + if k ~= exp then + return nil + end + exp = exp + 1 + end + return exp - 1 +end + +local encode + +encode = function (v) + if v == nil or v == ngx_null then + return "null" + end + + local typ = type(v) + if typ == 'string' then + return '"' .. encode_str(v) .. '"' + end + + if typ == 'number' or typ == 'boolean' then + return tostring(v) + end + + if typ == 'table' then + local n = is_arr(v) + if n then + local bits = new_tab(n, 0) + for i, elem in ipairs(v) do + bits[i] = encode(elem) + end + return "[" .. concat(bits, ",") .. "]" + end + + local keys = {} + local i = 0 + for key, _ in pairs(v) do + i = i + 1 + keys[i] = key + end + sort(keys) + + local bits = new_tab(0, i) + i = 0 + for _, key in ipairs(keys) do + i = i + 1 + bits[i] = encode(key) .. ":" .. encode(v[key]) + end + return "{" .. concat(bits, ",") .. "}" + end + + return '"<' .. typ .. '>"' +end +_M.encode = encode + +return _M diff --git a/src/deps/src/stream-lua-nginx-module/util/build.sh b/src/deps/src/stream-lua-nginx-module/util/build.sh new file mode 100755 index 000000000..b8dc34d34 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/util/build.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# this script is for module developers. +# the ngx-build script comes from the nginx-devel-utils project on GitHub: +# +# https://github.com/openresty/nginx-devel-utils + +root=`pwd` +version=$1 +force=$2 +home=~ + + #--add-module=$root/../stream-echo-nginx-module \ +ngx-build $force $version \ + --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE_INC -I$OPENSSL_INC" \ + --with-ld-opt="-L$PCRE_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \ + --with-http_stub_status_module \ + --with-http_image_filter_module \ + --with-http_ssl_module \ + --without-mail_pop3_module \ + --without-mail_imap_module \ + --without-mail_smtp_module \ + --without-http_upstream_ip_hash_module \ + --without-http_memcached_module \ + --without-http_referer_module \ + --without-http_autoindex_module \ + --without-http_auth_basic_module \ + --without-http_userid_module \ + --with-stream_ssl_module \ + --with-stream \ + --with-stream_ssl_preread_module \ + --with-ipv6 \ + --add-module=$root/../lua-nginx-module \ + --add-module=$root/../echo-nginx-module \ + --add-module=$root/../memc-nginx-module \ + --add-module=$root/../headers-more-nginx-module \ + --add-module=$root $opts \ + --with-poll_module \ + --without-http_ssi_module \ + --with-debug || exit 1 diff --git a/src/deps/src/stream-lua-nginx-module/util/fix-cosocket-tests.pl b/src/deps/src/stream-lua-nginx-module/util/fix-cosocket-tests.pl new file mode 100755 index 000000000..4c53fd84e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/util/fix-cosocket-tests.pl @@ -0,0 +1,62 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my ($port_var, $section, $tokens_stmt, $seen_closing, $uri); +while (<>) { + if (/^--- (\w+)/) { + $section = $1; + undef $port_var; + undef $tokens_stmt; + undef $seen_closing; + undef $seen_closing; + undef $uri; + print; + next; + } + if (/^\s*set \$port (\$TEST_NGINX_\w+|\d+);/) { + $port_var = $1; + next; + } + if (defined $section && $section eq 'stream_server_config') { + if (/^\s*\#?set\s+\$\w+\s+\S+/) { + next; + } + if (/^\s*server_tokens .*/) { + $tokens_stmt = $_; + next; + } + if (/^\s*lua_socket_buffer_size/) { + s/^\s*/ /; + print; + next; + } + if (/^(.*)\bngx\.var\.port\b(.*)/) { + if (!defined $port_var) { + die "No port variable defined"; + } + print "${1}$port_var$2\n"; + next; + } + if (!$uri && m{\bGET (/\S+) }) { + $uri = $1; + } + if (!$seen_closing && /^ \}/) { + $seen_closing = 1; + print; + print "\n--- config\n"; + if (defined $tokens_stmt) { + print $tokens_stmt; + } + if (!defined $uri) { + next; + #die "No uri found"; + } + print " location = $uri {"; + next; + } + } + + print; +} diff --git a/src/deps/src/stream-lua-nginx-module/util/fix-test-indent.pl b/src/deps/src/stream-lua-nginx-module/util/fix-test-indent.pl new file mode 100755 index 000000000..7ee4b7e2d --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/util/fix-test-indent.pl @@ -0,0 +1,58 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my ($entered, $indent, $delta); +while (<>) { + if (/^(\s+)\w+_by_lua_block \{$/) { + #die "HERE, enter"; + $entered = 1; + $indent = $1; + undef $delta; + print; + next; + } + + if ($entered) { + if (/^${indent}\}$/) { + undef $entered; + undef $indent; + undef $delta; + print; + next; + } + + if (/^${indent}(\s+)(.*)/) { + my ($extra, $lua) = ($1, $2); + if (!defined $delta) { + $delta = length($extra); + if ($delta > 4) { + $delta -= 4; + } else { + $delta = 0; + } + } + #warn "delta: $delta"; + if ($delta > 0) { + $extra = " " x (length($extra) - $delta); + } + print "$indent$extra$lua\n"; + next; + } + + if (/^\s*$/) { + print; + next; + } + + undef $entered; + undef $indent; + undef $delta; + print; + next; + } + + print; + next; +} diff --git a/src/deps/src/stream-lua-nginx-module/util/port-tests.pl b/src/deps/src/stream-lua-nginx-module/util/port-tests.pl new file mode 100755 index 000000000..6142d88a7 --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/util/port-tests.pl @@ -0,0 +1,74 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my $ignore_value; +while (<>) { + if (/use Test::Nginx::Socket::Lua([^:].*)/) { + print "use Test::Nginx::Socket::Lua::Stream$1"; + next; + } + + if ($ignore_value) { + if (/^---/) { + undef $ignore_value; + } else { + next; + } + } + + if (/^--- http_config\b(.*)/) { + print "--- stream_config$1\n"; + next; + } + + if (/^--- config\b(.*)/) { + print "--- stream_server_config$1\n"; + next; + } + + if (/^\s*location .*/) { + next; + } + + if (/^\s*(set|init|log|init_worker|content|rewrite|access)_by_lua (['"])(.*?)\2;/) { + print " ${1}_by_lua_block { $3 }\n"; + next; + } + + if (/^\s*(set|init|log|init_worker|content|rewrite|access)_by_lua (['"])\s*$/) { + print " ${1}_by_lua_block {\n"; + next; + } + + if (/^--- ignore_response$/) { + print "--- stream_response\n"; + next; + } + + if (/^--- error_code/) { + next; + } + + if (/^--- request[^:]*$/) { + $ignore_value = 1; + next; + } + + if (/^\s*["'];$/) { + next; + } + + if (/^--- response_body\b(.*)/) { + print "--- stream_response$1\n"; + next; + } + + if (/^--- response_body_like\b(.*)/) { + print "--- stream_response_like$1\n"; + next; + } + + print; +} diff --git a/src/deps/src/stream-lua-nginx-module/valgrind.suppress b/src/deps/src/stream-lua-nginx-module/valgrind.suppress new file mode 100644 index 000000000..3c5f6421e --- /dev/null +++ b/src/deps/src/stream-lua-nginx-module/valgrind.suppress @@ -0,0 +1,233 @@ +{ + + Memcheck:Addr1 + fun:ngx_init_cycle + fun:ngx_master_process_cycle + fun:main +} +{ + + Memcheck:Addr4 + fun:ngx_init_cycle + fun:ngx_master_process_cycle + fun:main +} +{ + + Memcheck:Cond + fun:ngx_vslprintf + fun:ngx_snprintf + fun:ngx_sock_ntop + fun:ngx_event_accept + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers +} +{ + + Memcheck:Cond + fun:ngx_vslprintf + fun:ngx_snprintf + fun:ngx_sock_ntop + fun:ngx_event_accept + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers +} +{ + + Memcheck:Addr1 + fun:ngx_vslprintf + fun:ngx_snprintf + fun:ngx_sock_ntop + fun:ngx_event_accept +} +{ + + exp-sgcheck:SorG + fun:ngx_http_lua_ndk_set_var_get +} +{ + + exp-sgcheck:SorG + fun:ngx_http_variables_init_vars + fun:ngx_http_block +} +{ + + exp-sgcheck:SorG + fun:ngx_conf_parse +} +{ + + exp-sgcheck:SorG + fun:ngx_vslprintf + fun:ngx_log_error_core +} +{ + + Memcheck:Param + epoll_ctl(event) + fun:epoll_ctl +} +{ + + Memcheck:Param + epoll_pwait(sigmask) + fun:epoll_pwait +} +{ + + Memcheck:Cond + fun:ngx_conf_flush_files + fun:ngx_single_process_cycle +} +{ + + Memcheck:Cond + fun:memcpy + fun:ngx_vslprintf + fun:ngx_log_error_core + fun:ngx_http_charset_header_filter +} +{ + + Memcheck:Param + socketcall.setsockopt(optval) + fun:setsockopt + fun:drizzle_state_connect +} +{ + + Memcheck:Cond + fun:ngx_conf_flush_files + fun:ngx_single_process_cycle + fun:main +} +{ + + Memcheck:Leak + fun:malloc + fun:ngx_alloc + fun:ngx_event_process_init +} +{ + + Memcheck:Param + sendmsg(mmsg[0].msg_hdr) + fun:sendmmsg + fun:__libc_res_nsend +} +{ + + Memcheck:Param + sendmsg(msg.msg_iov[0]) + fun:__sendmsg_nocancel + fun:ngx_write_channel + fun:ngx_pass_open_channel + fun:ngx_start_cache_manager_processes +} +{ + + Memcheck:Cond + fun:ngx_init_cycle + fun:ngx_master_process_cycle + fun:main +} +{ + + Memcheck:Cond + fun:index + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start +} +{ + + Memcheck:Param + sendmsg(mmsg[0].msg_hdr) + fun:sendmmsg + fun:__libc_res_nsend + fun:__libc_res_nquery + fun:__libc_res_nquerydomain + fun:__libc_res_nsearch +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_set_environment + fun:ngx_single_process_cycle +} +{ + + Memcheck:Cond + obj:* +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_set_environment + fun:ngx_worker_process_init +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_create_pool + fun:main +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_set_environment + fun:ngx_single_process_cycle +} +{ + + Memcheck:Param + sendmsg(msg.msg_control) + fun:sendmsg + fun:ngx_stream_lua_udp_sendmsg + fun:ngx_stream_lua_socket_udp_send + fun:lj_BC_FUNCC +} +{ + + Memcheck:Param + sendmsg(msg.msg_iov[0]) + fun:__sendmsg_nocancel + fun:ngx_write_channel + fun:ngx_pass_open_channel + fun:ngx_start_worker_processes +} +{ + + Memcheck:Param + sendmsg(msg.msg_iov[0]) + fun:__sendmsg_nocancel + fun:ngx_write_channel + fun:ngx_pass_open_channel + fun:ngx_start_privileged_agent_processes +} +{ + + Memcheck:Param + sendmsg(msg.msg_control) + fun:__sendmsg_nocancel + fun:ngx_stream_lua_udp_sendmsg + fun:ngx_stream_lua_socket_udp_send +}