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 + +[![LuaRocks](https://img.shields.io/luarocks/v/blaisewang/lua-resty-t1k?style=flat-square)](https://luarocks.org/modules/blaisewang/lua-resty-t1k) +[![Releases](https://img.shields.io/github/v/release/chaitin/lua-resty-t1k?style=flat-square)](https://github.com/chaitin/lua-resty-t1k/releases) +[![License](https://img.shields.io/github/license/chaitin/lua-resty-t1k?color=ff69b4&style=flat-square)](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. + +[![Test](https://img.shields.io/github/actions/workflow/status/chaitin/lua-resty-t1k/test.yml?logo=github&style=flat-square)](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]