diff --git a/.gitmodules b/.gitmodules
index ec02fff..75eb4fa 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,7 +1,5 @@
[submodule "blazehttp"]
path = blazehttp
url = https://github.com/chaitin/blazehttp
-[submodule "lua-resty-t1k"]
- path = lua-resty-t1k
- url = https://github.com/chaitin/lua-resty-t1k
+
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/Safeline.iml b/.idea/Safeline.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/Safeline.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 0000000..ef53f2b
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..cc37dd1
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..e2f9281
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/blazehttp b/blazehttp
index 23e8b59..afc88a9 160000
--- a/blazehttp
+++ b/blazehttp
@@ -1 +1 @@
-Subproject commit 23e8b59cd20d475f9590e497ecb87542e25d828e
+Subproject commit afc88a94d03db1cd52156c34c3c9c58053c9f658
diff --git a/lua-resty-t1k b/lua-resty-t1k
deleted file mode 160000
index 6446166..0000000
--- a/lua-resty-t1k
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 64461663018b9e5436e51fc30d68a808459096f3
diff --git a/sdk/lua-resty-t1k/.github/workflows/release.yml b/sdk/lua-resty-t1k/.github/workflows/release.yml
new file mode 100644
index 0000000..b86272c
--- /dev/null
+++ b/sdk/lua-resty-t1k/.github/workflows/release.yml
@@ -0,0 +1,46 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - "v*"
+
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Lua
+ uses: leafo/gh-actions-lua@v10
+
+ - name: Install Luarocks
+ uses: leafo/gh-actions-luarocks@v4
+
+ - name: Extract release tag
+ id: release_tag
+ run: |
+ # Extract the tag name from the ref
+ tag="${GITHUB_REF#refs/tags/}"
+ version_without_v="${tag#v}"
+ echo "version=${tag}" >> $GITHUB_ENV
+ echo "version_without_v=${version_without_v}" >> $GITHUB_ENV
+
+ - name: Create Release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ env.version }}
+ release_name: ${{ env.version }}
+ draft: false
+ prerelease: false
+
+ - name: Upload to luarocks
+ env:
+ LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }}
+ run: |
+ luarocks install dkjson
+ luarocks upload rockspec/lua-resty-t1k-${{ env.version_without_v }}-0.rockspec --api-key=${{ secrets.LUAROCKS_API_KEY }}
diff --git a/sdk/lua-resty-t1k/.github/workflows/test.yml b/sdk/lua-resty-t1k/.github/workflows/test.yml
new file mode 100644
index 0000000..d5ceeb1
--- /dev/null
+++ b/sdk/lua-resty-t1k/.github/workflows/test.yml
@@ -0,0 +1,63 @@
+name: Test
+
+on: [ push, pull_request ]
+
+jobs:
+ luacheck:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: leafo/gh-actions-lua@v10
+ with:
+ luaVersion: "luajit-openresty"
+ - uses: leafo/gh-actions-luarocks@v4
+ - run: luarocks install luacheck
+ - run: luacheck lib
+
+ run_tests:
+ strategy:
+ matrix:
+ openresty_version:
+ - 1.17.8.2
+ - 1.19.9.1
+ - 1.21.4.3
+ - 1.25.3.1
+
+ services:
+ detector:
+ image: chaitin/safeline-detector:t1k-ci-1.6.0
+
+ runs-on: ubuntu-latest
+ container:
+ image: openresty/openresty:${{ matrix.openresty_version }}-alpine-fat
+ # --init runs tinit as PID 1 and prevents the 'WARNING: killing the child process' spam from the test suite
+ options: --init
+
+ steps:
+ - name: Install deps
+ run: |
+ apk add --no-cache bash bind-tools curl git git-lfs libarchive-tools perl perl-dev wget
+ ln -s /usr/bin/bsdtar /usr/bin/tar
+
+ - name: Install CPAN
+ run: curl -s -L http://xrl.us/cpanm > /bin/cpanm && chmod +x /bin/cpanm
+
+ - name: Cache
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.cpan
+ ~/.cache
+ key: ${{ runner.os }}-${{ matrix.openresty_version }}-cache
+
+ - name: Install Test::Nginx
+ run: cpanm -q -n Test::Nginx
+
+ - uses: actions/checkout@v4
+ with:
+ lfs: true
+
+ - name: Run tests
+ run: |
+ curl -fs -X POST -H "Content-Type: application/octet-stream" --data-binary "@ci/bytecode" "http://detector:8001/update/policy"
+ env DETECTOR_IP=$(dig detector +short) prove -r t/
diff --git a/sdk/lua-resty-t1k/.gitignore b/sdk/lua-resty-t1k/.gitignore
new file mode 100644
index 0000000..2864f68
--- /dev/null
+++ b/sdk/lua-resty-t1k/.gitignore
@@ -0,0 +1,25 @@
+# IntelliJ project files
+.idea
+*.iml
+out
+gen
+
+# Visual Studio Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+# Test::Nginx files
+t/servroot
+
+# luarocks build files
+*.src.rock
diff --git a/sdk/lua-resty-t1k/.luacheckrc b/sdk/lua-resty-t1k/.luacheckrc
new file mode 100644
index 0000000..3d0e861
--- /dev/null
+++ b/sdk/lua-resty-t1k/.luacheckrc
@@ -0,0 +1,6 @@
+std = "ngx_lua"
+redefined = false
+max_line_length = 130
+max_code_line_length = 130
+max_string_line_length = 130
+max_comment_line_length = 130
diff --git a/sdk/lua-resty-t1k/LICENSE b/sdk/lua-resty-t1k/LICENSE
new file mode 100644
index 0000000..d6460f1
--- /dev/null
+++ b/sdk/lua-resty-t1k/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2023 Beijing Chaitin Technology Co., Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/sdk/lua-resty-t1k/README.md b/sdk/lua-resty-t1k/README.md
new file mode 100644
index 0000000..cda6d8e
--- /dev/null
+++ b/sdk/lua-resty-t1k/README.md
@@ -0,0 +1,76 @@
+# lua-resty-t1k
+
+[](https://luarocks.org/modules/blaisewang/lua-resty-t1k)
+[](https://github.com/chaitin/lua-resty-t1k/releases)
+[](https://github.com/chaitin/lua-resty-t1k/blob/main/LICENSE)
+
+## Name
+
+Lua implementation of the T1K protocol for [Chaitin/SafeLine](https://github.com/chaitin/safeline) Web Application Firewall.
+
+## Status
+
+Production ready.
+
+[](https://github.com/chaitin/lua-resty-t1k/actions)
+
+## Installation
+
+```bash
+luarocks install lua-resty-t1k
+```
+
+If you are in Mainland China
+
+```bash
+luarocks install lua-resty-t1k --server https://luarocks.cn
+```
+
+## Synopsis
+
+```lua
+location / {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block", -- block or monitor or off, default off
+ host = "unix:/workdir/snserver.sock", -- required, SafeLine WAF detection service host, unix domain socket, IP, or domain is supported, string
+ port = 8000, -- required when the host is an IP or domain, SafeLine WAF detection service port, integer
+ connect_timeout = 1000, -- connect timeout, in milliseconds, integer, default 1s (1000ms)
+ send_timeout = 1000, -- send timeout, in milliseconds, integer, default 1s (1000ms)
+ read_timeout = 1000, -- read timeout, in milliseconds, integer, default 1s (1000ms)
+ req_body_size = 1024, -- request body size, in KB, integer, default 1MB (1024KB)
+ keepalive_size = 256, -- maximum concurrent idle connections to the SafeLine WAF detection service, integer, default 256
+ keepalive_timeout = 60000, -- idle connection timeout, in milliseconds, integer, default 60s (60000ms)
+ remote_addr = "http_x_forwarded_for: 1", -- remote address from ngx.var.VARIABLE, string, default from ngx.var.remote_addr
+ }
+
+ local ok, err, _ = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+}
+```
+
+## Lua Resty T1K vs. C T1K
+
+[C T1K](https://t1k.chaitin.com/), as part of SafeLine's enterprise edition, is a deployment mode crafted in C language for enhanced performance.
+It is compatible with all versions of Nginx and does not require deployment via OpenResty (lua_nginx_module).
+
+| | Lua Resty T1K | C T1K |
+|-----------------------|---------------|-------|
+| Request Detection | ✅ | ✅ |
+| Response Detection | ❌ | ✅ |
+| Health Checks* | ❌ | ✅ |
+| Cookie Protection | ❌ | ✅ |
+| Bot Protection | ❌ | ✅ |
+| Proxy-side Statistics | ❌ | ✅ |
+
+* APISIX implements health check functionality for the `chaitin-waf` plugin. For more information, please see the [chaitin-waf documentation](https://apisix.apache.org/docs/apisix/next/plugins/chaitin-waf/).
diff --git a/sdk/lua-resty-t1k/ci/.dockerignore b/sdk/lua-resty-t1k/ci/.dockerignore
new file mode 100644
index 0000000..91cf71a
--- /dev/null
+++ b/sdk/lua-resty-t1k/ci/.dockerignore
@@ -0,0 +1 @@
+/bytecode
diff --git a/sdk/lua-resty-t1k/ci/Dockerfile b/sdk/lua-resty-t1k/ci/Dockerfile
new file mode 100644
index 0000000..d4ffe5f
--- /dev/null
+++ b/sdk/lua-resty-t1k/ci/Dockerfile
@@ -0,0 +1,6 @@
+# Usage:
+# docker build -t chaitin/safeline-detector:t1k-ci-1.6.0 .
+# docker push chaitin/safeline-detector:t1k-ci-1.6.0
+FROM chaitin/safeline-detector:1.6.0
+
+RUN sed -i "s/^# bind_addr/bind_addr/; s/^# listen_port/listen_port/; s/^bind_addr: unix/# bind_addr: unix/;" /detector/snserver.yml
diff --git a/sdk/lua-resty-t1k/ci/bytecode b/sdk/lua-resty-t1k/ci/bytecode
new file mode 100644
index 0000000..d207643
Binary files /dev/null and b/sdk/lua-resty-t1k/ci/bytecode differ
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k.lua b/sdk/lua-resty-t1k/lib/resty/t1k.lua
new file mode 100644
index 0000000..15c9cd4
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k.lua
@@ -0,0 +1,97 @@
+local consts = require "resty.t1k.constants"
+local filter = require "resty.t1k.filter"
+local handler = require "resty.t1k.handler"
+local log = require "resty.t1k.log"
+local request = require "resty.t1k.request"
+local utils = require "resty.t1k.utils"
+
+local lower = string.lower
+
+local ngx = ngx
+local nlog = ngx.log
+
+local log_fmt = log.fmt
+local debug_fmt = log.debug_fmt
+
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+local DEFAULT_T1K_CONNECT_TIMEOUT = 1000 -- 1s
+local DEFAULT_T1K_SEND_TIMEOUT = 1000 -- 1s
+local DEFAULT_T1K_READ_TIMEOUT = 1000 -- 1s
+local DEFAULT_T1K_REQ_BODY_SIZE = 1024 -- 1024 KB
+local DEFAULT_T1K_KEEPALIVE_SIZE = 256
+local DEFAULT_T1K_KEEPALIVE_TIMEOUT = 60 * 1000 -- 60s
+
+function _M.do_access(t, handle)
+ local ok, err, result
+ local opts = {}
+ t = t or {}
+
+ if not t.mode then
+ return true, nil, nil
+ end
+
+ opts.mode = lower(t.mode)
+ if opts.mode == consts.MODE_OFF then
+ nlog(debug_fmt("t1k is not enabled"))
+ return true, nil, nil
+ end
+
+ if opts.mode ~= consts.MODE_OFF and opts.mode ~= consts.MODE_BLOCK and opts.mode ~= consts.MODE_MONITOR then
+ err = log_fmt("invalid t1k mode: %s", t.mode)
+ return nil, err, nil
+ end
+
+ if not t.host then
+ err = log_fmt("invalid t1k host: %s", t.host)
+ return nil, err, nil
+ end
+ opts.host = t.host
+
+ if utils.starts_with(opts.host, consts.UNIX_SOCK_PREFIX) then
+ opts.uds = true
+ else
+ if not tonumber(t.port) then
+ err = log_fmt("invalid t1k port: %s", t.port)
+ return nil, err, nil
+ end
+ opts.port = tonumber(t.port)
+ end
+
+ opts.connect_timeout = t.connect_timeout or DEFAULT_T1K_CONNECT_TIMEOUT
+ opts.send_timeout = t.send_timeout or DEFAULT_T1K_SEND_TIMEOUT
+ opts.read_timeout = t.read_timeout or DEFAULT_T1K_READ_TIMEOUT
+ opts.req_body_size = t.req_body_size or DEFAULT_T1K_REQ_BODY_SIZE
+ opts.keepalive_size = t.keepalive_size or DEFAULT_T1K_KEEPALIVE_SIZE
+ opts.keepalive_timeout = t.keepalive_timeout or DEFAULT_T1K_KEEPALIVE_TIMEOUT
+
+ if t.remote_addr then
+ local var, idx = utils.to_var_idx(t.remote_addr)
+ opts.remote_addr_var = var
+ opts.remote_addr_idx = idx
+ end
+
+ ok, err, result = request.do_request(opts)
+ if not ok then
+ return ok, err, result
+ end
+
+ if handle and opts.mode == consts.MODE_BLOCK then
+ ok, err = _M.do_handle(result)
+ end
+
+ return ok, err, result
+end
+
+function _M.do_handle(t)
+ local ok, err = handler.handle(t)
+ return ok, err
+end
+
+function _M.do_header_filter()
+ filter.do_header_filter()
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/buffer.lua b/sdk/lua-resty-t1k/lib/resty/t1k/buffer.lua
new file mode 100644
index 0000000..965e99d
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/buffer.lua
@@ -0,0 +1,24 @@
+local _M = {
+ _VERSION = '1.0.0',
+}
+
+function _M:new(o)
+ o = o or {}
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function _M:add(v)
+ self[#self + 1] = v
+end
+
+function _M:len()
+ local len = 0
+ for _, v in ipairs(self) do
+ len = len + #v
+ end
+ return len
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/constants.lua b/sdk/lua-resty-t1k/lib/resty/t1k/constants.lua
new file mode 100644
index 0000000..0f4dff7
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/constants.lua
@@ -0,0 +1,30 @@
+local t = {}
+
+t.ACTION_PASSED = "."
+t.ACTION_BLOCKED = "?"
+
+t.MODE_OFF = "off"
+t.MODE_BLOCK = "block"
+t.MODE_MONITOR = "monitor"
+
+t.T1K_HEADER_SIZE = 5
+
+t.TAG_HEAD = 0x01
+t.TAG_BODY = 0x02
+t.TAG_EXTRA = 0x03
+t.TAG_VERSION = 0x20
+t.TAG_EXTRA_HEADER = 0x23
+t.TAG_EXTRA_BODY = 0x24
+
+t.MASK_FIRST = 0x40
+t.MASK_LAST = 0x80
+
+t.NGX_HTTP_HEADER_PREFIX = "http_"
+
+t.BLOCK_CONTENT_TYPE = "application/json"
+t.BLOCK_CONTENT_FORMAT = [[
+{"code": %s, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}]]
+
+t.UNIX_SOCK_PREFIX = "unix:"
+
+return t
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/file.lua b/sdk/lua-resty-t1k/lib/resty/t1k/file.lua
new file mode 100644
index 0000000..0ed1303
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/file.lua
@@ -0,0 +1,34 @@
+local buffer = require "resty.t1k.buffer"
+
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+local buffer_size = 2 ^ 13
+
+function _M.read(p, size)
+ size = (not size or size < 0) and 0 or size
+
+ local f, err = io.open(p, "rb")
+ if not f or err then
+ return nil, err, nil
+ end
+
+ local left = size
+ local buf = buffer:new()
+
+ while left ~= 0 do
+ local block_size = math.min(left, buffer_size)
+ local block = f:read(block_size)
+ if not block then
+ break
+ end
+ buf:add(block)
+ left = math.max(left - block_size, 0)
+ end
+ f:close()
+
+ return true, nil, buf
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/filter.lua b/sdk/lua-resty-t1k/lib/resty/t1k/filter.lua
new file mode 100644
index 0000000..a192b3f
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/filter.lua
@@ -0,0 +1,50 @@
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+local find = string.find
+local sub = string.sub
+
+local ngx = ngx
+
+local function parse_extra_header(extra_header)
+ local t = {}
+ local idx = 1
+
+ while (idx <= #extra_header) do
+ local key, val
+ local _, to = find(extra_header, ":", idx)
+ if to == 0 then
+ break
+ else
+ key = sub(extra_header, idx, to - 1)
+ end
+
+ idx = to + 1
+ _, to = find(extra_header, "\n", idx)
+ if to == 0 then
+ break
+ else
+ val = sub(extra_header, idx, to - 1)
+ end
+
+ t[key] = val
+ idx = to + 1
+ end
+
+ return t
+end
+
+function _M.do_header_filter()
+ local extra_header = ngx.ctx.t1k_extra_header
+ if extra_header ~= nil then
+ local header_table = parse_extra_header(extra_header)
+ for k, v in pairs(header_table) do
+ if k ~= nil and v ~= nil then
+ ngx.header[k] = v
+ end
+ end
+ end
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/handler.lua b/sdk/lua-resty-t1k/lib/resty/t1k/handler.lua
new file mode 100644
index 0000000..e1176ea
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/handler.lua
@@ -0,0 +1,36 @@
+local consts = require "resty.t1k.constants"
+local log = require "resty.t1k.log"
+
+local fmt = string.format
+
+local ngx = ngx
+
+local log_fmt = log.fmt
+
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+function _M.handle(t)
+ local t_type = type(t)
+ if t_type ~= "table" then
+ local err = log_fmt("invalid result type: %s", t_type)
+ return nil, err
+ end
+
+ local action = t["action"]
+ if action == consts.ACTION_PASSED then
+ return true, nil
+ elseif action == consts.ACTION_BLOCKED then
+ ngx.status = t["status"] or ngx.HTTP_FORBIDDEN
+ ngx.header.content_type = consts.BLOCK_CONTENT_TYPE
+ ngx.say(fmt(consts.BLOCK_CONTENT_FORMAT, ngx.status, t["event_id"]))
+
+ return ngx.exit(ngx.status)
+ else
+ local err = log_fmt("unknown action from t1k server: %s", action)
+ return nil, err
+ end
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/log.lua b/sdk/lua-resty-t1k/lib/resty/t1k/log.lua
new file mode 100644
index 0000000..cc2a6ec
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/log.lua
@@ -0,0 +1,27 @@
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+local fmt = string.format
+
+local ERR = ngx.ERR
+local WARN = ngx.WARN
+local DEBUG = ngx.DEBUG
+
+function _M.fmt(formatstring, ...)
+ return fmt("lua-resty-t1k: " .. formatstring, ...)
+end
+
+function _M.err_fmt(formatstring, ...)
+ return ERR, _M.fmt(formatstring, ...)
+end
+
+function _M.warn_fmt(formatstring, ...)
+ return WARN, _M.fmt(formatstring, ...)
+end
+
+function _M.debug_fmt(formatstring, ...)
+ return DEBUG, _M.fmt(formatstring, ...)
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/request.lua b/sdk/lua-resty-t1k/lib/resty/t1k/request.lua
new file mode 100644
index 0000000..753225c
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/request.lua
@@ -0,0 +1,360 @@
+local bit = require "bit"
+
+local buffer = require "resty.t1k.buffer"
+local consts = require "resty.t1k.constants"
+local file = require "resty.t1k.file"
+local log = require "resty.t1k.log"
+local utils = require "resty.t1k.utils"
+local uuid = require "resty.t1k.uuid"
+
+local _M = {
+ _VERSION = '1.0.0',
+}
+
+local bor = bit.bor
+local byte = string.byte
+local char = string.char
+local fmt = string.format
+local sub = string.sub
+
+local ngx = ngx
+local nlog = ngx.log
+local ngx_req = ngx.req
+local ngx_socket = ngx.socket
+local ngx_var = ngx.var
+
+local warn_fmt = log.warn_fmt
+local debug_fmt = log.debug_fmt
+
+local KEY_EXTRA_UUID = "UUID"
+local KEY_EXTRA_LOCAL_ADDR = "LocalAddr"
+local KEY_EXTRA_LOCAL_PORT = "LocalPort"
+local KEY_EXTRA_REMOTE_ADDR = "RemoteAddr"
+local KEY_EXTRA_REMOTE_PORT = "RemotePort"
+local KEY_EXTRA_SCHEME = "Scheme"
+local KEY_EXTRA_SERVER_NAME = "ServerName"
+local KEY_EXTRA_PROXY_NAME = "ProxyName"
+local KEY_EXTRA_REQ_BEGIN_TIME = "ReqBeginTime"
+local KEY_EXTRA_HAS_RSP_IF_OK = "HasRspIfOK"
+local KEY_EXTRA_HAS_RSP_IF_BLOCK = "HasRspIfBlock"
+
+local TAG_HEAD_WITH_MASK_FIRST = bor(consts.TAG_HEAD, consts.MASK_FIRST)
+local TAG_EXTRA_WITH_MASK_LAST = bor(consts.TAG_EXTRA, consts.MASK_LAST)
+
+local T1K_PROTO = "Proto:2\n"
+local T1K_PROTO_DATA = fmt("%s%s%s", char(consts.TAG_VERSION), utils.int_to_char_length(#T1K_PROTO), T1K_PROTO)
+
+local function read_request_body(opt_req_body_size)
+ local ok, err
+ local req_body, req_body_size
+
+ local content_length = tonumber(ngx_var.http_content_length) or 0
+ local transfer_encoding = ngx_var.http_transfer_encoding
+ if content_length == 0 and not transfer_encoding then
+ return true, nil, nil
+ end
+
+ ngx_req.read_body()
+ req_body = ngx_req.get_body_data()
+ if req_body then
+ req_body_size = #req_body
+ if req_body_size > opt_req_body_size then
+ nlog(debug_fmt("request body is too long: %d bytes, cut to %d bytes", req_body_size, opt_req_body_size))
+ req_body = sub(req_body, 1, opt_req_body_size)
+ end
+
+ return true, nil, req_body
+ end
+
+ local path = ngx_req.get_body_file()
+ if not path then
+ return true, nil, nil
+ end
+
+ ok, err, req_body = file.read(path, opt_req_body_size)
+ if not ok then
+ err = fmt("failed to read temporary file %s: %s", path, err)
+ return ok, err, nil
+ end
+
+ return true, nil, req_body
+end
+
+local function get_remote_addr(remote_addr_var, remote_addr_idx)
+ local addr
+ if remote_addr_var then
+ addr = utils.get_indexed_element(ngx_var[remote_addr_var], remote_addr_idx)
+ end
+ return addr or ngx_var.remote_addr
+end
+
+local function build_header()
+ local http_version = ngx_req.http_version()
+ if http_version < 2.0 then
+ return true, nil, ngx_req.raw_header()
+ end
+
+ local headers, err = ngx_req.get_headers(0, true)
+ if err then
+ err = fmt("failed to call ngx_req.get_headers: %s", err)
+ return nil, err, nil
+ end
+
+ local buf = buffer:new()
+ buf:add(ngx_req.get_method())
+ buf:add(" ")
+ buf:add(ngx_var.request_uri)
+ buf:add(fmt(" HTTP/%.1f\r\n", http_version))
+
+ for k, v in pairs(headers) do
+ buf:add(k .. ": " .. v .. "\r\n")
+ end
+ buf:add("\r\n")
+
+ return true, nil, buf
+end
+
+local function build_body(opts)
+ local ok, err
+ local body
+
+ local req_body_size = opts.req_body_size * 1024
+ ok, err, body = read_request_body(req_body_size)
+ if not ok then
+ return ok, err, nil
+ end
+
+ return true, nil, body
+end
+
+local function build_extra(opts)
+ local err
+
+ local src_ip = get_remote_addr(opts.remote_addr_var, opts.remote_addr_idx)
+ if not src_ip then
+ err = fmt("failed to get remote_addr, var: %s, idx %d", opts.remote_addr_var, opts.remote_addr_idx)
+ return nil, err
+ end
+
+ local src_port = ngx_var.remote_port
+ if not src_port then
+ err = "failed to get ngx_var.remote_port"
+ return nil, err, nil
+ end
+
+ local local_ip = ngx_var.server_addr
+ if not local_ip then
+ err = "failed to get ngx_var.server_addr"
+ return nil, err, nil
+ end
+
+ local local_port = ngx_var.server_port
+ if not local_port then
+ err = "failed to get ngx_var.server_port"
+ return nil, err, nil
+ end
+
+ local extra = buffer:new({
+ KEY_EXTRA_UUID, ":", uuid.generate_v4(), "\n",
+ KEY_EXTRA_REMOTE_ADDR, ":", src_ip, "\n",
+ KEY_EXTRA_REMOTE_PORT, ":", src_port, "\n",
+ KEY_EXTRA_LOCAL_ADDR, ":", local_ip, "\n",
+ KEY_EXTRA_LOCAL_PORT, ":", local_port, "\n",
+ KEY_EXTRA_SCHEME, ":", ngx_var.scheme, "\n",
+ KEY_EXTRA_SERVER_NAME, ":", ngx_var.server_name, "\n",
+ KEY_EXTRA_PROXY_NAME, ":", ngx_var.hostname, "\n",
+ KEY_EXTRA_REQ_BEGIN_TIME, ":", fmt("%.0f", ngx_req.start_time() * 1000000), "\n",
+ KEY_EXTRA_HAS_RSP_IF_OK, ":n\n",
+ KEY_EXTRA_HAS_RSP_IF_BLOCK, ":n\n"
+ })
+
+ return true, nil, extra
+end
+
+local function do_send(sock, data)
+ local ok, err = sock:send(data)
+ if not ok then
+ return ok, err
+ end
+ return true, nil
+end
+
+local function receive_data(s, srv)
+ local t = {}
+ local ft = true
+ local finished
+
+ repeat
+ local err
+ local tag, length, packet, rsp_body
+
+ packet, err = s:receive(consts.T1K_HEADER_SIZE)
+ if err then
+ err = fmt("failed to receive info packet from t1k server %s: %s", srv, err)
+ return nil, err, nil
+ end
+ if not packet then
+ err = fmt("empty packet from t1k server %s", srv)
+ return nil, err, nil
+ end
+
+ if ft then
+ if not utils.is_mask_first(byte(packet, 1, 1)) then
+ err = fmt("first packet is not MASK_FIRST from t1k server %s", srv)
+ return nil, err, nil
+ end
+ ft = false
+ end
+
+ finished, tag, length = utils.packet_parser(packet)
+ if length > 0 then
+ rsp_body, err = s:receive(length)
+ if not rsp_body or #rsp_body ~= length then
+ err = fmt("failed to receive payload from t1k server %s: %s", srv, err)
+ return nil, err, nil
+ end
+ t[tag] = rsp_body
+ end
+
+ until (finished)
+
+ return true, nil, t
+end
+
+local function get_socket(opts)
+ local ok, err
+ local count, sock, server
+
+ sock, err = ngx_socket.tcp()
+ if not sock then
+ err = fmt("failed to create socket: %s", err)
+ return nil, err, nil, nil
+ end
+
+ sock:settimeouts(opts.connect_timeout, opts.send_timeout, opts.read_timeout)
+
+ if opts.uds then
+ server = opts.host
+ ok, err = sock:connect(opts.host)
+ else
+ server = fmt("%s:%d", opts.host, opts.port)
+ ok, err = sock:connect(opts.host, opts.port)
+ end
+ if not ok then
+ sock:close()
+ err = fmt("failed to connect to t1k server %s: %s", server, err)
+ return ok, err, nil, nil
+ end
+ nlog(debug_fmt("successfully connected to t1k server %s", server))
+
+ count, err = sock:getreusedtimes()
+ if not count then
+ nlog(warn_fmt("failed to get reused times from t1k server %s: %s", server, err))
+ end
+
+ if count == 0 then
+ ok, err = sock:setoption("keepalive", true)
+ if not ok then
+ nlog(warn_fmt("failed to set keepalive for t1k server %s: %s", server, err))
+ end
+ ok, err = sock:setoption("reuseaddr", true)
+ if not ok then
+ nlog(warn_fmt("failed to set reuseaddr for t1k server %s: %s", server, err))
+ end
+ ok, err = sock:setoption("tcp-nodelay", true)
+ if not ok then
+ nlog(warn_fmt("failed to set tcp-nodelay for t1k server %s: %s", server, err))
+ end
+ end
+
+ return true, nil, sock, server
+end
+
+local function do_socket(opts, header, body, extra)
+ local ok, err
+ local t, sock, server
+
+ ok, err, sock, server = get_socket(opts)
+ if not ok then
+ err = fmt("failed to get socket: %s", err)
+ return ok, err, nil
+ end
+
+ ok, err = do_send(sock, { char(TAG_HEAD_WITH_MASK_FIRST), utils.int_to_char_length(header:len()), header })
+ if not ok then
+ sock:close()
+ err = fmt("failed to send header data to t1k server %s: %s", server, err)
+ return ok, err, nil
+ end
+
+ if body ~= nil then
+ ok, err = do_send(sock, { char(consts.TAG_BODY), utils.int_to_char_length(body:len()), body })
+ if not ok then
+ sock:close()
+ err = fmt("failed to send body data to t1k server %s: %s", server, err)
+ return ok, err, nil
+ end
+ end
+
+ ok, err = do_send(sock, { T1K_PROTO_DATA, char(TAG_EXTRA_WITH_MASK_LAST), utils.int_to_char_length(extra:len()), extra })
+ if not ok then
+ sock:close()
+ err = fmt("failed to send extra data to t1k server %s: %s", server, err)
+ return ok, err, nil
+ end
+
+ ok, err, t = receive_data(sock, server)
+ if not ok then
+ return ok, err, nil
+ end
+
+ ok, err = sock:setkeepalive(opts.keepalive_timeout, opts.keepalive_size)
+ if not ok then
+ nlog(warn_fmt("failed to set keepalive: %s", err))
+ sock:close()
+ end
+
+ return true, nil, t
+end
+
+function _M.do_request(opts)
+ local ok, err
+ local header, body, extra, t
+
+ ok, err, header = build_header(opts)
+ if not ok then
+ return ok, err, nil
+ end
+
+ ok, err, body = build_body(opts)
+ if not ok then
+ return ok, err, nil
+ end
+
+ ok, err, extra = build_extra(opts)
+ if not ok then
+ return ok, err, nil
+ end
+
+ ok, err, t = do_socket(opts, header, body, extra)
+ if not ok then
+ return ok, err, nil
+ end
+
+ if opts.mode == consts.MODE_BLOCK then
+ local extra_header = t[consts.TAG_EXTRA_HEADER]
+ if extra_header then
+ ngx.ctx.t1k_extra_header = extra_header
+ end
+ end
+
+ local result = {
+ action = t[consts.TAG_HEAD],
+ status = t[consts.TAG_BODY],
+ event_id = utils.get_event_id(t[consts.TAG_EXTRA_BODY]),
+ }
+
+ return true, nil, result
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/utils.lua b/sdk/lua-resty-t1k/lib/resty/t1k/utils.lua
new file mode 100644
index 0000000..b0480a1
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/utils.lua
@@ -0,0 +1,123 @@
+local consts = require "resty.t1k.constants"
+
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+local band = bit.band
+local bnot = bit.bnot
+local lshift = bit.lshift
+local rshift = bit.rshift
+
+local abs = math.abs
+local char = string.char
+
+local ngx = ngx
+local ngx_re = ngx.re
+local re_match = ngx_re.match
+local re_gmatch = ngx_re.gmatch
+local re_gsub = ngx_re.gsub
+
+local NOT_MASK_FIRST = bnot(consts.MASK_FIRST)
+local NOT_MASK_LAST = bnot(consts.MASK_LAST)
+
+function _M.int_to_char_length(x)
+ return char(band(x, 0xff)) .. char(band(rshift(x, 8), 0xff)) ..
+ char(band(rshift(x, 16), 0xff)) .. char(band(rshift(x, 24), 0xff))
+end
+
+function _M.char_to_int_length(l)
+ return l:byte(1, 1) + lshift(l:byte(2, 2), 8) + lshift(l:byte(3, 3), 16) + lshift(l:byte(4, 4), 24)
+end
+
+function _M.is_mask_first(b)
+ return band(b, consts.MASK_FIRST) == consts.MASK_FIRST
+end
+
+function _M.is_mask_last(b)
+ return band(b, consts.MASK_LAST) == consts.MASK_LAST
+end
+
+local function tag_parser(tag)
+ return band(band(tag, NOT_MASK_FIRST), NOT_MASK_LAST)
+end
+
+function _M.packet_parser(header)
+ if #header ~= consts.T1K_HEADER_SIZE then
+ return true, nil, 0
+ end
+
+ local fb = header:byte(1, 1)
+ local finished = _M.is_mask_last(fb)
+ local tag = tag_parser(fb)
+ local length = _M.char_to_int_length(header:sub(2, 5))
+
+ return finished, tag, length
+end
+
+function _M.starts_with(str, start)
+ return str:sub(1, #start) == start
+end
+
+function _M.to_var_idx(o)
+ local var = o
+ local idx
+
+ local _, p = o:find(":")
+ if p then
+ var = o:sub(1, p - 1)
+ idx = tonumber(o:sub(p + 1))
+ end
+
+ var = re_gsub(var:lower(), "-", "_")
+ if not _M.starts_with(var, consts.NGX_HTTP_HEADER_PREFIX) then
+ var = consts.NGX_HTTP_HEADER_PREFIX .. var
+ end
+
+ return var, idx
+end
+
+function _M.get_indexed_element(str, idx)
+ if not str or not idx or idx == 0 then
+ return str
+ end
+
+ local it, err = re_gmatch(str, [[([^,\s]+)]], "jo")
+ if err then
+ return nil
+ end
+
+ local t = {}
+ for m, e in it do
+ if e then
+ return nil
+ end
+ table.insert(t, m[1])
+ end
+
+ local len = #t
+ if len < abs(idx) then
+ return nil
+ end
+
+ return t[idx > 0 and idx or (len + idx + 1)]
+end
+
+function _M.get_event_id(str)
+ if not str then
+ return nil
+ end
+
+ local m, err = re_match(str, [[]], "jo")
+ if err then
+ return nil
+ end
+
+ if m then
+ return m[1]
+ end
+
+ return nil
+end
+
+return _M
diff --git a/sdk/lua-resty-t1k/lib/resty/t1k/uuid.lua b/sdk/lua-resty-t1k/lib/resty/t1k/uuid.lua
new file mode 100644
index 0000000..9c04e97
--- /dev/null
+++ b/sdk/lua-resty-t1k/lib/resty/t1k/uuid.lua
@@ -0,0 +1,100 @@
+local bit = require "bit"
+local ffi = require "ffi"
+
+local log = require "resty.t1k.log"
+
+local _M = {
+ _VERSION = '1.0.0'
+}
+
+local C = ffi.C
+local N_BYTES = 32
+local random = math.random
+
+local nlog = ngx.log
+
+local warn_fmt = log.warn_fmt
+
+ffi.cdef [[
+ int RAND_bytes(unsigned char *buf, int num);
+]]
+
+local function _rand_bytes(buf, len)
+ return C.RAND_bytes(buf, len)
+end
+
+local function rand_bytes(len)
+ local buf = ffi.new("char[?]", len)
+ local ok, ret = pcall(_rand_bytes, buf, len)
+
+ if not ok or ret ~= 1 then
+ nlog(warn_fmt("call RAND_bytes failed: %s", ret))
+ return nil
+ end
+
+ return ffi.string(buf, len)
+end
+
+do
+ local band = bit.band
+ local bor = bit.bor
+ local tohex = bit.tohex
+
+ local fmt = string.format
+ local byte = string.byte
+
+ function _M.generate_v4()
+ local bytes = rand_bytes(N_BYTES)
+
+ -- fallback to math.random based method
+ if not bytes then
+ return (fmt('%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s',
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+
+ tohex(bor(band(random(0, 255), 0x0F), 0x40), 2),
+ tohex(random(0, 255), 2),
+
+ tohex(bor(band(random(0, 255), 0x3F), 0x80), 2),
+ tohex(random(0, 255), 2),
+
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2),
+ tohex(random(0, 255), 2)))
+ end
+
+ return fmt('%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s',
+ tohex(byte(bytes, 1, 2), 2),
+ tohex(byte(bytes, 3, 4), 2),
+ tohex(byte(bytes, 5, 6), 2),
+ tohex(byte(bytes, 7, 8), 2),
+
+ tohex(byte(bytes, 9, 10), 2),
+ tohex(byte(bytes, 11, 12), 2),
+
+ tohex(bor(band(byte(bytes, 13, 14), 0x0F), 0x40), 2),
+ tohex(byte(bytes, 15, 16), 2),
+
+ tohex(bor(band(byte(bytes, 17, 18), 0x3F), 0x80), 2),
+ tohex(byte(bytes, 19, 20), 2),
+
+ tohex(byte(bytes, 21, 22), 2),
+ tohex(byte(bytes, 23, 24), 2),
+ tohex(byte(bytes, 25, 26), 2),
+ tohex(byte(bytes, 27, 28), 2),
+ tohex(byte(bytes, 29, 30), 2),
+ tohex(byte(bytes, 31, 32), 2))
+ end
+end
+
+return setmetatable(_M, {
+ __call = _M.generate_v4
+})
diff --git a/sdk/lua-resty-t1k/mainspec/lua-resty-t1k-main-0-0.rockspec b/sdk/lua-resty-t1k/mainspec/lua-resty-t1k-main-0-0.rockspec
new file mode 100644
index 0000000..5b9b790
--- /dev/null
+++ b/sdk/lua-resty-t1k/mainspec/lua-resty-t1k-main-0-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k-main"
+version = "0-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ branch = "main",
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.0-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.0-0.rockspec
new file mode 100644
index 0000000..297b403
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.0-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.0.0-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.0.0"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.1-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.1-0.rockspec
new file mode 100644
index 0000000..a83373a
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.1-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.0.1-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.0.1"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.2-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.2-0.rockspec
new file mode 100644
index 0000000..8b6327e
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.2-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.0.2-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.0.2"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.3-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.3-0.rockspec
new file mode 100644
index 0000000..1d264f7
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.0.3-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.0.3-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.0.3"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.0-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.0-0.rockspec
new file mode 100644
index 0000000..240caa4
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.0-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.1.0-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.1.0"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.1-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.1-0.rockspec
new file mode 100644
index 0000000..bea43b6
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.1-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.1.1-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.1.1"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.2-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.2-0.rockspec
new file mode 100644
index 0000000..3a3fe5c
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.2-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.1.2-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.1.2"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.3-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.3-0.rockspec
new file mode 100644
index 0000000..acac469
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.3-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.1.3-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.1.3"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.4-0.rockspec b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.4-0.rockspec
new file mode 100644
index 0000000..73899fe
--- /dev/null
+++ b/sdk/lua-resty-t1k/rockspec/lua-resty-t1k-1.1.4-0.rockspec
@@ -0,0 +1,32 @@
+package = "lua-resty-t1k"
+version = "1.1.4-0"
+source = {
+ url = "git://github.com/chaitin/lua-resty-t1k",
+ tag = "v1.1.4"
+}
+
+description = {
+ summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
+ detailed = [[
+ Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
+ ]],
+ homepage = "https://github.com/chaitin/lua-resty-t1k",
+ license = "Apache License 2.0",
+ maintainer = "Xudong Wang "
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["resty.t1k"] = "lib/resty/t1k.lua",
+ ["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
+ ["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
+ ["resty.t1k.file"] = "lib/resty/t1k/file.lua",
+ ["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
+ ["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
+ ["resty.t1k.log"] = "lib/resty/t1k/log.lua",
+ ["resty.t1k.request"] = "lib/resty/t1k/request.lua",
+ ["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
+ ["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
+ },
+}
diff --git a/sdk/lua-resty-t1k/t/buffer.t b/sdk/lua-resty-t1k/t/buffer.t
new file mode 100644
index 0000000..a08a17a
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/buffer.t
@@ -0,0 +1,36 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: buffer add
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local buffer = require "resty.t1k.buffer"
+ local b = buffer:new()
+ b:add("hello")
+ b:add(" ")
+ b:add("world")
+ b:add("!")
+ ngx.say(b[1], b[2], b[3], b[4])
+ ngx.say(b:len())
+ }
+ }
+--- request
+GET /t
+--- response_body
+hello world!
+12
+--- no_error_log
+[error]
diff --git a/sdk/lua-resty-t1k/t/file.t b/sdk/lua-resty-t1k/t/file.t
new file mode 100644
index 0000000..c6b8275
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/file.t
@@ -0,0 +1,136 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: read full file
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local file = require "resty.t1k.file"
+ local path = ngx.var.document_root .. "/foo.bar"
+ local ok, err, content = file.read(path, 2 ^ 15)
+ if not ok then
+ ngx.say(err)
+ end
+ ngx.print(table.concat(content))
+ }
+ }
+--- user_files eval
+[
+ ["foo.bar" => "a" x (2 ** 14) ],
+]
+--- request
+GET /t
+--- response_body eval
+"a" x (2 ** 14)
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: read partial file
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local file = require "resty.t1k.file"
+ local path = ngx.var.document_root .. "/foo.bar"
+ local ok, err, content = file.read(path, 2 ^ 13)
+ if not ok then
+ ngx.say(err)
+ end
+ ngx.print(table.concat(content))
+ }
+ }
+--- user_files eval
+[
+ ["foo.bar" => "a" x (2 ** 14) ],
+]
+--- request
+GET /t
+--- response_body eval
+"a" x (2 ** 13)
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: read negative bytes
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local file = require "resty.t1k.file"
+ local path = ngx.var.document_root .. "/foo.bar"
+ local ok, err, content = file.read(path, -1)
+ if not ok then
+ ngx.say(err)
+ end
+ ngx.print(table.concat(content))
+ }
+ }
+--- user_files
+>>> foo.bar
+--- request
+GET /t
+--- response_body eval
+""
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: read empty file
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local file = require "resty.t1k.file"
+ local path = ngx.var.document_root .. "/foo.bar"
+ local ok, err, content = file.read(path, 1)
+ if not ok then
+ ngx.say(err)
+ end
+ ngx.print(table.concat(content))
+ }
+ }
+--- user_files
+>>> foo.bar
+--- request
+GET /t
+--- response_body eval
+""
+--- no_error_log
+[error]
+
+
+
+=== TEST 5: read non-existent file
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local file = require "resty.t1k.file"
+ local ok, err, buffer = file.read("/opt/non_existent_file", 0)
+ if not ok then
+ ngx.say(err)
+ end
+ }
+ }
+--- request
+GET /t
+--- response_body
+/opt/non_existent_file: No such file or directory
+--- no_error_log
+[error]
diff --git a/sdk/lua-resty-t1k/t/filter.t b/sdk/lua-resty-t1k/t/filter.t
new file mode 100644
index 0000000..8098cc6
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/filter.t
@@ -0,0 +1,39 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 5);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: do_header_filter
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ ngx.ctx.t1k_extra_header = "k1:v1\nk2:v2\nk3:v3\n"
+ }
+
+ header_filter_by_lua_block {
+ local filter = require "resty.t1k.filter"
+ filter.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("hi")
+ }
+ }
+--- request
+GET /t
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- no_error_log
+[error]
diff --git a/sdk/lua-resty-t1k/t/handler.t b/sdk/lua-resty-t1k/t/handler.t
new file mode 100644
index 0000000..df83223
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/handler.t
@@ -0,0 +1,138 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: handle passed action
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local handler = require "resty.t1k.handler"
+
+ local t = {
+ action = ".",
+ }
+
+ local ok, err = handler.handle(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: handle blocked action
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local handler = require "resty.t1k.handler"
+
+ local t = {
+ action = "?",
+ status = 405,
+ event_id = "c0c039a7c348486eaffd9e2f9846b66b",
+ }
+
+ local ok, err = handler.handle(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local filter = require "resty.t1k.filter"
+ filter.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+{"code": 405, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "c0c039a7c348486eaffd9e2f9846b66b"}
+--- error_code eval
+"405"
+--- no_error_log
+[error]
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: handle unknown action
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local handler = require "resty.t1k.handler"
+
+ local t = {
+ action = "~"
+ }
+
+ local ok, err = handler.handle(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- error_log
+lua-resty-t1k: unknown action from t1k server: ~
+
+
+
+=== TEST 4: handle nil result
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local handler = require "resty.t1k.handler"
+
+ local ok, err = handler.handle(nil)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- error_log
+lua-resty-t1k: invalid result type: nil
diff --git a/sdk/lua-resty-t1k/t/integration.t b/sdk/lua-resty-t1k/t/integration.t
new file mode 100644
index 0000000..77110ca
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/integration.t
@@ -0,0 +1,1142 @@
+use Test::Nginx::Socket 'no_plan';
+
+our $MainConfig = <<'_EOC_';
+env DETECTOR_IP;
+_EOC_
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+ lua_socket_log_errors off;
+_EOC_
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: integration test blocked
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_headers
+Content-Type: application/json
+--- response_body_like eval
+'^{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": ".*"}$'
+--- error_code: 403
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 2: integration test blocked internal handle
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_headers
+Content-Type: application/json
+--- response_body_like eval
+'^{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": ".*"}$'
+--- error_code: 403
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 3: integration test blocked http2
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- http2
+--- request
+GET /t/shell.php
+--- response_headers
+Content-Type: application/json
+--- response_body_like eval
+'^{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": ".*"}$'
+--- error_code: 403
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 4: integration test blocked http2 internal handle
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- http2
+--- request
+GET /t/shell.php
+--- response_headers
+Content-Type: application/json
+--- response_body_like eval
+'^{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": ".*"}$'
+--- error_code: 403
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 5: integration test monitor
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 6: integration test monitor internal handle
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 7: integration test monitor http2
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- http2
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log eval
+["lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000", "skip blocking"]
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 8: integration test monitor http2 internal handle
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- http2
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log eval
+"lua-resty-t1k: successfully connected to t1k server $ENV{DETECTOR_IP}:8000"
+--- log_level: debug
+--- skip_eval
+4: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 9: integration test disabled
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "off",
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: t1k is not enabled
+skip blocking
+--- log_level: debug
+
+
+
+=== TEST 10: integration test configuration priority
+--- main_config eval: $::MainConfig
+--- http_config eval: $::HttpConfig
+--- config
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = os.getenv("DETECTOR_IP"),
+ port = 8000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ location /pass {
+ access_by_lua_block {
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+
+ location /block {
+ content_by_lua_block {
+ ngx.say("there must be a problem when you see this line")
+ }
+ }
+--- request eval
+["GET /pass/shell.php", "GET /block/shell.php"]
+--- response_body_like eval
+["passed", '^{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": ".*"}$']
+--- error_code eval
+[200, 403]
+--- no_error_log
+[error]
+--- skip_eval
+6: not exists($ENV{DETECTOR_IP})
+
+
+
+=== TEST 11: integration test blocked extra headers
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\x23\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- response_headers
+Content-Type: application/json
+--- response_body
+{"code": 405, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "c0c039a7c348486eaffd9e2f9846b66b"}
+--- error_code eval
+"405"
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 12: integration test blocked extra headers internal handle
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\x23\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- response_headers
+Content-Type: application/json
+--- response_body
+{"code": 405, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "c0c039a7c348486eaffd9e2f9846b66b"}
+--- error_code eval
+"405"
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 13: integration test blocked extra headers with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\x23\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- response_headers
+Content-Type: application/json
+--- response_body
+{"code": 405, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "c0c039a7c348486eaffd9e2f9846b66b"}
+--- error_code eval
+"405"
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 14: integration test passed extra headers
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00.\xa3\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a"
+--- request
+GET /t
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 15: integration test passed extra headers internal handle
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00.\xa3\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a"
+--- request
+GET /t
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 16: integration test passed extra headers with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00.\xa3\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a"
+--- request
+GET /t
+--- response_headers
+k1: v1
+k2: v2
+k3: v3
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 17: integration test monitor extra headers
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\x23\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- raw_response_headers_unlike eval
+'.*k1: v1\r\n.*'
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+skip blocking
+--- log_level: debug
+
+
+
+=== TEST 18: integration test monitor extra headers internal handle
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t, true)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\x23\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- raw_response_headers_unlike eval
+'.*k1: v1\r\n.*'
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 19: integration test monitor extra headers with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "monitor",
+ host = "unix:t1k.sock",
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if t.mode ~= "block" then
+ ngx.log(ngx.DEBUG, "skip blocking")
+ return
+ end
+
+ ok, err = t1k.do_handle(result)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ header_filter_by_lua_block {
+ local t1k = require "resty.t1k"
+ t1k.do_header_filter()
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\x23\x12\x00\x00\x00k1:v1\x0ak2:v2\x0ak3:v3\x0a\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- raw_response_headers_unlike eval
+'.*k1: v1\r\n.*'
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+skip blocking
+--- log_level: debug
diff --git a/sdk/lua-resty-t1k/t/log.t b/sdk/lua-resty-t1k/t/log.t
new file mode 100644
index 0000000..8e5853a
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/log.t
@@ -0,0 +1,67 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 2 + 2);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: err_fmt
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local log = require "resty.t1k.log"
+ ngx.log(log.err_fmt("%s - %04d - %.4f", "test", 1, 1))
+ }
+ }
+--- request
+GET /t
+[error]
+--- error_log
+lua-resty-t1k: test - 0001 - 1.0000
+--- log_level: error
+
+
+
+=== TEST 2: warn_fmt
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local log = require "resty.t1k.log"
+ ngx.log(log.warn_fmt("%s - %04d - %.4f", "test", 1, 1))
+ }
+ }
+--- request
+GET /t
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: test - 0001 - 1.0000
+--- log_level: warn
+
+
+
+=== TEST 3: debug_fmt
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local log = require "resty.t1k.log"
+ ngx.log(log.debug_fmt("%s - %04d - %.4f", "test", 1, 1))
+ }
+ }
+--- request
+GET /t
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: test - 0001 - 1.0000
+--- log_level: debug
diff --git a/sdk/lua-resty-t1k/t/option.t b/sdk/lua-resty-t1k/t/option.t
new file mode 100644
index 0000000..b10e53d
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/option.t
@@ -0,0 +1,169 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3 + 1);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: do_access nil option
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local ok, err, _ = t1k.do_access(nil)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[debug]
+--- log_level: debug
+
+
+
+=== TEST 2: do_access disabled
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "off",
+ }
+
+ local ok, err, _ = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: t1k is not enabled
+--- log_level: debug
+
+
+
+=== TEST 3: do_access invalid mode
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "invalid",
+ }
+
+ local ok, err, _ = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t/shell.php
+--- response_body
+passed
+--- error_log
+lua-resty-t1k: invalid t1k mode: invalid
+
+
+
+=== TEST 4: do_access invalid host
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block"
+ }
+
+ local ok, err, _ = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- error_log
+lua-resty-t1k: invalid t1k host: nil
+--- log_level: debug
+
+
+
+=== TEST 5: do_access invalid port
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local t1k = require "resty.t1k"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1"
+ }
+
+ local ok, err, _ = t1k.do_access(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ }
+
+ content_by_lua_block {
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- error_log
+lua-resty-t1k: invalid t1k port: nil
+--- log_level: debug
diff --git a/sdk/lua-resty-t1k/t/request.t b/sdk/lua-resty-t1k/t/request.t
new file mode 100644
index 0000000..55e8991
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/request.t
@@ -0,0 +1,640 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3 + 14);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: do_request blocked
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ ngx.say(result["status"])
+ ngx.say(result["event_id"])
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_body
+?
+405
+c0c039a7c348486eaffd9e2f9846b66b
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 2: do_request blocked with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ ngx.say(result["status"])
+ ngx.say(result["event_id"])
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_body
+?
+405
+c0c039a7c348486eaffd9e2f9846b66b
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 3: do_request passed
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\xc1\x01\x00\x00\x00."
+--- request
+GET /t
+--- response_body
+.
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 4: do_request passed with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\xc1\x01\x00\x00\x00."
+--- request
+GET /t
+--- response_body
+.
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 5: do_request trim request body
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 0.0625,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\xc1\x01\x00\x00\x00."
+--- request
+GET /t
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+--- response_body
+.
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: request body is too long: 123 bytes, cut to 64 bytes
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 6: do_request trim request body with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 0.0625,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\xc1\x01\x00\x00\x00."
+--- request
+GET /t
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+--- response_body
+.
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: request body is too long: 123 bytes, cut to 64 bytes
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 7: do_request refuse connection
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 0.0625,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say("result: ", result)
+ }
+ }
+--- request
+GET /t
+--- response_body
+result: nil
+--- error_log
+failed to connect to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 8: do_request refuse connection with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 0.0625,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say("result: ", result)
+ }
+ }
+--- request
+GET /t
+--- response_body
+result: nil
+--- error_log
+failed to connect to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 9: do_request timeout
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 100,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say("result: ", result)
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply_delay: 200ms
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_body
+result: nil
+--- error_log
+failed to receive info packet from t1k server 127.0.0.1:18000: timeout
+--- log_level: debug
+
+
+
+=== TEST 10: do_request timeout with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 100,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say("result: ", result)
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply_delay: 200ms
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00"
+--- request
+GET /t/shell.php
+--- response_body
+result: nil
+--- error_log
+failed to receive info packet from t1k server unix:t1k.sock: timeout
+--- log_level: debug
+
+
+
+=== TEST 11: do_request invalid action
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 0.0625,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say("action: ", result["action"])
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\xc1\x01\x00\x00\x00~"
+--- request
+GET /t
+--- response_body
+action: ~
+--- no_error_log
+[error]
+--- error_log
+successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 12: do_request invalid action with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 0.0625,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say("action: ", result["action"])
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\xc1\x01\x00\x00\x00~"
+--- request
+GET /t
+--- response_body
+action: ~
+--- no_error_log
+[error]
+--- error_log
+successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
+
+
+
+=== TEST 13: do_request remote address
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ access_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("ngx.var.http_x_real_ip or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_x_real_ip) or ngx.var.remote_addr)
+ ngx.say("ngx.var.http_x_forwarded_for: 2 or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 2) or ngx.var.remote_addr)
+ ngx.say("ngx.var.http_x_forwarded_for: -2 or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -2) or ngx.var.remote_addr)
+ ngx.say("ngx.var.http_non_existent_header or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_non_existent_header) or ngx.var.remote_addr)
+ }
+ }
+--- request
+GET /t
+--- more_headers
+X-Forwarded-For: 1.1.1.1, 2.2.2.2, 2001:db8:3333:4444:5555:6666:7777:8888, 3.3.3.3
+X-Real-IP: 100.100.100.100
+--- response_body
+ngx.var.http_x_real_ip or ngx.var.remote_addr is 100.100.100.100
+ngx.var.http_x_forwarded_for: 2 or ngx.var.remote_addr is 2.2.2.2
+ngx.var.http_x_forwarded_for: -2 or ngx.var.remote_addr is 2001:db8:3333:4444:5555:6666:7777:8888
+ngx.var.http_non_existent_header or ngx.var.remote_addr is 127.0.0.1
+--- no_error_log
+[error]
+
+
+
+=== TEST 14: do_request http2
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "127.0.0.1",
+ port = 18000,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ ngx.say(result["status"])
+ ngx.say(result["event_id"])
+ }
+ }
+--- tcp_listen: 18000
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00"
+--- http2
+--- request
+GET /t/shell.php
+--- tcp_query eval
+qr/.*HTTP\/2.0.*/
+--- response_body
+?
+405
+c0c039a7c348486eaffd9e2f9846b66b
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
+--- log_level: debug
+
+
+
+=== TEST 15: do_request http2 with unix domain socket
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local request = require "resty.t1k.request"
+
+ local t = {
+ mode = "block",
+ host = "unix:t1k.sock",
+ uds = true,
+ connect_timeout = 1000,
+ send_timeout = 1000,
+ read_timeout = 1000,
+ req_body_size = 1024,
+ keepalive_size = 16,
+ keepalive_timeout = 10000,
+ }
+
+ local ok, err, result = request.do_request(t)
+ if not ok then
+ ngx.log(ngx.ERR, err)
+ end
+
+ ngx.say(result["action"])
+ ngx.say(result["status"])
+ ngx.say(result["event_id"])
+ }
+ }
+--- tcp_listen: t1k.sock
+--- tcp_reply eval
+"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00"
+--- http2
+--- request
+GET /t/shell.php
+--- tcp_query eval
+qr/.*HTTP\/2.0.*/
+--- response_body
+?
+405
+c0c039a7c348486eaffd9e2f9846b66b
+--- no_error_log
+[error]
+--- error_log
+lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
+--- log_level: debug
diff --git a/sdk/lua-resty-t1k/t/utils.t b/sdk/lua-resty-t1k/t/utils.t
new file mode 100644
index 0000000..50fb3f3
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/utils.t
@@ -0,0 +1,264 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: int_to_char_length
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("255 to char length: ", utils.int_to_char_length(255))
+ ngx.print("16777216 to char length: ", utils.int_to_char_length(16777216))
+ }
+ }
+--- request
+GET /t
+--- response_body eval
+"255 to char length: \x{ff}\x{00}\x{00}\x{00}
+16777216 to char length: \x{00}\x{00}\x{00}\x{01}"
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: int_to_char_length
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("0xff 0x00 0x00 0x00 to int length: ", utils.char_to_int_length("\xff\x00\x00\x00"))
+ ngx.say("0x00 0x00 0x00 0x01 to int length: ", utils.char_to_int_length("\x00\x00\x00\x01"))
+ }
+ }
+--- request
+GET /t
+--- response_body
+0xff 0x00 0x00 0x00 to int length: 255
+0x00 0x00 0x00 0x01 to int length: 16777216
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: is_mask_first
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("0x30 is MASK FIRST: ", utils.is_mask_first(0x30))
+ ngx.say("0x41 is MASK FIRST: ", utils.is_mask_first(0x41))
+ ngx.say("0xc0 is MASK FIRST: ", utils.is_mask_first(0xc0))
+ ngx.say("0xc1 is MASK FIRST: ", utils.is_mask_first(0xc1))
+ }
+ }
+--- request
+GET /t
+--- response_body
+0x30 is MASK FIRST: false
+0x41 is MASK FIRST: true
+0xc0 is MASK FIRST: true
+0xc1 is MASK FIRST: true
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: is_mask_last
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("0x41 is MASK LAST: ", utils.is_mask_last(65))
+ ngx.say("0x80 is MASK LAST: ", utils.is_mask_last(128))
+ }
+ }
+--- request
+GET /t
+--- response_body
+0x41 is MASK LAST: false
+0x80 is MASK LAST: true
+--- no_error_log
+[error]
+
+
+
+=== TEST 5: packet_parser unfinished
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ local finished, tag, length = utils.packet_parser("\x41\x59\x00\x00\x00")
+ ngx.say("finished: ", finished)
+ ngx.say("tag == TAG_HEAD: ", tag == 1)
+ ngx.say("length: ", 89)
+ }
+ }
+--- request
+GET /t
+--- response_body
+finished: false
+tag == TAG_HEAD: true
+length: 89
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: packet_parser finished
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ local finished, tag, length = utils.packet_parser("\xa0\x08\x00\x00\x00")
+ ngx.say("finished: ", finished)
+ ngx.say("tag == TAG_VERSION: ", tag == 32)
+ ngx.say("length: ", 8)
+ }
+ }
+--- request
+GET /t
+--- response_body
+finished: true
+tag == TAG_VERSION: true
+length: 8
+--- no_error_log
+[error]
+
+
+
+=== TEST 7: starts_with
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("http://www.baidu.com starts with \"http\": ", utils.starts_with("http://www.baidu.com", "http"))
+ ngx.say("http://www.baidu.com starts with \"https\": ", utils.starts_with("http://www.baidu.com", "https"))
+ }
+ }
+--- request
+GET /t
+--- response_body
+http://www.baidu.com starts with "http": true
+http://www.baidu.com starts with "https": false
+--- no_error_log
+[error]
+
+
+
+=== TEST 8: to_var_idx
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ local fmt = string.format
+ ngx.say(fmt("http_x_real_ip is %s and %s", utils.to_var_idx("http_x_real_ip")))
+ ngx.say(fmt("X_REAL_IP is %s and %s", utils.to_var_idx("X_REAL_IP")))
+ ngx.say(fmt("X-Forwarded-For: 1 is %s and %d", utils.to_var_idx("X-Forwarded-For: 1")))
+ ngx.say(fmt("X-Forwarded-For: -1 is %s and %d", utils.to_var_idx("X-Forwarded-For: -1")))
+ ngx.say(fmt("X-FORWARDED-FOR:100 is %s and %d", utils.to_var_idx("X-FORWARDED-FOR:-100")))
+ }
+ }
+--- request
+GET /t
+--- response_body
+http_x_real_ip is http_x_real_ip and nil
+X_REAL_IP is http_x_real_ip and nil
+X-Forwarded-For: 1 is http_x_forwarded_for and 1
+X-Forwarded-For: -1 is http_x_forwarded_for and -1
+X-FORWARDED-FOR:100 is http_x_forwarded_for and -100
+--- no_error_log
+[error]
+
+
+
+=== TEST 9: get_indexed_element
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say("X-Forwarded-For: 1 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 1))
+ ngx.say("X-Forwarded-For: 2 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 2))
+ ngx.say("X-Forwarded-For: 3 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 3))
+ ngx.say("X-Forwarded-For: 4 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 4))
+ ngx.say("X-Forwarded-For: 5 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 5))
+ ngx.say("X-Forwarded-For: -1 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -1))
+ ngx.say("X-Forwarded-For: -2 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -2))
+ ngx.say("X-Forwarded-For: -3 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -3))
+ ngx.say("X-Forwarded-For: -4 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -4))
+ ngx.say("X-Forwarded-For: -5 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -5))
+ ngx.say("X-Forwarded-For: 0 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 0))
+ ngx.say("X-Non-Existent-Header is ", utils.get_indexed_element(ngx.var.http_non_existent_header))
+ ngx.say("X-Real-IP is ", utils.get_indexed_element(ngx.var.http_x_real_ip))
+ ngx.say("X-Real-IP: 1 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, 1))
+ ngx.say("X-Real-IP: 2 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, 2))
+ ngx.say("X-Real-IP: -1 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, -1))
+ ngx.say("X-Real-IP: -2 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, -2))
+ }
+ }
+--- request
+GET /t
+--- more_headers
+X-Forwarded-For: 1.1.1.1, 2.2.2.2
+X-Forwarded-For: 3.3.3.3, 4.4.4.4
+X-Real-IP: 10.10.10.10
+--- response_body
+X-Forwarded-For: 1 is 1.1.1.1
+X-Forwarded-For: 2 is 2.2.2.2
+X-Forwarded-For: 3 is 3.3.3.3
+X-Forwarded-For: 4 is 4.4.4.4
+X-Forwarded-For: 5 is nil
+X-Forwarded-For: -1 is 4.4.4.4
+X-Forwarded-For: -2 is 3.3.3.3
+X-Forwarded-For: -3 is 2.2.2.2
+X-Forwarded-For: -4 is 1.1.1.1
+X-Forwarded-For: -5 is nil
+X-Forwarded-For: 0 is 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4
+X-Non-Existent-Header is nil
+X-Real-IP is 10.10.10.10
+X-Real-IP: 1 is 10.10.10.10
+X-Real-IP: 2 is nil
+X-Real-IP: -1 is 10.10.10.10
+X-Real-IP: -2 is nil
+--- no_error_log
+[error]
+
+
+
+=== TEST 10: get_event_id
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local utils = require "resty.t1k.utils"
+ ngx.say(utils.get_event_id(""))
+ ngx.say(utils.get_event_id(""))
+ ngx.say(utils.get_event_id(""))
+ }
+ }
+--- request
+GET /t
+--- response_body
+0988987de04844c7a3ce6d27865c9513
+8bae6adf33864c7f8bf715a9b7a65b2c
+nil
+--- no_error_log
+[error]
diff --git a/sdk/lua-resty-t1k/t/uuid.t b/sdk/lua-resty-t1k/t/uuid.t
new file mode 100644
index 0000000..f1060b5
--- /dev/null
+++ b/sdk/lua-resty-t1k/t/uuid.t
@@ -0,0 +1,29 @@
+use Test::Nginx::Socket;
+
+our $HttpConfig = <<'_EOC_';
+ lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
+_EOC_
+
+repeat_each(3);
+
+plan tests => repeat_each() * (blocks() * 3);
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: generate_v4
+--- http_config eval: $::HttpConfig
+--- config
+ location /t {
+ content_by_lua_block {
+ local uuid = require "resty.t1k.uuid"
+ ngx.say(uuid.generate_v4())
+ }
+ }
+--- request
+GET /t
+--- response_body_like
+^[0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}$
+--- no_error_log
+[error]