├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── ignore_words │ ├── license-checker.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .licenserc.yaml ├── .luacheckrc ├── LICENSE ├── Makefile ├── README.md ├── config ├── gen_wasm_host_api.py ├── go.mod ├── go.sum ├── install-wasmedge.sh ├── install-wasmtime.sh ├── lib └── resty │ └── proxy-wasm.lua ├── proxy_wasm_abi.md ├── src ├── http │ ├── ngx_http_wasm_api.c │ ├── ngx_http_wasm_api.h │ ├── ngx_http_wasm_api_def.h │ ├── ngx_http_wasm_api_wasmedge.h │ ├── ngx_http_wasm_api_wasmtime.h │ ├── ngx_http_wasm_call.c │ ├── ngx_http_wasm_call.h │ ├── ngx_http_wasm_ctx.h │ ├── ngx_http_wasm_module.c │ ├── ngx_http_wasm_module.h │ ├── ngx_http_wasm_state.c │ └── ngx_http_wasm_state.h ├── proxy_wasm │ ├── proxy_wasm_map.c │ ├── proxy_wasm_map.h │ ├── proxy_wasm_memory.c │ ├── proxy_wasm_memory.h │ └── proxy_wasm_types.h └── vm │ ├── vm.c │ ├── vm.h │ ├── wasmedge.c │ └── wasmtime.c ├── t ├── WASM.pm ├── assemblyscript.t ├── certs │ ├── test.crt │ └── test.key ├── http_call.t ├── http_call_callback.t ├── http_call_multi.t ├── http_lifecycle.t ├── http_req_body.t ├── http_req_header.t ├── http_resp_body.t ├── http_resp_header.t ├── http_send_response.t ├── hup.t ├── log.t ├── metric.t ├── plugin_lifecycle.t ├── property.t ├── rust │ ├── fault-injection.t │ └── http-call.t └── testdata │ ├── README.md │ ├── assemblyscript │ ├── assembly │ │ ├── index.ts │ │ └── tsconfig.json │ ├── index.js │ ├── package-lock.json │ └── package.json │ ├── http_body │ └── main.go │ ├── http_call │ └── main.go │ ├── http_header │ └── main.go │ ├── http_lifecycle │ └── main.go │ ├── http_send_response │ └── main.go │ ├── log │ └── main.go │ ├── metric │ └── main.go │ ├── plugin_lifecycle │ └── main.go │ ├── property │ ├── get.go │ └── set.go │ └── rust │ ├── fault-injection │ ├── Cargo.lock │ ├── Cargo.toml │ └── fault_injection.rs │ └── http-call │ ├── Cargo.lock │ ├── Cargo.toml │ └── http_call.rs └── utils └── check-test-code-style.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | root = true 16 | 17 | [*] 18 | charset = utf-8 19 | end_of_line = lf 20 | indent_style = space 21 | ; the C code's indentation depends on the syntax 22 | ;indent_size = 4 23 | insert_final_newline = true 24 | trim_trailing_whitespace = true 25 | 26 | [Makefile] 27 | indent_style = tab 28 | 29 | [*.go] 30 | indent_style = tab 31 | block_comment_start = /* 32 | block_comment = * 33 | block_comment_end = */ 34 | 35 | [**/go.mod] 36 | indent_style = tab 37 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.t linguist-language=Text 2 | *.pm linguist-language=Perl 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | -------------------------------------------------------------------------------- /.github/workflows/ignore_words: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/license-checker.yml: -------------------------------------------------------------------------------- 1 | name: License checker 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | check-license: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 3 13 | 14 | steps: 15 | - uses: actions/checkout@v3.4.0 16 | - name: Check License Header 17 | uses: apache/skywalking-eyes@v0.4.0 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | code-lint: 11 | runs-on: "ubuntu-20.04" 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v3.4.0 15 | 16 | - name: Install luacheck 17 | run: | 18 | sudo apt install -y luarocks 19 | sudo luarocks install luacheck > build.log 2>&1 || (cat build.log && exit 1) 20 | 21 | - name: Setup Nodejs env 22 | uses: actions/setup-node@v3.6.0 23 | with: 24 | node-version: '12' 25 | 26 | - name: Install eclint 27 | run: | 28 | sudo npm install -g eclint 29 | 30 | - name: Script 31 | run: | 32 | make lint 33 | 34 | misc-lint: 35 | runs-on: "ubuntu-20.04" 36 | steps: 37 | - name: Check out code 38 | uses: actions/checkout@v3.4.0 39 | 40 | - name: Spell check 41 | run: | 42 | pip install codespell==2.1.0 43 | git grep --cached -l '' | xargs codespell --ignore-words=.github/workflows/ignore_words 44 | 45 | - name: Merge conflict 46 | run: | 47 | grep "^<<<<<<< HEAD" $(git grep --cached -l '' | xargs) && exit 1 || true 48 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | vm: 15 | - wasmedge 16 | - wasmtime 17 | runs-on: "ubuntu-20.04" 18 | env: 19 | OPENRESTY_PREFIX: "/usr/local/openresty" 20 | 21 | steps: 22 | - name: Check out code 23 | uses: actions/checkout@v3.4.0 24 | 25 | - name: Set up Clang 26 | uses: egor-tensin/setup-clang@v1 27 | 28 | - name: Set up Node.js 29 | uses: actions/setup-node@v3.6.0 30 | with: 31 | node-version: 10.0.0 32 | 33 | - name: Set up Golang 34 | uses: actions/setup-go@v3 35 | with: 36 | go-version: "1.17.5" 37 | 38 | - name: Install rust 39 | uses: actions-rs/toolchain@v1 40 | with: 41 | toolchain: stable 42 | target: wasm32-wasi 43 | default: true 44 | override: true 45 | 46 | - name: Get dependencies 47 | run: | 48 | sudo apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl luarocks libubsan0 49 | sudo luarocks install lua-resty-http > build.log 2>&1 || (cat build.log && exit 1) 50 | 51 | wget https://github.com/tinygo-org/tinygo/releases/download/v0.22.0/tinygo_0.22.0_amd64.deb 52 | sudo dpkg -i tinygo_0.22.0_amd64.deb 53 | 54 | - name: Before install 55 | run: | 56 | sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1) 57 | git clone https://github.com/iresty/test-nginx.git test-nginx 58 | 59 | - name: Install WasmEdge 60 | if: matrix.vm == 'wasmedge' 61 | run: | 62 | ./install-wasmedge.sh 63 | 64 | - name: Install 65 | run: | 66 | wget https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-base.sh 67 | chmod +x build-apisix-base.sh 68 | OR_PREFIX=$OPENRESTY_PREFIX CC="clang -fsanitize=address -fcolor-diagnostics -Qunused-arguments" \ 69 | cc_opt="-Werror -fsanitize=undefined" \ 70 | ld_opt="-lubsan -Wl,-rpath,$OPENRESTY_PREFIX/wasmedge/lib" \ 71 | ./build-apisix-base.sh latest 72 | 73 | - name: Script 74 | run: | 75 | sudo chown -R runner:root ~/.cache 76 | make build.all.testdata 77 | export PATH=$OPENRESTY_PREFIX/nginx/sbin:$PATH 78 | WASM_VM=${{ matrix.vm }} prove -I. -Itest-nginx/lib -r t/ 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ccls 2 | 3 | wasmtime-*-c-api 4 | wasmtime-*-c-api.tar.xz 5 | wasmtime-c-api 6 | wasmedge/ 7 | *.wasm 8 | *.wasm.map 9 | *.wat 10 | 11 | t/servroot 12 | node_modules/ 13 | t/testdata/rust/*/target 14 | utils/reindex 15 | .idea/ 16 | -------------------------------------------------------------------------------- /.licenserc.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | header: 16 | license: 17 | spdx-id: Apache-2.0 18 | copyright-owner: Shenzhen ZhiLiu Technology Co., Ltd. 19 | 20 | paths-ignore: 21 | - '.gitignore' 22 | - '.gitattributes' 23 | - 'LICENSE' 24 | - '.github/' 25 | # Exclude test files 26 | - 'go.mod' 27 | - 'go.sum' 28 | - 't/certs/' 29 | - 't/testdata/assemblyscript/node_modules/' 30 | - 't/testdata/assemblyscript/build/' 31 | - '**/Cargo.lock' 32 | - '**/*.json' 33 | # Exclude dev files 34 | - '.ccls-cache/' 35 | 36 | comment: on-failure 37 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | -- Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | -- 15 | ignore = { 16 | '_', 17 | "311", 18 | "511", 19 | "512", 20 | "542", 21 | "6..", 22 | } 23 | std = 'ngx_lua' 24 | unused_args = false 25 | redefined = false 26 | read_globals = { 27 | "coroutine._yield" 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021- Shenzhen ZhiLiu Technology Co., Ltd. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | OPENRESTY_PREFIX ?= /usr/local/openresty 16 | INSTALL ?= install 17 | RUST_DIR = $(wildcard t/testdata/rust/*) 18 | 19 | .PHONY: install 20 | install: 21 | $(INSTALL) -m 664 lib/resty/*.lua $(OPENRESTY_PREFIX)/lualib/resty/ 22 | cp -r ./wasmtime-c-api $(OPENRESTY_PREFIX)/ 23 | if [ -d ./wasmedge ]; then cp -r ./wasmedge $(OPENRESTY_PREFIX)/; fi 24 | 25 | .PHONY: build.go.testdata 26 | build.go.testdata: 27 | @find ./t/testdata -type f -name "*.go" | xargs -Ip tinygo build -o p.wasm -scheduler=none -target=wasi p 28 | 29 | .PHONY: build.testdata 30 | build.testdata: 31 | @find ./t/testdata -type f -name "main.go" | grep ${name} | xargs -Ip tinygo build -o p.wasm -scheduler=none -target=wasi p 32 | 33 | .PHONY: build.assemblyscript.testdata 34 | build.assemblyscript.testdata: 35 | @cd ./t/testdata/assemblyscript && npm install && npm run asbuild 36 | 37 | .PHONY: build.rust.testdata 38 | build.rust.testdata: 39 | $(foreach DIR, $(RUST_DIR), \ 40 | cd $(DIR) && \ 41 | cargo build --target=wasm32-wasi && \ 42 | cd ../../../..; \ 43 | ) 44 | 45 | .PHONY: build.all.testdata 46 | build.all.testdata: build.go.testdata build.assemblyscript.testdata build.rust.testdata 47 | 48 | .PHONY: utils 49 | utils: 50 | ifeq ("$(wildcard utils/reindex)", "") 51 | wget -P utils https://raw.githubusercontent.com/iresty/openresty-devel-utils/master/reindex 52 | chmod a+x utils/reindex 53 | endif 54 | 55 | 56 | .PHONY: lint 57 | lint: utils 58 | luacheck . 59 | ./utils/check-test-code-style.sh 60 | git ls-files | xargs eclint check 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 17 | ## Status 18 | 19 | This library is under construction. See https://github.com/api7/wasm-nginx-module/issues/25 to know the progress. 20 | 21 | ## Description 22 | 23 | A Nginx module which tries to implement [proxy wasm ABI](https://github.com/proxy-wasm/spec) in Nginx. 24 | The [Wasm integration of Apache APISIX](https://github.com/apache/apisix/blob/master/docs/en/latest/wasm.md) is powered by this module. 25 | 26 | ## Install dependencies 27 | 28 | * Download the wasmtime C API package and rename it to `wasmtime-c-api/`, with the `./install-wasmtime.sh`. 29 | Remember to add the `wasmtime-c-api/lib` to the library search path when you build Nginx, for instance, 30 | 31 | ``` 32 | export wasmtime_prefix=/path/to/wasm-nginx-module/wasmtime-c-api 33 | ./configure ... \ 34 | --with-ld-opt="-Wl,-rpath,${wasmtime_prefix}/lib" \ 35 | ``` 36 | 37 | * Download WasmEdge with the `./install-wasmedge.sh`. 38 | Remember to add the `wasmedge/lib` to the library search path when you build Nginx, for instance, 39 | 40 | ``` 41 | export wasmedge_prefix=/path/to/wasm-nginx-module/wasmedge 42 | ./configure ... \ 43 | --with-ld-opt="-Wl,-rpath,${wasmedge_prefix}/lib" \ 44 | ``` 45 | 46 | ## Directives 47 | 48 | ### wasm_vm 49 | 50 | **syntax:** *wasm_vm wasmtime|wasmedge* 51 | 52 | **default:** - 53 | 54 | **context:** *http* 55 | 56 | Select the Wasm VM. Currently, only wasmtime and WasmEdge are supported. 57 | If the directive is not set, the Wasm VM won't be enabled. 58 | 59 | ## Methods 60 | 61 | **Remember to set the `wasm_vm` directive!** 62 | 63 | ### load 64 | 65 | `syntax: plugin, err = proxy_wasm.load(name, path)` 66 | 67 | Load a `.wasm` file from the filesystem and return a Wasm plugin. 68 | 69 | ```lua 70 | local plugin, err = proxy_wasm.load("plugin","t/testdata/plugin_lifecycle/main.go.wasm") 71 | ``` 72 | 73 | ### on_configure 74 | 75 | `syntax: plugin_ctx, err = proxy_wasm.on_configure(plugin, conf)` 76 | 77 | Create a plugin ctx with the given plugin and conf. 78 | 79 | ```lua 80 | local plugin, err = proxy_wasm.load("plugin","t/testdata/plugin_lifecycle/main.go.wasm") 81 | if not plugin then 82 | ngx.log(ngx.ERR, "failed to load wasm ", err) 83 | return 84 | end 85 | local ctx, err = wasm.on_configure(plugin, '{"body":512}') 86 | if not ctx then 87 | ngx.log(ngx.ERR, "failed to create plugin ctx ", err) 88 | return 89 | end 90 | ``` 91 | 92 | ### on_http_request_headers 93 | 94 | `syntax: ok, err = proxy_wasm.on_http_request_headers(plugin_ctx)` 95 | 96 | Run the HTTP request headers filter in the plugin of the given plugin ctx. 97 | 98 | ```lua 99 | local plugin, err = proxy_wasm.load("plugin","t/testdata/plugin_lifecycle/main.go.wasm") 100 | if not plugin then 101 | ngx.log(ngx.ERR, "failed to load wasm ", err) 102 | return 103 | end 104 | local ctx, err = wasm.on_configure(plugin, '{"body":512}') 105 | if not ctx then 106 | ngx.log(ngx.ERR, "failed to create plugin ctx ", err) 107 | return 108 | end 109 | assert(wasm.on_http_request_headers(ctx)) 110 | ``` 111 | 112 | ### on_http_request_body 113 | 114 | `syntax: ok, err = proxy_wasm.on_http_request_body(plugin_ctx, body, end_of_body)` 115 | 116 | Run the HTTP request body filter in the plugin of the given plugin ctx. 117 | 118 | ```lua 119 | local plugin, err = proxy_wasm.load("plugin","t/testdata/plugin_lifecycle/main.go.wasm") 120 | if not plugin then 121 | ngx.log(ngx.ERR, "failed to load wasm ", err) 122 | return 123 | end 124 | local ctx, err = wasm.on_configure(plugin, '{"body":512}') 125 | if not ctx then 126 | ngx.log(ngx.ERR, "failed to create plugin ctx ", err) 127 | return 128 | end 129 | -- get_body is a utility method to get the whole request body 130 | local body = request.get_body() 131 | -- if the body is not the whole request body, for example, it comes from 132 | -- lua-resty-upload, remember to set end_of_body to false 133 | assert(wasm.on_http_request_body(ctx, body, true)) 134 | ``` 135 | 136 | ### on_http_response_headers 137 | 138 | `syntax: ok, err = proxy_wasm.on_http_response_headers(plugin_ctx)` 139 | 140 | Run the HTTP response headers filter in the plugin of the given plugin ctx. 141 | 142 | ```lua 143 | local plugin, err = proxy_wasm.load("plugin","t/testdata/http_lifecycle/main.go.wasm") 144 | if not plugin then 145 | ngx.log(ngx.ERR, "failed to load wasm ", err) 146 | return 147 | end 148 | local ctx, err = wasm.on_configure(plugin, '{"body":512}') 149 | if not ctx then 150 | ngx.log(ngx.ERR, "failed to create plugin ctx ", err) 151 | return 152 | end 153 | assert(wasm.on_http_response_headers(ctx)) 154 | ``` 155 | 156 | ### on_http_response_body 157 | 158 | `syntax: ok, err = proxy_wasm.on_http_response_body(plugin_ctx)` 159 | 160 | Run the HTTP response body filter in the plugin of the given plugin ctx. 161 | This method need to be called in `body_filter_by_lua` phase and may be run 162 | multiple times. 163 | 164 | ```lua 165 | local plugin, err = proxy_wasm.load("plugin","t/testdata/http_lifecycle/main.go.wasm") 166 | if not plugin then 167 | ngx.log(ngx.ERR, "failed to load wasm ", err) 168 | return 169 | end 170 | local ctx, err = wasm.on_configure(plugin, '{"body":512}') 171 | if not ctx then 172 | ngx.log(ngx.ERR, "failed to create plugin ctx ", err) 173 | return 174 | end 175 | assert(wasm.on_http_response_body(ctx)) 176 | ``` 177 | 178 | ## proxy-wasm ABI 179 | 180 | Implemented proxy-wasm ABI can be found in [proxy_wasm_abi](./proxy_wasm_abi.md). 181 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | ngx_module_type=HTTP 16 | ngx_module_name=ngx_http_wasm_module 17 | ngx_module_srcs=" \ 18 | $ngx_addon_dir/src/http/ngx_http_wasm_module.c \ 19 | $ngx_addon_dir/src/http/ngx_http_wasm_api.c \ 20 | $ngx_addon_dir/src/http/ngx_http_wasm_call.c \ 21 | $ngx_addon_dir/src/http/ngx_http_wasm_state.c \ 22 | $ngx_addon_dir/src/proxy_wasm/proxy_wasm_map.c \ 23 | $ngx_addon_dir/src/proxy_wasm/proxy_wasm_memory.c \ 24 | $ngx_addon_dir/src/vm/wasmtime.c \ 25 | $ngx_addon_dir/src/vm/vm.c \ 26 | " 27 | ngx_module_deps=" \ 28 | $ngx_addon_dir/src/http/ngx_http_wasm_api_def.h \ 29 | $ngx_addon_dir/src/vm/vm.h \ 30 | " 31 | ngx_module_incs=" \ 32 | $ngx_addon_dir/src \ 33 | $ngx_addon_dir/wasmtime-c-api/include \ 34 | " 35 | ngx_module_libs=" \ 36 | -L$ngx_addon_dir/wasmtime-c-api/lib -lwasmtime \ 37 | " 38 | 39 | if [ -d $ngx_addon_dir/wasmedge ]; then 40 | # if wasmedge is installed 41 | wasmedge_path=$ngx_addon_dir/wasmedge 42 | ngx_module_srcs=" \ 43 | $ngx_module_srcs \ 44 | $ngx_addon_dir/src/vm/wasmedge.c \ 45 | " 46 | ngx_module_incs=" \ 47 | $ngx_module_incs \ 48 | $wasmedge_path/include \ 49 | " 50 | ngx_module_libs=" \ 51 | $ngx_module_libs \ 52 | -L$wasmedge_path/lib -lwasmedge_c \ 53 | " 54 | 55 | echo "Build with WasmEdge enabled" 56 | have=NGX_WASM_HAVE_WASMEDGE . auto/have 57 | fi 58 | 59 | . auto/module 60 | 61 | ngx_addon_name=$ngx_module_name 62 | -------------------------------------------------------------------------------- /gen_wasm_host_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | import os 17 | import re 18 | import sys 19 | from string import Template 20 | 21 | MAX_WASM_API_ARG = 12 22 | 23 | header = """/* 24 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 25 | * 26 | * Licensed under the Apache License, Version 2.0 (the "License"); 27 | * you may not use this file except in compliance with the License. 28 | * You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | * 38 | */ 39 | /* Code generated by `./gen_wasm_host_api.py src/http`. DO NOT EDIT. */""" 40 | 41 | api_def_tpl = """ 42 | $header 43 | #ifndef NGX_HTTP_WASM_API_DEF_H 44 | #define NGX_HTTP_WASM_API_DEF_H 45 | 46 | #include 47 | #include 48 | 49 | 50 | $host_api_def 51 | 52 | 53 | #endif 54 | """ 55 | 56 | tpl = """ 57 | $header 58 | #ifndef $vm_api_header_name 59 | #define $vm_api_header_name 60 | 61 | 62 | #include 63 | #include 64 | #include 65 | $vm_header 66 | #include "proxy_wasm/proxy_wasm_types.h" 67 | #include "http/ngx_http_wasm_api_def.h" 68 | 69 | 70 | #define MAX_WASM_API_ARG $max_wasm_api_arg 71 | 72 | $wasm_api_def 73 | 74 | 75 | #endif 76 | """ 77 | 78 | def generate_wasm_runtime_struct(vm): 79 | if vm == "wasmtime": 80 | return """ 81 | #define DEFINE_WASM_API(NAME, ARG_CHECK) \\ 82 | static wasm_trap_t* wasmtime_##NAME( \\ 83 | void *env, \\ 84 | wasmtime_caller_t *caller, \\ 85 | const wasmtime_val_t *args, \\ 86 | size_t nargs, \\ 87 | wasmtime_val_t *results, \\ 88 | size_t nresults \\ 89 | ) { \\ 90 | ARG_CHECK \\ 91 | results[0].kind = WASMTIME_I32; \\ 92 | results[0].of.i32 = res; \\ 93 | return NULL; \\ 94 | } 95 | #define DEFINE_WASM_NAME(NAME, ARG) \\ 96 | {ngx_string(#NAME), wasmtime_##NAME, ARG}, 97 | 98 | 99 | typedef struct { 100 | ngx_str_t name; 101 | wasmtime_func_callback_t cb; 102 | int8_t param_num; 103 | wasm_valkind_t param_type[MAX_WASM_API_ARG]; 104 | } ngx_wasm_wasmtime_host_api_t; 105 | 106 | """ 107 | elif vm == "wasmedge": 108 | return """ 109 | #define DEFINE_WASM_API(NAME, ARG_CHECK) \\ 110 | static WasmEdge_Result wasmedge_##NAME( \\ 111 | void *Data, \\ 112 | WasmEdge_MemoryInstanceContext *MemCxt, \\ 113 | const WasmEdge_Value *In, \\ 114 | WasmEdge_Value *Out \\ 115 | ) { \\ 116 | ARG_CHECK \\ 117 | Out[0] = WasmEdge_ValueGenI32(res); \\ 118 | return WasmEdge_Result_Success; \\ 119 | } 120 | #define DEFINE_WASM_NAME(NAME, ARG) \\ 121 | {ngx_string(#NAME), wasmedge_##NAME, ARG}, 122 | 123 | 124 | typedef struct { 125 | ngx_str_t name; 126 | WasmEdge_HostFunc_t cb; 127 | int8_t param_num; 128 | enum WasmEdge_ValType param_type[MAX_WASM_API_ARG]; 129 | } ngx_wasm_wasmedge_host_api_t; 130 | 131 | """ 132 | 133 | def predefined_macro(vm): 134 | vm_def = generate_wasm_runtime_struct(vm) 135 | 136 | for i in range(MAX_WASM_API_ARG + 1): 137 | if i == 0: 138 | void_def = """ 139 | #define DEFINE_WASM_NAME_ARG_VOID \\ 140 | 0, {} 141 | #define DEFINE_WASM_API_ARG_CHECK_VOID(NAME) \\ 142 | int32_t res = NAME(); 143 | """ 144 | vm_def += void_def 145 | else: 146 | param_s = "" 147 | if vm == "wasmtime": 148 | kind = "WASM_I32" 149 | else: 150 | kind = "WasmEdge_ValType_I32" 151 | for j in range(1, i + 1): 152 | if j % 5 == 1: 153 | param_s += " " 154 | param_s += kind + ", " 155 | if j % 5 == 0: 156 | param_s += " \\\n" 157 | vm_def += """ 158 | #define DEFINE_WASM_NAME_ARG_I32_%d \\ 159 | %d, { \\ 160 | %s}""" % (i, i, param_s) 161 | vm_def += """ 162 | #define DEFINE_WASM_API_ARG_CHECK_I32_%d(NAME) \\\n""" % (i) 163 | for j in range(i): 164 | if vm == "wasmtime": 165 | vm_def += " int32_t p%d = args[%d].of.i32; \\\n" % (j, j) 166 | elif vm == "wasmedge": 167 | vm_def += " int32_t p%d = WasmEdge_ValueGetI32(In[%d]); \\\n" % (j, j) 168 | param_s = ", ".join('p' + str(j) for j in range(i)) 169 | vm_def += " int32_t res = NAME(%s);\n" % (param_s) 170 | 171 | if vm == "wasmtime": 172 | vm_def += r''' 173 | #define DEFINE_WASM_NAME_ARG_I32_I64 \ 174 | 2, { \ 175 | WASM_I32, WASM_I64, } 176 | #define DEFINE_WASM_API_ARG_CHECK_I32_I64(NAME) \ 177 | int32_t p0 = args[0].of.i32; \ 178 | int64_t p1 = args[1].of.i64; \ 179 | int32_t res = NAME(p0, p1); 180 | ''' 181 | elif vm == "wasmedge": 182 | vm_def += r''' 183 | #define DEFINE_WASM_NAME_ARG_I32_I64 \ 184 | 2, { \ 185 | WasmEdge_ValType_I32, WasmEdge_ValType_I64, } 186 | #define DEFINE_WASM_API_ARG_CHECK_I32_I64(NAME) \ 187 | int32_t p0 = WasmEdge_ValueGetI32(In[0]); \ 188 | int64_t p1 = WasmEdge_ValueGetI64(In[1]); \ 189 | int32_t res = NAME(p0, p1); 190 | ''' 191 | 192 | return vm_def 193 | 194 | def get_host_apis(src_dir): 195 | apis = [] 196 | with open(os.path.join(src_dir, "ngx_http_wasm_api.c")) as f: 197 | matching = False 198 | api = "" 199 | name = "" 200 | for line in f: 201 | m = re.match(r"^(proxy_\w+)\(", line) 202 | if m is not None: 203 | matching = True 204 | name = m.group(1) 205 | elif line[0] == '{' and matching: 206 | matching = False 207 | api_list = api.split(',') 208 | n_param = len(api_list) 209 | if "(void)" in api: 210 | n_param -= 1 211 | apis.append({ 212 | "name": name, 213 | "n_param": n_param, 214 | "raw_func": api 215 | }) 216 | api = "" 217 | if matching: 218 | api += line.rstrip() 219 | return apis 220 | 221 | if __name__ == '__main__': 222 | host_api_def = "" 223 | api_def = "" 224 | name_def = "" 225 | for i in range(MAX_WASM_API_ARG + 1): 226 | if i == 0: 227 | host_api_def += "#define DEFINE_WASM_API_ARG_VOID void\n" 228 | else: 229 | param_suffix = "I32_%d" % i 230 | param_s = "" 231 | for j in range(1, i + 1): 232 | if j % 8 == 1: 233 | if j != 1: 234 | param_s += "," 235 | param_s += " \\\n " 236 | else: 237 | param_s += ", " 238 | param_s += "int32_t" 239 | host_api_def += "#define DEFINE_WASM_API_ARG_%s%s\n" % (param_suffix, param_s) 240 | 241 | host_api_def += """#define DEFINE_WASM_API_ARG_I32_I64 \\\n int32_t, int64_t""" 242 | 243 | host_api_def += "\n\n" 244 | 245 | src_dir = sys.argv[1] 246 | apis = get_host_apis(src_dir) 247 | 248 | for api in apis: 249 | name = api["name"] 250 | n_param = api["n_param"] 251 | if n_param == 0: 252 | param_suffix = "VOID" 253 | elif n_param == 2 and "int64_t" in api["raw_func"]: 254 | param_suffix = "I32_I64" 255 | else: 256 | param_suffix = "I32_%d" % n_param 257 | 258 | host_api_def += """int32_t %s(DEFINE_WASM_API_ARG_%s);\n""" % (name, param_suffix) 259 | api_def += """ 260 | DEFINE_WASM_API(%s, 261 | DEFINE_WASM_API_ARG_CHECK_%s(%s))""" % (name, param_suffix, name) 262 | 263 | name_def += " DEFINE_WASM_NAME(%s, DEFINE_WASM_NAME_ARG_%s)\n" % (name, param_suffix) 264 | 265 | name_def += " { ngx_null_string, NULL, 0, {} }\n};" 266 | 267 | s = Template(api_def_tpl) 268 | with open(os.path.join(src_dir, "ngx_http_wasm_api_def.h"), 'w') as f: 269 | f.write(s.substitute(header=header, host_api_def=host_api_def)) 270 | 271 | s = Template(tpl) 272 | 273 | wasmtime_def = predefined_macro("wasmtime") 274 | wasmtime_def += api_def + "\n\nstatic ngx_wasm_wasmtime_host_api_t host_apis[] = {\n" 275 | wasmtime_def += name_def 276 | with open(os.path.join(src_dir, "ngx_http_wasm_api_wasmtime.h"), 'w') as f: 277 | f.write(s.substitute( 278 | header=header, 279 | vm_header="#include ", 280 | vm_api_header_name="NGX_HTTP_WASM_API_WASMTIME_H", 281 | wasm_api_def=wasmtime_def, 282 | max_wasm_api_arg=MAX_WASM_API_ARG, 283 | )) 284 | 285 | wasmedge_def = predefined_macro("wasmedge") 286 | wasmedge_def += api_def + "\n\nstatic ngx_wasm_wasmedge_host_api_t host_apis[] = {\n" 287 | wasmedge_def += name_def 288 | with open(os.path.join(src_dir, "ngx_http_wasm_api_wasmedge.h"), 'w') as f: 289 | f.write(s.substitute( 290 | header=header, 291 | vm_header="#include ", 292 | vm_api_header_name="NGX_HTTP_WASM_API_WASMEDGE_H", 293 | wasm_api_def=wasmedge_def, 294 | max_wasm_api_arg=MAX_WASM_API_ARG, 295 | )) 296 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/api7/wasm-nginx-module 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/tetratelabs/proxy-wasm-go-sdk v0.16.0 7 | github.com/valyala/fastjson v1.6.3 // indirect 8 | ) 9 | 10 | //replace github.com/tetratelabs/proxy-wasm-go-sdk => ../proxy-wasm-go-sdk 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 7 | github.com/tetratelabs/proxy-wasm-go-sdk v0.14.1-0.20210819090022-1e4e69881a31 h1:V3GXN5nayOdIU3NypbxVegGFCVGm78qOA8Q7wkeudy8= 8 | github.com/tetratelabs/proxy-wasm-go-sdk v0.14.1-0.20210819090022-1e4e69881a31/go.mod h1:qZ+4i6e2wHlhnhgpH0VG4QFzqd2BEvQbQFU0npt2e2k= 9 | github.com/tetratelabs/proxy-wasm-go-sdk v0.15.0 h1:9ckJdaKHhWi1km9J5s7jMF/3GXnSbFkBeR9k9z5cPkY= 10 | github.com/tetratelabs/proxy-wasm-go-sdk v0.15.0/go.mod h1:8CxNZJ+9yDEvNnAog384fC8j1tKNF0tTZevGjOuY9ds= 11 | github.com/tetratelabs/proxy-wasm-go-sdk v0.16.0 h1:6xhDLV4DD2+q3Rs4CDh7cqo69rQ50XgCusv/58D44o4= 12 | github.com/tetratelabs/proxy-wasm-go-sdk v0.16.0/go.mod h1:8CxNZJ+9yDEvNnAog384fC8j1tKNF0tTZevGjOuY9ds= 13 | github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= 14 | github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= 15 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 16 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /install-wasmedge.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | set -euo pipefail -x 17 | 18 | if echo "int main(void) {}" | gcc -o /dev/null -v -x c - &> /dev/stdout| grep collect | tr -s " " "\012" | grep musl; then 19 | # skip if the libc is musl 20 | exit 0 21 | fi 22 | 23 | curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/0.10.0/utils/install.sh | bash -s -- -e none -p ./wasmedge -v 0.10.0 24 | -------------------------------------------------------------------------------- /install-wasmtime.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | set -euo pipefail -x 17 | arch=$(uname -m | tr '[:upper:]' '[:lower:]') 18 | os=$(uname -s | tr '[:upper:]' '[:lower:]') 19 | if [ "$os" = "darwin" ]; then 20 | os="macos" 21 | if [ "$arch" = "arm64" ]; then 22 | arch="aarch64" 23 | fi 24 | else 25 | os="linux" 26 | fi 27 | ARCH=$arch 28 | VER=v0.38.1 29 | wget https://github.com/bytecodealliance/wasmtime/releases/download/${VER}/wasmtime-${VER}-${ARCH}-${os}-c-api.tar.xz 30 | tar -xvf ./wasmtime-${VER}-${ARCH}-${os}-c-api.tar.xz > /dev/null 31 | if [ -d wasmtime-c-api ]; then 32 | rm -rf wasmtime-c-api 33 | fi 34 | mv wasmtime-${VER}-${ARCH}-${os}-c-api wasmtime-c-api 35 | if { echo "int main(void) {}" | gcc -o /dev/null -v -x c - &> /dev/stdout| grep collect | tr -s " " "\012" | grep musl; } \ 36 | || ( [[ -f /etc/redhat-release ]] && [[ "$arch" = "aarch64" ]] ); then 37 | # build from source code if the libc is musl or under centos aarch64 38 | if ! command -v cargo > /dev/null; then 39 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 40 | source "$HOME/.cargo/env" 41 | fi 42 | 43 | git clone https://github.com/bytecodealliance/wasmtime -b ${VER} --depth 1 \ 44 | && cd wasmtime \ 45 | && git submodule update --init \ 46 | && RUSTFLAGS="-C target-feature=-crt-static" \ 47 | cargo build --release --manifest-path crates/c-api/Cargo.toml \ 48 | && mv target/release/libwasmtime.* ../wasmtime-c-api/lib \ 49 | && cd .. 50 | fi 51 | -------------------------------------------------------------------------------- /proxy_wasm_abi.md: -------------------------------------------------------------------------------- 1 | 17 | # Proxy-Wasm ABI specification implemented in the wasm-nginx-module 18 | 19 | As this module is still WIP, this list will be changed in the future version. 20 | Due to the behavior of Nginx and the different Proxy-Wasm SDK, we doesn't 21 | strictly implement the Proxy-Wasm ABI. In this case, we will add a `Note: ` 22 | in the corresponding section. 23 | 24 | # Functions implemented in the Wasm module 25 | 26 | All functions implemented in the Wasm module, other than the integration and memory management 27 | functions, include context identifier (`context_id`) as the first parameter, which should be used to 28 | distinguish between different contexts. 29 | 30 | 31 | ## Integration 32 | 33 | ### `_start` 34 | 35 | * params: 36 | - none 37 | * returns: 38 | - none 39 | 40 | Start function which is called when the module is loaded and initialized. This can be used by SDKs 41 | to setup and/or initialize state, but no proxy_ functions can be used at that point yet. 42 | 43 | 44 | ## Memory management 45 | 46 | ### `proxy_on_memory_allocate` 47 | 48 | * params: 49 | - `i32 (size_t) memory_size` 50 | * returns: 51 | - `i32 (void*) allocated_ptr` 52 | 53 | Allocates memory using in-VM memory allocator and returns it to the host. 54 | We also implement `malloc` as an alias of this function. 55 | 56 | 57 | ## Module lifecycle 58 | 59 | ### `proxy_on_context_create` 60 | 61 | * params: 62 | - `i32 (uint32_t) context_id` 63 | - `i32 (uint32_t) parent_context_id` 64 | * returns: 65 | - none 66 | 67 | Called when the host environment creates a new root context (if `parent_context_id` is `0`) or a new 68 | per-stream context. 69 | 70 | 71 | ### `proxy_on_done` 72 | 73 | * params: 74 | - `i32 (uint32_t) context_id` 75 | * returns: 76 | - `i32 (bool) is_done` 77 | 78 | Called when the host environment is done processing the context (`context_id`). Return value 79 | indicates when the Wasm VM is done with the processing as well. 80 | 81 | 82 | ### `proxy_on_delete` 83 | 84 | * params: 85 | - `i32 (uint32_t) context_id` 86 | * returns: 87 | - none 88 | 89 | Called when the host environment removes the context (`context_id`). This is used to signal that VM 90 | should stop tracking that `context_id` and remove all associated state. 91 | 92 | 93 | ## Configuration 94 | 95 | ### `proxy_on_configure` 96 | 97 | * params: 98 | - `i32 (uint32_t) root_context_id` 99 | - `i32 (size_t) plugin_configuration_size` 100 | * returns: 101 | - `i32 (bool) success` 102 | 103 | Called when the host environment starts the plugin. Its configuration (`plugin_configuration_size`) 104 | might be retrieved using `proxy_get_buffer`. 105 | 106 | 107 | ## HTTP (L7) extensions 108 | 109 | ### `proxy_on_request_headers` 110 | 111 | * params: 112 | - `i32 (uint32_t) context_id` 113 | - `i32 (size_t) num_headers` 114 | - `i32 (bool) end_of_stream` 115 | * returns: 116 | - `i32 (proxy_action_t) next_action` 117 | 118 | Called when HTTP request headers are received from the client. 119 | 120 | TODO: pass a correct `num_headers` but not 0. 121 | 122 | ### `proxy_on_request_body` 123 | 124 | * params: 125 | - `i32 (uint32_t) context_id` 126 | - `i32 (size_t) body_size` 127 | - `i32 (bool) end_of_stream` 128 | * returns: 129 | - `i32 (proxy_action_t) next_action` 130 | 131 | Called for HTTP request body received from the client. Request body can be retrieved 132 | using `proxy_get_buffer_bytes`. 133 | 134 | ### `proxy_on_response_headers` 135 | 136 | * params: 137 | - `i32 (uint32_t) context_id` 138 | - `i32 (size_t) num_headers` 139 | - `i32 (bool) end_of_stream` 140 | * returns: 141 | - `i32 (proxy_action_t) next_action` 142 | 143 | Called when HTTP response headers are received from the upstream. 144 | 145 | TODO: pass a correct `num_headers` but not 0. 146 | 147 | 148 | ## HTTP calls 149 | 150 | ### `proxy_on_http_call_response` 151 | 152 | * params: 153 | - `i32 (uint32_t) context_id` 154 | - `i32 (uint32_t) callout_id` 155 | - `i32 (size_t) num_headers` 156 | - `i32 (size_t) body_size` 157 | - `i32 (size_t) num_trailers` 158 | * returns: 159 | - none 160 | 161 | Called when the response to the HTTP call (`callout_id`) is received. 162 | 163 | 164 | # Functions implemented in the host environment 165 | 166 | All functions implemented in the host environment return `proxy_result_t`, which indicates the 167 | status of the call (successful, invalid memory access, etc.), and the return values are written into 168 | memory pointers passed in as arguments (indicated by the `return_` prefix in the specification). 169 | 170 | 171 | ## Logging 172 | 173 | ### `proxy_log` 174 | 175 | * params: 176 | - `i32 (proxy_log_level_t) log_level` 177 | - `i32 (const char*) message_data` 178 | - `i32 (size_t) message_size` 179 | * returns: 180 | - `i32 (proxy_result_t) call_result` 181 | 182 | Log message (`message_data`, `message_size`) at the given `log_level`. 183 | 184 | 185 | ## Buffers, maps, and properties 186 | 187 | ### `proxy_get_buffer` 188 | 189 | * params: 190 | - `i32 (proxy_buffer_type_t) buffer_type` 191 | - `i32 (offset_t) offset` 192 | - `i32 (size_t) max_size` 193 | - `i32 (const char**) return_buffer_data` 194 | - `i32 (size_t*) return_buffer_size` 195 | - `i32 (uint32_t*) return_flags` 196 | * returns: 197 | - `i32 (proxy_result_t) call_result` 198 | 199 | Get up to max_size bytes from the `buffer_type`, starting from `offset`. Bytes are written into 200 | buffer slice (`return_buffer_data`, `return_buffer_size`), and buffer flags are written into 201 | `return_flags`. 202 | 203 | Note: we implement `proxy_get_buffer_bytes` (an older version of `proxy_get_buffer`) instead 204 | because proxy-wasm-go-sdk uses it. We also implement `proxy_get_configuration` to get 205 | configuration separately because proxy-wasm-rust-sdk still uses it. 206 | 207 | `proxy_get_buffer_bytes` 208 | 209 | * params: 210 | - `i32 (proxy_buffer_type_t) buffer_type` 211 | - `i32 (offset_t) offset` 212 | - `i32 (size_t) max_size` 213 | - `i32 (const char**) return_buffer_data` 214 | - `i32 (size_t*) return_buffer_size` 215 | * returns: 216 | - `i32 (proxy_result_t) call_result` 217 | 218 | 219 | ### `proxy_get_map` (`proxy_get_header_map_pairs`) 220 | 221 | * params: 222 | - `i32 (proxy_map_type_t) map_type` 223 | - `i32 (const char**) return_map_data` 224 | - `i32 (size_t*) return_map_size` 225 | * returns: 226 | - `i32 (proxy_result_t) call_result` 227 | 228 | Get all key-value pairs from a given map (`map_type`). 229 | 230 | 231 | ### `proxy_get_map_value` (`proxy_get_header_map_value`) 232 | 233 | * params: 234 | - `i32 (proxy_map_type_t) map_type` 235 | - `i32 (const char*) key_data` 236 | - `i32 (size_t) key_size` 237 | - `i32 (const char**) return_value_data` 238 | - `i32 (size_t*) return_value_size` 239 | * returns: 240 | - `i32 (proxy_result_t) call_result` 241 | 242 | Get content of key (`key_data`, `key_size`) from a given map (`map_type`). 243 | 244 | 245 | ### `proxy_set_map_value` (`proxy_replace_header_map_value`) 246 | 247 | * params: 248 | - `i32 (proxy_map_type_t) map_type` 249 | - `i32 (const char*) key_data` 250 | - `i32 (size_t) key_size` 251 | - `i32 (const char*) value_data` 252 | - `i32 (size_t) value_size` 253 | * returns: 254 | - `i32 (proxy_result_t) call_result` 255 | 256 | Set or replace the content of key (`key_data`, `key_size`) to the value (`value_data`, `value_size`) 257 | in a given map (`map_type`). 258 | 259 | 260 | ### `proxy_add_map_value` (`proxy_add_header_map_value`) 261 | 262 | * params: 263 | - `i32 (proxy_map_type_t) map_type` 264 | - `i32 (const char*) key_data` 265 | - `i32 (size_t) key_size` 266 | - `i32 (const char*) value_data` 267 | - `i32 (size_t) value_size` 268 | * returns: 269 | - `i32 (proxy_result_t) call_result` 270 | 271 | Add key (`key_data`, `key_size`) with the value (`value_data`, `value_size`) to a given map 272 | (`map_type`). 273 | 274 | 275 | ### `proxy_remove_map_value` (`proxy_remove_header_map_value`) 276 | 277 | * params: 278 | - `i32 (proxy_map_type_t) map_type` 279 | - `i32 (const char*) key_data` 280 | - `i32 (size_t) key_size` 281 | * returns: 282 | - `i32 (proxy_result_t) call_result` 283 | 284 | Remove key (`key_data`, `key_size`) from a given map (`map_type`). 285 | 286 | 287 | ### `proxy_get_property` 288 | 289 | * params: 290 | - `i32 (const char*) path_data` 291 | - `i32 (size_t) path_size` 292 | - `i32 (const char*) res_data` 293 | - `i32 (size_t) res_size` 294 | 295 | Get data such as Nginx variables and plugin ID, Use path_data as key and write the value of the obtained variable to res_data. 296 | 297 | 298 | ### `proxy_set_property` 299 | 300 | * params: 301 | - `i32 (const char*) path_data` 302 | - `i32 (size_t) path_size` 303 | - `i32 (const char*) data` 304 | - `i32 (size_t) size` 305 | 306 | Set the Nginx variable value, using path_data as the key and data as the value. 307 | 308 | ## HTTP (L7) extensions 309 | 310 | ### `proxy_send_http_response` 311 | 312 | * params: 313 | - `i32 (uint32_t) response_code` 314 | - `i32 (const char*) response_code_details_data` 315 | - `i32 (size_t) response_code_details_size` 316 | - `i32 (const char*) response_body_data` 317 | - `i32 (size_t) response_body_size` 318 | - `i32 (const char*) additional_headers_map_data` 319 | - `i32 (size_t) additional_headers_size` 320 | - `i32 (uint32_t) grpc_status` 321 | * returns: 322 | - `i32 (proxy_result_t) call_result` 323 | 324 | Sends HTTP response without forwarding request to the upstream. 325 | Note: we only implement the handling of response_code and response_body. 326 | 327 | We only implement `proxy_send_local_response` as an alias because proxy-wasm-go-sdk uses it. 328 | 329 | 330 | ## HTTP calls 331 | 332 | ### `proxy_dispatch_http_call` 333 | 334 | * params: 335 | - `i32 (const char*) upstream_name_data` 336 | - `i32 (size_t) upstream_name_size` 337 | - `i32 (const char*) headers_map_data` 338 | - `i32 (size_t) headers_map_size` 339 | - `i32 (const char*) body_data` 340 | - `i32 (size_t) body_size` 341 | - `i32 (const char*) trailers_map_data` 342 | - `i32 (size_t) trailers_map_size` 343 | - `i32 (uint32_t) timeout_milliseconds` 344 | - `i32 (uint32_t*) return_callout_id` 345 | * returns: 346 | - `i32 (proxy_result_t) call_result` 347 | 348 | Dispatch an HTTP call to upstream (`upstream_name_data`, `upstream_name_size`). Once the response is 349 | returned to the host, `proxy_on_http_call_response` will be called with a unique call identifier 350 | (`return_callout_id`). 351 | 352 | ## metric extensions 353 | 354 | ### `proxy_define_metric` 355 | 356 | * params: 357 | - `i32 (proxy_metric_type_t) metric_type` 358 | - `i32 (const char*) name_ptr` 359 | - `i32 (size_t) name_size` 360 | - `i32 (uint32_t*) metric_id` 361 | * returns: 362 | - `i32 (proxy_result_t) call_result` 363 | 364 | ### `proxy_increment_metric` 365 | 366 | * params: 367 | - `i32 (uint32_t) metric_id` 368 | - `i64 (int64_t) offset` 369 | * returns: 370 | - `i32 (proxy_result_t) call_result` 371 | 372 | ### `proxy_record_metric` 373 | 374 | * Params: 375 | - `i32 (uint32_t) metric_id` 376 | - `i64 (uint64_t) value` 377 | * returns: 378 | - `i32 (proxy_result_t) call_result` 379 | 380 | ### `proxy_get_metric` 381 | 382 | * Params: 383 | - `i32 (uint32_t) metric_id` 384 | - `i64 (uint64_t*) result` 385 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef NGX_HTTP_WASM_API_H 18 | #define NGX_HTTP_WASM_API_H 19 | 20 | #include 21 | #include 22 | 23 | 24 | ngx_int_t ngx_http_wasm_resolve_symbol(void); 25 | 26 | 27 | #endif // NGX_HTTP_WASM_API_H 28 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_api_def.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | /* Code generated by `./gen_wasm_host_api.py src/http`. DO NOT EDIT. */ 19 | #ifndef NGX_HTTP_WASM_API_DEF_H 20 | #define NGX_HTTP_WASM_API_DEF_H 21 | 22 | #include 23 | #include 24 | 25 | 26 | #define DEFINE_WASM_API_ARG_VOID void 27 | #define DEFINE_WASM_API_ARG_I32_1 \ 28 | int32_t 29 | #define DEFINE_WASM_API_ARG_I32_2 \ 30 | int32_t, int32_t 31 | #define DEFINE_WASM_API_ARG_I32_3 \ 32 | int32_t, int32_t, int32_t 33 | #define DEFINE_WASM_API_ARG_I32_4 \ 34 | int32_t, int32_t, int32_t, int32_t 35 | #define DEFINE_WASM_API_ARG_I32_5 \ 36 | int32_t, int32_t, int32_t, int32_t, int32_t 37 | #define DEFINE_WASM_API_ARG_I32_6 \ 38 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t 39 | #define DEFINE_WASM_API_ARG_I32_7 \ 40 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t 41 | #define DEFINE_WASM_API_ARG_I32_8 \ 42 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t 43 | #define DEFINE_WASM_API_ARG_I32_9 \ 44 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, \ 45 | int32_t 46 | #define DEFINE_WASM_API_ARG_I32_10 \ 47 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, \ 48 | int32_t, int32_t 49 | #define DEFINE_WASM_API_ARG_I32_11 \ 50 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, \ 51 | int32_t, int32_t, int32_t 52 | #define DEFINE_WASM_API_ARG_I32_12 \ 53 | int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, \ 54 | int32_t, int32_t, int32_t, int32_t 55 | #define DEFINE_WASM_API_ARG_I32_I64 \ 56 | int32_t, int64_t 57 | 58 | int32_t proxy_set_effective_context(DEFINE_WASM_API_ARG_I32_1); 59 | int32_t proxy_get_property(DEFINE_WASM_API_ARG_I32_4); 60 | int32_t proxy_set_property(DEFINE_WASM_API_ARG_I32_4); 61 | int32_t proxy_log(DEFINE_WASM_API_ARG_I32_3); 62 | int32_t proxy_get_buffer_bytes(DEFINE_WASM_API_ARG_I32_5); 63 | int32_t proxy_set_buffer_bytes(DEFINE_WASM_API_ARG_I32_5); 64 | int32_t proxy_send_local_response(DEFINE_WASM_API_ARG_I32_8); 65 | int32_t proxy_send_http_response(DEFINE_WASM_API_ARG_I32_8); 66 | int32_t proxy_get_current_time_nanoseconds(DEFINE_WASM_API_ARG_I32_1); 67 | int32_t proxy_set_tick_period_milliseconds(DEFINE_WASM_API_ARG_I32_1); 68 | int32_t proxy_get_configuration(DEFINE_WASM_API_ARG_I32_2); 69 | int32_t proxy_get_header_map_pairs(DEFINE_WASM_API_ARG_I32_3); 70 | int32_t proxy_set_header_map_pairs(DEFINE_WASM_API_ARG_I32_3); 71 | int32_t proxy_get_header_map_value(DEFINE_WASM_API_ARG_I32_5); 72 | int32_t proxy_remove_header_map_value(DEFINE_WASM_API_ARG_I32_3); 73 | int32_t proxy_replace_header_map_value(DEFINE_WASM_API_ARG_I32_5); 74 | int32_t proxy_add_header_map_value(DEFINE_WASM_API_ARG_I32_5); 75 | int32_t proxy_get_shared_data(DEFINE_WASM_API_ARG_I32_5); 76 | int32_t proxy_set_shared_data(DEFINE_WASM_API_ARG_I32_5); 77 | int32_t proxy_register_shared_queue(DEFINE_WASM_API_ARG_I32_3); 78 | int32_t proxy_resolve_shared_queue(DEFINE_WASM_API_ARG_I32_5); 79 | int32_t proxy_dequeue_shared_queue(DEFINE_WASM_API_ARG_I32_3); 80 | int32_t proxy_enqueue_shared_queue(DEFINE_WASM_API_ARG_I32_3); 81 | int32_t proxy_continue_request(DEFINE_WASM_API_ARG_VOID); 82 | int32_t proxy_continue_response(DEFINE_WASM_API_ARG_VOID); 83 | int32_t proxy_clear_route_cache(DEFINE_WASM_API_ARG_VOID); 84 | int32_t proxy_continue_stream(DEFINE_WASM_API_ARG_I32_1); 85 | int32_t proxy_close_stream(DEFINE_WASM_API_ARG_I32_1); 86 | int32_t proxy_http_call(DEFINE_WASM_API_ARG_I32_10); 87 | int32_t proxy_grpc_call(DEFINE_WASM_API_ARG_I32_12); 88 | int32_t proxy_grpc_stream(DEFINE_WASM_API_ARG_I32_9); 89 | int32_t proxy_grpc_send(DEFINE_WASM_API_ARG_I32_4); 90 | int32_t proxy_grpc_cancel(DEFINE_WASM_API_ARG_I32_1); 91 | int32_t proxy_grpc_close(DEFINE_WASM_API_ARG_I32_1); 92 | int32_t proxy_get_status(DEFINE_WASM_API_ARG_I32_3); 93 | int32_t proxy_done(DEFINE_WASM_API_ARG_VOID); 94 | int32_t proxy_call_foreign_function(DEFINE_WASM_API_ARG_I32_6); 95 | int32_t proxy_define_metric(DEFINE_WASM_API_ARG_I32_4); 96 | int32_t proxy_increment_metric(DEFINE_WASM_API_ARG_I32_I64); 97 | int32_t proxy_record_metric(DEFINE_WASM_API_ARG_I32_I64); 98 | int32_t proxy_get_metric(DEFINE_WASM_API_ARG_I32_I64); 99 | 100 | 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_call.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #include 18 | #include "ngx_http_wasm_call.h" 19 | #include "ngx_http_wasm_ctx.h" 20 | #include "proxy_wasm/proxy_wasm_types.h" 21 | #include "proxy_wasm/proxy_wasm_map.h" 22 | 23 | 24 | typedef struct { 25 | ngx_str_t name; 26 | ngx_uint_t ty; 27 | } proxy_wasm_pseudo_header_t; 28 | 29 | 30 | typedef struct { 31 | int32_t timeout_ms; 32 | ngx_str_t *up; 33 | u_char *map_data; 34 | ngx_str_t *body; 35 | } proxy_wasm_callout_t; 36 | 37 | 38 | static proxy_wasm_pseudo_header_t proxy_wasm_pseudo_headers[] = { 39 | {ngx_string(":path"), PROXY_WASM_REQUEST_HEADER_PATH}, 40 | {ngx_string(":method"), PROXY_WASM_REQUEST_HEADER_METHOD}, 41 | {ngx_string(":scheme"), PROXY_WASM_REQUEST_HEADER_SCHEME}, 42 | {ngx_string(":authority"), PROXY_WASM_REQUEST_HEADER_AUTHORITY}, 43 | {ngx_null_string, 0 } 44 | }; 45 | 46 | 47 | static uint32_t cur_callout_id = 0; 48 | 49 | 50 | ngx_int_t 51 | ngx_http_wasm_call_register(ngx_http_request_t *r, ngx_str_t *up, u_char *map_data, 52 | ngx_str_t *body, int32_t timeout, uint32_t *callout_id) 53 | { 54 | ngx_http_wasm_ctx_t *ctx; 55 | proxy_wasm_callout_t *callout; 56 | 57 | ctx = ngx_http_wasm_get_module_ctx(r); 58 | /* as we are running with http ctx, we can ensure the ctx is not NULL */ 59 | 60 | if (ctx->callout != NULL) { 61 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 62 | "multiple calls are not supported"); 63 | return NGX_ERROR; 64 | } 65 | 66 | callout = ngx_palloc(r->pool, sizeof(proxy_wasm_callout_t)); 67 | if (callout == NULL) { 68 | return NGX_DECLINED; 69 | } 70 | 71 | if (timeout < 0) { 72 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 73 | "invalid timeout: %d", timeout); 74 | return NGX_ERROR; 75 | } 76 | 77 | callout->timeout_ms = timeout; 78 | callout->up = up; 79 | callout->map_data = map_data; 80 | callout->body = body; 81 | 82 | ctx->callout = callout; 83 | ctx->callout_id = cur_callout_id; 84 | 85 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 86 | "register http call callout id: %d, host: %V", 87 | cur_callout_id, up); 88 | 89 | *callout_id = cur_callout_id; 90 | cur_callout_id++; 91 | 92 | return NGX_OK; 93 | } 94 | 95 | 96 | ngx_int_t 97 | ngx_http_wasm_call_max_headers_count(ngx_http_request_t *r) 98 | { 99 | ngx_http_wasm_ctx_t *ctx; 100 | proxy_wasm_callout_t *callout; 101 | proxy_wasm_map_iter it; 102 | 103 | ctx = ngx_http_wasm_get_module_ctx(r); 104 | callout = ctx->callout; 105 | proxy_wasm_map_init_iter(&it, callout->map_data); 106 | 107 | return it.len; 108 | } 109 | 110 | 111 | void 112 | ngx_http_wasm_call_get(ngx_http_request_t *r, ngx_str_t *method, ngx_str_t *scheme, 113 | ngx_str_t *host, ngx_str_t *path, 114 | proxy_wasm_table_elt_t *headers, ngx_str_t *body, 115 | int32_t *timeout) 116 | { 117 | ngx_http_wasm_ctx_t *ctx; 118 | proxy_wasm_callout_t *callout; 119 | proxy_wasm_map_iter it; 120 | ngx_uint_t i; 121 | char *key, *val; 122 | int32_t key_len, val_len; 123 | 124 | ctx = ngx_http_wasm_get_module_ctx(r); 125 | callout = ctx->callout; 126 | ctx->callout = NULL; 127 | 128 | *host = *callout->up; 129 | *timeout = callout->timeout_ms; 130 | 131 | proxy_wasm_map_init_iter(&it, callout->map_data); 132 | 133 | while (proxy_wasm_map_next(&it, &key, &key_len, &val, &val_len)) { 134 | if (key_len == 0) { 135 | continue; 136 | } 137 | 138 | if (key[0] == ':') { 139 | for (i = 0; proxy_wasm_pseudo_headers[i].ty > 0; i++) { 140 | proxy_wasm_pseudo_header_t *wh; 141 | 142 | wh = &proxy_wasm_pseudo_headers[i]; 143 | 144 | if ((size_t) key_len != wh->name.len) { 145 | continue; 146 | } 147 | 148 | if (ngx_strncasecmp((u_char *) key, wh->name.data, wh->name.len) == 0) { 149 | 150 | switch (wh->ty) { 151 | case PROXY_WASM_REQUEST_HEADER_PATH: 152 | path->data = (u_char *) val; 153 | path->len = val_len; 154 | goto next; 155 | 156 | case PROXY_WASM_REQUEST_HEADER_METHOD: 157 | method->data = (u_char *) val; 158 | method->len = val_len; 159 | goto next; 160 | 161 | case PROXY_WASM_REQUEST_HEADER_SCHEME: 162 | scheme->data = (u_char *) val; 163 | scheme->len = val_len; 164 | goto next; 165 | 166 | case PROXY_WASM_REQUEST_HEADER_AUTHORITY: 167 | headers->key.data = (u_char *) "host"; 168 | headers->key.len = 4; 169 | headers->value.data = (u_char *) val; 170 | headers->value.len = val_len; 171 | headers++; 172 | goto next; 173 | 174 | default: 175 | break; 176 | } 177 | } 178 | } 179 | } 180 | 181 | headers->key.data = (u_char *) key; 182 | headers->key.len = key_len; 183 | headers->value.data = (u_char *) val; 184 | headers->value.len = val_len; 185 | headers++; 186 | 187 | next: 188 | continue; 189 | } 190 | 191 | /* mark the end */ 192 | headers->key.len = 0; 193 | 194 | if (callout->body) { 195 | *body = *callout->body; 196 | } else { 197 | body->len = 0; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_call.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef NGX_HTTP_WASM_CALL_H 18 | #define NGX_HTTP_WASM_CALL_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | /* this is NGX_HTTP_LUA_CONTEXT_$PHASE in the lua-nginx-module */ 26 | #define NGX_HTTP_WASM_PHASE_REWRITE 0x0002 27 | #define NGX_HTTP_WASM_PHASE_ACCESS 0x0004 28 | #define NGX_HTTP_WASM_PHASE_CONTENT 0x0008 29 | #define NGX_HTTP_WASM_PHASE_TIMER 0x0080 30 | 31 | 32 | #define NGX_HTTP_WASM_YIELDABLE (NGX_HTTP_WASM_PHASE_REWRITE \ 33 | | NGX_HTTP_WASM_PHASE_ACCESS \ 34 | | NGX_HTTP_WASM_PHASE_CONTENT \ 35 | | NGX_HTTP_WASM_PHASE_TIMER \ 36 | ) 37 | 38 | 39 | ngx_int_t ngx_http_wasm_call_register(ngx_http_request_t *r, ngx_str_t *up, 40 | u_char *map_data, ngx_str_t *body, 41 | int32_t timeout, uint32_t *callout_id); 42 | 43 | 44 | #endif // NGX_HTTP_WASM_CALL_H 45 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_ctx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef NGX_HTTP_WASM_CTX_H 18 | #define NGX_HTTP_WASM_CTX_H 19 | 20 | #include 21 | #include 22 | #include "ngx_http_wasm_state.h" 23 | #include "proxy_wasm/proxy_wasm_types.h" 24 | 25 | 26 | #define PROXY_WASM_ABI_VER_010 0 27 | #define PROXY_WASM_ABI_VER_020 1 28 | #define PROXY_WASM_ABI_VER_021 2 29 | #define PROXY_WASM_ABI_VER_MAX 99 30 | 31 | 32 | typedef struct { 33 | void *plugin; 34 | uint32_t cur_ctx_id; 35 | uint32_t abi_version; 36 | ngx_str_t name; 37 | ngx_http_wasm_state_t *state; 38 | ngx_queue_t occupied; 39 | ngx_queue_t free; 40 | unsigned done:1; 41 | } ngx_http_wasm_plugin_t; 42 | 43 | 44 | typedef struct { 45 | ngx_queue_t queue; 46 | uint32_t id; 47 | ngx_http_wasm_state_t *state; 48 | ngx_http_wasm_plugin_t *hw_plugin; 49 | ngx_pool_t *pool; 50 | ngx_queue_t occupied; 51 | ngx_queue_t free; 52 | unsigned done:1; 53 | } ngx_http_wasm_plugin_ctx_t; 54 | 55 | 56 | typedef struct { 57 | ngx_queue_t queue; 58 | uint32_t id; 59 | ngx_http_wasm_plugin_ctx_t *hwp_ctx; 60 | } ngx_http_wasm_http_ctx_t; 61 | 62 | 63 | typedef struct { 64 | ngx_array_t *http_ctxs; 65 | void *callout; 66 | uint32_t callout_id; 67 | /* for http callback */ 68 | proxy_wasm_table_elt_t *call_resp_headers; 69 | ngx_uint_t call_resp_n_header; 70 | ngx_str_t *call_resp_body; 71 | } ngx_http_wasm_ctx_t; 72 | 73 | 74 | ngx_http_wasm_ctx_t *ngx_http_wasm_get_module_ctx(ngx_http_request_t *r); 75 | 76 | 77 | #endif // NGX_HTTP_WASM_CTX_H 78 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef NGX_HTTP_WASM_MODULE_H 18 | #define NGX_HTTP_WASM_MODULE_H 19 | 20 | #include 21 | #include 22 | 23 | 24 | typedef struct { 25 | ngx_str_t vm; 26 | uint32_t code; 27 | ngx_str_t body; 28 | } ngx_http_wasm_main_conf_t; 29 | 30 | 31 | extern ngx_module_t ngx_http_wasm_module; 32 | 33 | 34 | #endif // NGX_HTTP_WASM_MODULE_H 35 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_state.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #include "ngx_http_wasm_state.h" 18 | 19 | 20 | static ngx_http_wasm_state_t *cur_state = NULL; 21 | 22 | 23 | void 24 | ngx_http_wasm_set_state(ngx_http_wasm_state_t *state) 25 | { 26 | if (state == NULL) { 27 | /* clear state data */ 28 | cur_state->body.data = NULL; 29 | cur_state->body.len = 0; 30 | } 31 | 32 | cur_state = state; 33 | } 34 | 35 | 36 | const ngx_str_t * 37 | ngx_http_wasm_get_conf(void) 38 | { 39 | if (cur_state == NULL) { 40 | return NULL; 41 | } 42 | 43 | return &cur_state->conf; 44 | } 45 | 46 | 47 | ngx_http_request_t * 48 | ngx_http_wasm_get_req(void) 49 | { 50 | if (cur_state == NULL) { 51 | return NULL; 52 | } 53 | 54 | return cur_state->r; 55 | } 56 | 57 | 58 | ngx_log_t * 59 | ngx_http_wasm_get_log(void) 60 | { 61 | if (cur_state != NULL && cur_state->r != NULL) { 62 | return cur_state->r->connection->log; 63 | } 64 | 65 | return ngx_cycle->log; 66 | } 67 | 68 | 69 | ngx_str_t * 70 | ngx_http_wasm_get_plugin_name(void) 71 | { 72 | if (cur_state == NULL) { 73 | return NULL; 74 | } 75 | 76 | return cur_state->plugin_name; 77 | } 78 | 79 | 80 | const ngx_str_t * 81 | ngx_http_wasm_get_body(void) 82 | { 83 | if (cur_state == NULL) { 84 | return NULL; 85 | } 86 | 87 | return &cur_state->body; 88 | } 89 | -------------------------------------------------------------------------------- /src/http/ngx_http_wasm_state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef NGX_HTTP_WASM_STATE_H 18 | #define NGX_HTTP_WASM_STATE_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | typedef struct { 26 | ngx_str_t conf; 27 | ngx_str_t body; 28 | ngx_http_request_t *r; 29 | ngx_str_t *plugin_name; 30 | } ngx_http_wasm_state_t; 31 | 32 | 33 | void ngx_http_wasm_set_state(ngx_http_wasm_state_t *state); 34 | const ngx_str_t *ngx_http_wasm_get_conf(void); 35 | ngx_http_request_t *ngx_http_wasm_get_req(void); 36 | ngx_log_t *ngx_http_wasm_get_log(void); 37 | ngx_str_t *ngx_http_wasm_get_plugin_name(void); 38 | const ngx_str_t *ngx_http_wasm_get_body(void); 39 | 40 | 41 | #endif // NGX_HTTP_WASM_STATE_H 42 | -------------------------------------------------------------------------------- /src/proxy_wasm/proxy_wasm_map.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #include "proxy_wasm_map.h" 18 | 19 | 20 | /** 21 | * the format of proxy_map_t is: 22 | * number of items (4 bytes) + 23 | * len of key1 + len of val1 + len of key2 + ... (4 bytes for each len) 24 | * data of key1 + \0 + data of val1 + data of key2 + ... 25 | */ 26 | 27 | 28 | void 29 | proxy_wasm_map_init_map(const u_char *map_data, int32_t len) 30 | { 31 | *(int32_t *) map_data = len; 32 | } 33 | 34 | 35 | void 36 | proxy_wasm_map_init_iter(proxy_wasm_map_iter *it, const u_char *map_data) 37 | { 38 | it->idx = 0; 39 | it->len = *(int32_t *) map_data; 40 | it->size_ptr = (int32_t *) (map_data) + 1; 41 | it->data_ptr = (char *) (it->size_ptr + 2 * it->len); 42 | } 43 | 44 | 45 | bool 46 | proxy_wasm_map_next(proxy_wasm_map_iter *it, char **key, int32_t *key_len, 47 | char **val, int32_t *val_len) 48 | { 49 | if (it->idx == it->len) { 50 | return false; 51 | } 52 | 53 | *key_len = *it->size_ptr; 54 | it->size_ptr++; 55 | *val_len = *it->size_ptr; 56 | it->size_ptr++; 57 | 58 | *key = it->data_ptr; 59 | it->data_ptr += *key_len + 1; 60 | *val = it->data_ptr; 61 | it->data_ptr += *val_len + 1; 62 | 63 | it->idx++; 64 | return true; 65 | } 66 | 67 | 68 | bool 69 | proxy_wasm_map_reserve(proxy_wasm_map_iter *it, char **key, int32_t key_len, 70 | char **val, int32_t val_len) 71 | { 72 | if (it->idx == it->len) { 73 | return false; 74 | } 75 | 76 | *it->size_ptr = key_len; 77 | it->size_ptr++; 78 | *it->size_ptr = val_len; 79 | it->size_ptr++; 80 | 81 | *key = it->data_ptr; 82 | it->data_ptr += key_len; 83 | *it->data_ptr = '\0'; 84 | it->data_ptr++; 85 | *val = it->data_ptr; 86 | it->data_ptr += val_len; 87 | *it->data_ptr = '\0'; 88 | it->data_ptr++; 89 | 90 | it->idx++; 91 | return true; 92 | } 93 | 94 | 95 | bool 96 | proxy_wasm_map_reserve_literal_with_len(proxy_wasm_map_iter *it, 97 | const char *key, size_t key_len, 98 | const char *val, size_t val_len) 99 | { 100 | if (it->idx == it->len) { 101 | return false; 102 | } 103 | 104 | *it->size_ptr = key_len; 105 | it->size_ptr++; 106 | *it->size_ptr = val_len; 107 | it->size_ptr++; 108 | 109 | /* copy trailing '\0' */ 110 | it->data_ptr = (char *) ngx_cpymem(it->data_ptr, key, key_len + 1); 111 | it->data_ptr = (char *) ngx_cpymem(it->data_ptr, val, val_len + 1); 112 | 113 | it->idx++; 114 | return true; 115 | } 116 | -------------------------------------------------------------------------------- /src/proxy_wasm/proxy_wasm_map.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef PROXY_WASM_MAP_H 18 | #define PROXY_WASM_MAP_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | typedef enum { 26 | PROXY_MAP_TYPE_HTTP_REQUEST_HEADERS = 0, 27 | PROXY_MAP_TYPE_HTTP_REQUEST_TRAILERS = 1, 28 | PROXY_MAP_TYPE_HTTP_RESPONSE_HEADERS = 2, 29 | PROXY_MAP_TYPE_HTTP_RESPONSE_TRAILERS = 3, 30 | PROXY_MAP_TYPE_HTTP_CALL_RESPONSE_HEADERS = 6, 31 | PROXY_MAP_TYPE_HTTP_CALL_RESPONSE_TRAILERS = 7, 32 | } proxy_map_type_t; 33 | 34 | 35 | typedef struct { 36 | int32_t idx; 37 | int32_t len; 38 | int32_t *size_ptr; 39 | char *data_ptr; 40 | } proxy_wasm_map_iter; 41 | 42 | 43 | void proxy_wasm_map_init_map(const u_char *map_data, int32_t len); 44 | void proxy_wasm_map_init_iter(proxy_wasm_map_iter *it, const u_char *map_data); 45 | bool proxy_wasm_map_next(proxy_wasm_map_iter *it, char **key, int32_t *key_len, 46 | char **val, int32_t *val_len); 47 | bool proxy_wasm_map_reserve(proxy_wasm_map_iter *it, char **key, int32_t key_len, 48 | char **val, int32_t val_len); 49 | bool proxy_wasm_map_reserve_literal_with_len(proxy_wasm_map_iter *it, 50 | const char *key, size_t key_len, 51 | const char *val, size_t val_len); 52 | #define proxy_wasm_map_reserve_literal(it, k, v) \ 53 | proxy_wasm_map_reserve_literal_with_len(it, k, sizeof((k)) - 1, v, sizeof((v)) - 1) 54 | 55 | 56 | #endif // PROXY_WASM_MAP_H 57 | -------------------------------------------------------------------------------- /src/proxy_wasm/proxy_wasm_memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #include "vm/vm.h" 18 | #include "proxy_wasm_memory.h" 19 | 20 | 21 | static ngx_str_t proxy_on_memory_allocate = ngx_string("proxy_on_memory_allocate"); 22 | static ngx_str_t exported_malloc = ngx_string("malloc"); 23 | 24 | 25 | int32_t proxy_wasm_memory_alloc(ngx_log_t *log, int32_t size) 26 | { 27 | int32_t addr; 28 | 29 | addr = ngx_wasm_vm->call(NULL, &proxy_on_memory_allocate, true, NGX_WASM_PARAM_I32, size); 30 | if (addr == 0) { 31 | addr = ngx_wasm_vm->call(NULL, &exported_malloc, true, NGX_WASM_PARAM_I32, size); 32 | } 33 | 34 | if (addr == 0) { 35 | ngx_log_error(NGX_LOG_ERR, log, 0, "failed to malloc"); 36 | } 37 | 38 | return addr; 39 | } 40 | -------------------------------------------------------------------------------- /src/proxy_wasm/proxy_wasm_memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef PROXY_WASM_MEMORY_H 18 | #define PROXY_WASM_MEMORY_H 19 | #include 20 | #include 21 | 22 | 23 | /* 24 | * malloc allocates memory in WASM, and then return the address of the allocated 25 | * memory. 26 | */ 27 | int32_t proxy_wasm_memory_alloc(ngx_log_t *log, int32_t size); 28 | 29 | 30 | #endif // PROXY_WASM_MEMORY_H 31 | -------------------------------------------------------------------------------- /src/proxy_wasm/proxy_wasm_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef PROXY_WASM_TYPES_H 18 | #define PROXY_WASM_TYPES_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | typedef struct { 26 | ngx_http_lua_ffi_str_t key; 27 | ngx_http_lua_ffi_str_t value; 28 | } proxy_wasm_table_elt_t; 29 | 30 | 31 | typedef enum { 32 | PROXY_RESULT_OK = 0, 33 | // The result could not be found, e.g. a provided key did not appear in a 34 | // table. 35 | PROXY_RESULT_NOT_FOUND = 1, 36 | // An argument was bad, e.g. did not not conform to the required range. 37 | PROXY_RESULT_BAD_ARGUMENT = 2, 38 | // A protobuf could not be serialized. 39 | PROXY_RESULT_SERIALIZATIONF_AILURE = 3, 40 | // A protobuf could not be parsed. 41 | PROXY_RESULT_PARSE_FAILURE = 4, 42 | // A provided expression (e.g. "foo.bar") was illegal or unrecognized. 43 | PROXY_RESULT_BAD_EXPRESSION = 5, 44 | // A provided memory range was not legal. 45 | PROXY_RESULT_INVALID_MEMORY_ACCESS = 6, 46 | // Data was requested from an empty container. 47 | PROXY_RESULT_EMPTY = 7, 48 | // The provided CAS did not match that of the stored data. 49 | PROXY_RESULT_CAS_MISMATCH = 8, 50 | // Returned result was unexpected, e.g. of the incorrect size. 51 | PROXY_RESULT_RESULT_MISMATCH = 9, 52 | // Internal failure: trying check logs of the surrounding system. 53 | PROXY_RESULT_INTERNAL_FAILURE = 10, 54 | // The connection/stream/pipe was broken/closed unexpectedly. 55 | PROXY_RESULT_BROKEN_CONNECTION = 11, 56 | // Feature not implemented. 57 | PROXY_RESULT_UNIMPLEMENTED = 12, 58 | } proxy_result_t; 59 | 60 | 61 | typedef enum { 62 | Counter = 0, 63 | Gauge = 1, 64 | Histogram = 2, 65 | Max = 2, 66 | } metric_type_t; 67 | 68 | 69 | typedef enum { 70 | PROXY_LOG_TRACE, 71 | PROXY_LOG_DEBUG, 72 | PROXY_LOG_INFO, 73 | PROXY_LOG_WARN, 74 | PROXY_LOG_ERROR, 75 | PROXY_LOG_CRITICAL, 76 | } proxy_log_level_t; 77 | 78 | 79 | typedef enum { 80 | PROXY_BUFFER_TYPE_HTTP_REQUEST_BODY, 81 | PROXY_BUFFER_TYPE_HTTP_RESPONSE_BODY, 82 | PROXY_BUFFER_TYPE_DOWNSTREAM_DATA, 83 | PROXY_BUFFER_TYPE_UPSTREAM_DATA, 84 | PROXY_BUFFER_TYPE_HTTP_CALL_RESPONSE_BODY, 85 | PROXY_BUFFER_TYPE_GRPC_RECEIVE_BUFFER, 86 | PROXY_BUFFER_TYPE_VM_CONFIGURATION, 87 | PROXY_BUFFER_TYPE_PLUGIN_CONFIGURATION, 88 | PROXY_BUFFER_TYPE_CALL_DATA, 89 | } proxy_buffer_type_t; 90 | 91 | 92 | enum { 93 | PROXY_WASM_REQUEST_HEADER = 1, 94 | PROXY_WASM_RESPONSE_HEADER, 95 | }; 96 | 97 | 98 | enum { 99 | PROXY_WASM_REQUEST_HEADER_PATH = 1, 100 | PROXY_WASM_REQUEST_HEADER_METHOD, 101 | PROXY_WASM_REQUEST_HEADER_SCHEME, 102 | PROXY_WASM_REQUEST_HEADER_AUTHORITY, 103 | }; 104 | 105 | 106 | enum { 107 | PROXY_WASM_RESPONSE_HEADER_STATUS = 1, 108 | }; 109 | 110 | 111 | #endif // PROXY_WASM_TYPES_H 112 | -------------------------------------------------------------------------------- /src/vm/vm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #include "vm.h" 18 | 19 | 20 | ngx_wasm_vm_t *ngx_wasm_vm = NULL; 21 | 22 | 23 | ngx_int_t 24 | ngx_wasm_vm_init(ngx_str_t *name) 25 | { 26 | if (ngx_strcmp(name->data, "wasmtime") == 0) { 27 | ngx_wasm_vm = &ngx_wasm_wasmtime_vm; 28 | 29 | #if (NGX_WASM_HAVE_WASMEDGE) 30 | } else if (ngx_strcmp(name->data, "wasmedge") == 0) { 31 | ngx_wasm_vm = &ngx_wasm_wasmedge_vm; 32 | #endif 33 | } 34 | 35 | if (ngx_wasm_vm == NULL) { 36 | ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0, "unsupported wasm vm %V", name); 37 | return NGX_ERROR; 38 | } 39 | 40 | return ngx_wasm_vm->init(); 41 | } 42 | 43 | 44 | void 45 | ngx_wasm_vm_cleanup(void *data) 46 | { 47 | ngx_wasm_vm_t *vm = data; 48 | 49 | if (vm == NULL) { 50 | return; 51 | } 52 | 53 | vm->cleanup(); 54 | } 55 | -------------------------------------------------------------------------------- /src/vm/vm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef VM_H 18 | #define VM_H 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | #define NGX_WASM_PARAM_VOID 1 27 | #define NGX_WASM_PARAM_I32 2 28 | #define NGX_WASM_PARAM_I32_I32 3 29 | #define NGX_WASM_PARAM_I32_I32_I32 4 30 | #define NGX_WASM_PARAM_I32_I32_I32_I32 5 31 | #define NGX_WASM_PARAM_I32_I32_I32_I32_I32 6 32 | 33 | 34 | typedef struct { 35 | ngx_str_t *name; 36 | 37 | ngx_int_t (*init)(void); 38 | void (*cleanup)(void); 39 | 40 | void *(*load)(const char *bytecode, size_t size); 41 | void (*unload)(void *plugin); 42 | 43 | /* 44 | * get_memory returns a pointer to the given address in WASM. 45 | * It returns NULL if addr + size is out of bound. 46 | */ 47 | u_char *(*get_memory)(ngx_log_t *log, int32_t addr, int32_t size); 48 | 49 | /* 50 | * call run a function exported from the plugin. 51 | */ 52 | ngx_int_t (*call)(void *plugin, ngx_str_t *name, bool has_result, 53 | int param_type, ...); 54 | /* 55 | * has check if a function is exported from the plugin. 56 | */ 57 | bool (*has)(void *plugin, ngx_str_t *name); 58 | } ngx_wasm_vm_t; 59 | 60 | 61 | extern ngx_wasm_vm_t *ngx_wasm_vm; 62 | extern ngx_wasm_vm_t ngx_wasm_wasmtime_vm; 63 | #if (NGX_WASM_HAVE_WASMEDGE) 64 | extern ngx_wasm_vm_t ngx_wasm_wasmedge_vm; 65 | #endif 66 | 67 | 68 | ngx_int_t ngx_wasm_vm_init(ngx_str_t *name); 69 | void ngx_wasm_vm_cleanup(void *data); 70 | 71 | 72 | #endif // VM_H 73 | -------------------------------------------------------------------------------- /t/WASM.pm: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | package t::WASM; 16 | 17 | use Test::Nginx::Socket::Lua; 18 | use Test::Nginx::Socket::Lua::Stream -Base; 19 | use Cwd qw(cwd); 20 | 21 | log_level('info'); 22 | no_long_string(); 23 | no_shuffle(); 24 | master_on(); 25 | worker_connections(1024); 26 | 27 | 28 | $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); 29 | $ENV{WASM_VM} ||= "wasmtime"; 30 | 31 | 32 | add_block_preprocessor(sub { 33 | my ($block) = @_; 34 | 35 | if (!$block->request) { 36 | $block->set_value("request", "GET /t"); 37 | } 38 | 39 | if (!$block->no_error_log && !$block->error_log) { 40 | $block->set_value("no_error_log", "[error]\n[alert]"); 41 | } 42 | 43 | if (!$block->shutdown_error_log) { 44 | # ensure the Leak log is checked 45 | $block->set_value("shutdown_error_log", ""); 46 | } 47 | my $pat = $block->no_shutdown_error_log // ''; 48 | $block->set_value("no_shutdown_error_log", "LeakSanitizer: detected memory leaks\n" . $pat); 49 | 50 | my $wasm_vm = $ENV{WASM_VM}; 51 | my $http_config = $block->http_config // ''; 52 | $http_config .= <<_EOC_; 53 | lua_package_path "lib/?.lua;;"; 54 | lua_ssl_trusted_certificate ../../certs/test.crt; 55 | wasm_vm $wasm_vm; 56 | 57 | server { 58 | listen 1980; 59 | listen 1981 ssl; 60 | ssl_certificate ../../certs/test.crt; 61 | ssl_certificate_key ../../certs/test.key; 62 | 63 | location /sleep { 64 | content_by_lua_block { 65 | ngx.sleep(1) 66 | } 67 | } 68 | 69 | location /repeated_headers { 70 | content_by_lua_block { 71 | ngx.header["foo"] = {"bar", "baz"} 72 | } 73 | } 74 | 75 | location / { 76 | content_by_lua_block { 77 | local cjson = require("cjson") 78 | ngx.log(ngx.WARN, "hit with [", ngx.var.request_method, " ", 79 | ngx.var.scheme, "://", ngx.var.host, ngx.var.request_uri, "]") 80 | local hdrs = ngx.req.get_headers() 81 | hdrs["user-agent"] = nil 82 | local res = {} 83 | for k, v in pairs(hdrs) do 84 | table.insert(res, {k, v}) 85 | end 86 | table.sort(res, function (a, b) 87 | return a[1] < b[1] 88 | end) 89 | ngx.log(ngx.WARN, "hit with headers ", cjson.encode(res)) 90 | 91 | if ngx.var.request_method == "POST" then 92 | ngx.req.read_body() 93 | local body = ngx.req.get_body_data() 94 | ngx.log(ngx.WARN, "hit with body ", body) 95 | end 96 | 97 | if ngx.var.arg_body then 98 | ngx.print(ngx.var.arg_body) 99 | end 100 | } 101 | } 102 | } 103 | _EOC_ 104 | 105 | $block->set_value("http_config", $http_config); 106 | 107 | my $main_config = $block->main_config // ''; 108 | $main_config .= <<_EOC_; 109 | env WASMTIME_BACKTRACE_DETAILS=1; 110 | _EOC_ 111 | $block->set_value("main_config", $main_config); 112 | }); 113 | 114 | 1; 115 | -------------------------------------------------------------------------------- /t/assemblyscript.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM; 16 | 17 | if ($ENV{"WASM_VM"} eq "wasmedge") { 18 | # err msg: When linking module: "wasi_unstable" , function name: "proc_exit" 19 | plan(skip_all => "wasmedge doesn't support wasi_unstable"); 20 | } else { 21 | plan('no_plan'); 22 | } 23 | 24 | run_tests(); 25 | 26 | __DATA__ 27 | 28 | === TEST 1: fault injection 29 | --- config 30 | location /t { 31 | content_by_lua_block { 32 | local wasm = require("resty.proxy-wasm") 33 | local plugin = assert(wasm.load("FaultInjection", 34 | "t/testdata/assemblyscript/build/untouched.wasm")) 35 | local ctx = assert(wasm.on_configure(plugin, 'body')) 36 | assert(wasm.on_http_request_headers(ctx)) 37 | } 38 | } 39 | --- error_code: 403 40 | --- response_body chomp 41 | body 42 | -------------------------------------------------------------------------------- /t/certs/test.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV 3 | BAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G 4 | A1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa 5 | GA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n 6 | RG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM 7 | CHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe 8 | cvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb 9 | VDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR 10 | 2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr 11 | abf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2 12 | WjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/ 13 | Evm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1 14 | /3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh 15 | /B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj 16 | cTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ 17 | tSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl 18 | c3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC 19 | tC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY 20 | 1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl 21 | PYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob 22 | rJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy 23 | hme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1 24 | 7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y 25 | IJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve 26 | U/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM= 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /t/certs/test.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIG5AIBAAKCAYEAyCM0rqJecvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5 3 | jhZB3W6BkWUWR4oNFLLSqcVbVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfo 4 | eLj0efMiOepOSZflj9Ob4yKR2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5s 5 | mPtW1Oc/BV5terhscJdOgmRrabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt 6 | 6iMWEGeQU6mwPENgvj1olji2WjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiy 7 | Vt1TmtMWn1ztk6FfLRqwJWR/Evm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1o 8 | npRVeXhrBajbCRDRBMwaNw/1/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2 9 | fzaqpIfyUbPST4GdqNG9NyIh/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI 10 | 1cGrGwyXbrieNp63AgMBAAECggGBAJM8g0duoHmIYoAJzbmKe4ew0C5fZtFUQNmu 11 | O2xJITUiLT3ga4LCkRYsdBnY+nkK8PCnViAb10KtIT+bKipoLsNWI9Xcq4Cg4G3t 12 | 11XQMgPPgxYXA6m8t+73ldhxrcKqgvI6xVZmWlKDPn+CY/Wqj5PA476B5wEmYbNC 13 | GIcd1FLl3E9Qm4g4b/sVXOHARF6iSvTR+6ol4nfWKlaXSlx2gNkHuG8RVpyDsp9c 14 | z9zUqAdZ3QyFQhKcWWEcL6u9DLBpB/gUjyB3qWhDMe7jcCBZR1ALyRyEjmDwZzv2 15 | jlv8qlLFfn9R29UI0pbuL1eRAz97scFOFme1s9oSU9a12YHfEd2wJOM9bqiKju8y 16 | DZzePhEYuTZ8qxwiPJGy7XvRYTGHAs8+iDlG4vVpA0qD++1FTpv06cg/fOdnwshE 17 | OJlEC0ozMvnM2rZ2oYejdG3aAnUHmSNa5tkJwXnmj/EMw1TEXf+H6+xknAkw05nh 18 | zsxXrbuFUe7VRfgB5ElMA/V4NsScgQKBwQDmMRtnS32UZjw4A8DsHOKFzugfWzJ8 19 | Gc+3sTgs+4dNIAvo0sjibQ3xl01h0BB2Pr1KtkgBYB8LJW/FuYdCRS/KlXH7PHgX 20 | 84gYWImhNhcNOL3coO8NXvd6+m+a/Z7xghbQtaraui6cDWPiCNd/sdLMZQ/7LopM 21 | RbM32nrgBKMOJpMok1Z6zsPzT83SjkcSxjVzgULNYEp03uf1PWmHuvjO1yELwX9/ 22 | goACViF+jst12RUEiEQIYwr4y637GQBy+9cCgcEA3pN9W5OjSPDVsTcVERig8++O 23 | BFURiUa7nXRHzKp2wT6jlMVcu8Pb2fjclxRyaMGYKZBRuXDlc/RNO3uTytGYNdC2 24 | IptU5N4M7iZHXj190xtDxRnYQWWo/PR6EcJj3f/tc3Itm1rX0JfuI3JzJQgDb9Z2 25 | s/9/ub8RRvmQV9LM/utgyOwNdf5dyVoPcTY2739X4ZzXNH+CybfNa+LWpiJIVEs2 26 | txXbgZrhmlaWzwA525nZ0UlKdfktdcXeqke9eBghAoHARVTHFy6CjV7ZhlmDEtqE 27 | U58FBOS36O7xRDdpXwsHLnCXhbFu9du41mom0W4UdzjgVI9gUqG71+SXrKr7lTc3 28 | dMHcSbplxXkBJawND/Q1rzLG5JvIRHO1AGJLmRgIdl8jNgtxgV2QSkoyKlNVbM2H 29 | Wy6ZSKM03lIj74+rcKuU3N87dX4jDuwV0sPXjzJxL7NpR/fHwgndgyPcI14y2cGz 30 | zMC44EyQdTw+B/YfMnoZx83xaaMNMqV6GYNnTHi0TO2TAoHBAKmdrh9WkE2qsr59 31 | IoHHygh7Wzez+Ewr6hfgoEK4+QzlBlX+XV/9rxIaE0jS3Sk1txadk5oFDebimuSk 32 | lQkv1pXUOqh+xSAwk5v88dBAfh2dnnSa8HFN3oz+ZfQYtnBcc4DR1y2X+fVNgr3i 33 | nxruU2gsAIPFRnmvwKPc1YIH9A6kIzqaoNt1f9VM243D6fNzkO4uztWEApBkkJgR 34 | 4s/yOjp6ovS9JG1NMXWjXQPcwTq3sQVLnAHxZRJmOvx69UmK4QKBwFYXXjeXiU3d 35 | bcrPfe6qNGjfzK+BkhWznuFUMbuxyZWDYQD5yb6ukUosrj7pmZv3BxKcKCvmONU+ 36 | CHgIXB+hG+R9S2mCcH1qBQoP/RSm+TUzS/Bl2UeuhnFZh2jSZQy3OwryUi6nhF0u 37 | LDzMI/6aO1ggsI23Ri0Y9ZtqVKczTkxzdQKR9xvoNBUufjimRlS80sJCEB3Qm20S 38 | wzarryret/7GFW1/3cz+hTj9/d45i25zArr3Pocfpur5mfz3fJO8jg== 39 | -----END RSA PRIVATE KEY----- 40 | -------------------------------------------------------------------------------- /t/http_call_callback.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: get all headers 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local json = require("cjson") 26 | local wasm = require("resty.proxy-wasm") 27 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 28 | local conf = {host = "127.0.0.1:1980", action = "headers"} 29 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 30 | assert(wasm.on_http_request_headers(ctx)) 31 | } 32 | } 33 | --- error_log 34 | get numHeaders 6 35 | --- no_error_log 36 | [error] 37 | --- grep_error_log eval 38 | qr/get header \S+: \S+/ 39 | --- grep_error_log_out eval 40 | qr/get header :status: 200, 41 | get header connection: keep-alive, 42 | get header content-type: text\/plain, 43 | get header date: \w+, 44 | get header server: openresty\/[^,]+, 45 | get header transfer-encoding: chunked, 46 | / 47 | 48 | 49 | 50 | === TEST 2: get all headers, with repeated headers 51 | --- config 52 | location /t { 53 | content_by_lua_block { 54 | local json = require("cjson") 55 | local wasm = require("resty.proxy-wasm") 56 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 57 | local conf = {host = "127.0.0.1:1980", path = "/repeated_headers", action = "headers"} 58 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 59 | assert(wasm.on_http_request_headers(ctx)) 60 | } 61 | } 62 | --- error_log 63 | get numHeaders 8 64 | --- no_error_log 65 | [error] 66 | --- grep_error_log eval 67 | qr/get header \S+: \S+/ 68 | --- grep_error_log_out eval 69 | qr/get header :status: 200, 70 | get header connection: keep-alive, 71 | get header content-type: text\/plain, 72 | get header date: \w+, 73 | get header foo: bar, 74 | get header foo: baz, 75 | get header server: openresty\/[^,]+, 76 | get header transfer-encoding: chunked, 77 | / 78 | 79 | 80 | 81 | === TEST 3: callback when the request failed 82 | --- config 83 | location /t { 84 | content_by_lua_block { 85 | local json = require("cjson") 86 | local wasm = require("resty.proxy-wasm") 87 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 88 | local conf = {host = "127.0.0.1:1979", action = "headers"} 89 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 90 | assert(wasm.on_http_request_headers(ctx)) 91 | } 92 | } 93 | --- error_log 94 | callback err: error status returned by host: not found 95 | 96 | 97 | 98 | === TEST 4: get body, no body 99 | --- config 100 | location /t { 101 | content_by_lua_block { 102 | local json = require("cjson") 103 | local wasm = require("resty.proxy-wasm") 104 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 105 | local conf = {host = "127.0.0.1:1980", action = "body"} 106 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 107 | assert(wasm.on_http_request_headers(ctx)) 108 | } 109 | } 110 | --- error_log 111 | get bodySize 0 112 | error status returned by host: not found 113 | 114 | 115 | 116 | === TEST 5: get body 117 | --- config 118 | location /t { 119 | content_by_lua_block { 120 | local json = require("cjson") 121 | local wasm = require("resty.proxy-wasm") 122 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 123 | local conf = {host = "127.0.0.1:1980", action = "body", path = "/?body=helloworld"} 124 | for _, e in ipairs({ 125 | {0, 2}, 126 | {1, 1}, 127 | {1, 2}, 128 | {0, 10}, 129 | {1, 9}, 130 | {8, 2}, 131 | {9, 1}, 132 | {1, 10}, 133 | }) do 134 | conf.start = e[1] 135 | conf.size = e[2] 136 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 137 | assert(wasm.on_http_request_headers(ctx)) 138 | end 139 | } 140 | } 141 | --- grep_error_log eval 142 | qr/get body \[\w+\]/ 143 | --- grep_error_log_out 144 | get body [he] 145 | get body [e] 146 | get body [el] 147 | get body [helloworld] 148 | get body [elloworld] 149 | get body [ld] 150 | get body [d] 151 | get body [elloworld] 152 | 153 | 154 | 155 | === TEST 6: get body, bad cases 156 | --- config 157 | location /t { 158 | content_by_lua_block { 159 | local json = require("cjson") 160 | local wasm = require("resty.proxy-wasm") 161 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 162 | local conf = {host = "127.0.0.1:1980", action = "body", path = "/?body=helloworld"} 163 | for _, e in ipairs({ 164 | {-1, 2}, 165 | {1, 0}, 166 | {1, -1}, 167 | {10, 2}, 168 | }) do 169 | conf.start = e[1] 170 | conf.size = e[2] 171 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 172 | assert(wasm.on_http_request_headers(ctx)) 173 | end 174 | } 175 | } 176 | --- error_log 177 | [error] 178 | --- grep_error_log eval 179 | qr/error status returned by host: bad argument/ 180 | --- grep_error_log_out eval 181 | "error status returned by host: bad argument\n" x 4 182 | 183 | 184 | 185 | === TEST 7: call then send 186 | --- config 187 | location /t { 188 | content_by_lua_block { 189 | local json = require("cjson") 190 | local wasm = require("resty.proxy-wasm") 191 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 192 | local conf = {host = "127.0.0.1:1980", action = "then_send"} 193 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 194 | assert(wasm.on_http_request_headers(ctx)) 195 | } 196 | } 197 | --- error_code: 503 198 | --- grep_error_log eval 199 | qr/run http callback callout id: \d+, plugin ctx id: \d+/ 200 | --- grep_error_log_out 201 | run http callback callout id: 0, plugin ctx id: 1 202 | 203 | 204 | 205 | === TEST 8: call then call 206 | --- config 207 | location /t { 208 | content_by_lua_block { 209 | local json = require("cjson") 210 | local wasm = require("resty.proxy-wasm") 211 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 212 | local conf = {host = "127.0.0.1:1980", action = "then_call"} 213 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 214 | assert(wasm.on_http_request_headers(ctx)) 215 | } 216 | } 217 | --- error_code: 403 218 | --- grep_error_log eval 219 | qr/run http callback callout id: \d+, plugin ctx id: \d+/ 220 | --- grep_error_log_out 221 | run http callback callout id: 0, plugin ctx id: 1 222 | run http callback callout id: 1, plugin ctx id: 1 223 | 224 | 225 | 226 | === TEST 9: call x 3 227 | --- config 228 | location /t { 229 | content_by_lua_block { 230 | local json = require("cjson") 231 | local wasm = require("resty.proxy-wasm") 232 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 233 | local conf = {host = "127.0.0.1:1980", action = "then_call_again"} 234 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 235 | assert(wasm.on_http_request_headers(ctx)) 236 | } 237 | } 238 | --- error_code: 401 239 | --- grep_error_log eval 240 | qr/run http callback callout id: \d+, plugin ctx id: \d+/ 241 | --- grep_error_log_out 242 | run http callback callout id: 0, plugin ctx id: 1 243 | run http callback callout id: 1, plugin ctx id: 1 244 | run http callback callout id: 2, plugin ctx id: 1 245 | -------------------------------------------------------------------------------- /t/http_call_multi.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: sanity 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local json = require("cjson") 26 | local wasm = require("resty.proxy-wasm") 27 | local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm")) 28 | local data = json.encode({ 29 | {host = "127.0.0.1:1980"}, 30 | {host = "127.0.0.1:1981"}, 31 | }) 32 | local ctx = assert(wasm.on_configure(plugin, data)) 33 | assert(wasm.on_http_request_headers(ctx)) 34 | } 35 | } 36 | --- grep_error_log eval 37 | qr/multiple calls are not supported/ 38 | --- grep_error_log_out 39 | multiple calls are not supported 40 | --- error_log 41 | httpcall failed: 42 | -------------------------------------------------------------------------------- /t/http_lifecycle.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: manage ctx 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | local plugin = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 27 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 28 | assert(wasm.on_http_request_headers(ctx)) 29 | assert(wasm.on_http_request_headers(ctx)) 30 | } 31 | } 32 | --- grep_error_log eval 33 | qr/(create|free) http context \d/ 34 | --- grep_error_log_out 35 | create http context 2 36 | free http context 2 37 | 38 | 39 | 40 | === TEST 2: ensure plugin ctx is free after http ctx 41 | --- config 42 | location /t { 43 | content_by_lua_block { 44 | local wasm = require("resty.proxy-wasm") 45 | local plugin = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 46 | do 47 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 48 | assert(wasm.on_http_request_headers(ctx)) 49 | end 50 | collectgarbage() 51 | } 52 | } 53 | --- grep_error_log eval 54 | qr/free (plugin|http) context \d/ 55 | --- grep_error_log_out 56 | free http context 2 57 | free plugin context 1 58 | 59 | 60 | 61 | === TEST 3: multiple http ctx 62 | --- http_config 63 | init_by_lua_block { 64 | local wasm = require("resty.proxy-wasm") 65 | local plugin = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 66 | package.loaded.ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 67 | } 68 | --- config 69 | location /t { 70 | content_by_lua_block { 71 | local http = require "resty.http" 72 | local uri = "http://127.0.0.1:" .. ngx.var.server_port 73 | .. "/hit" 74 | 75 | for _ = 1, 2 do 76 | local t = {} 77 | for i = 1, 9 do 78 | local th = assert(ngx.thread.spawn(function(i) 79 | local httpc = http.new() 80 | local res, err = httpc:request_uri(uri..i, {method = "GET"}) 81 | if not res then 82 | ngx.log(ngx.ERR, err) 83 | return 84 | end 85 | end, i)) 86 | table.insert(t, th) 87 | end 88 | for i, th in ipairs(t) do 89 | ngx.thread.wait(th) 90 | end 91 | -- check if the ctx id is reused 92 | end 93 | } 94 | } 95 | location /hit { 96 | content_by_lua_block { 97 | local wasm = require("resty.proxy-wasm") 98 | local ctx = package.loaded.ctx 99 | assert(wasm.on_http_request_headers(ctx)) 100 | ngx.sleep(math.random() / 10) 101 | } 102 | } 103 | --- grep_error_log eval 104 | qr/free http context (1|11)$/ 105 | --- grep_error_log_out 106 | 107 | 108 | 109 | === TEST 4: multi plugin ctx in same req 110 | --- config 111 | location /t { 112 | content_by_lua_block { 113 | local wasm = require("resty.proxy-wasm") 114 | local plugin = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 115 | local ctx1 = assert(wasm.on_configure(plugin, '{"body":512}')) 116 | local ctx2 = assert(wasm.on_configure(plugin, '{"body":256}')) 117 | assert(wasm.on_http_request_headers(ctx1)) 118 | assert(wasm.on_http_request_headers(ctx2)) 119 | } 120 | } 121 | --- grep_error_log eval 122 | qr/run http ctx \d+ with conf \S+ on http request headers,/ 123 | --- grep_error_log_out 124 | run http ctx 3 with conf {"body":512} on http request headers, 125 | run http ctx 4 with conf {"body":256} on http request headers, 126 | 127 | 128 | 129 | === TEST 5: multi plugin in same req 130 | --- config 131 | location /t { 132 | content_by_lua_block { 133 | local wasm = require("resty.proxy-wasm") 134 | local plugin1 = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 135 | local plugin2 = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 136 | local ctx1 = assert(wasm.on_configure(plugin1, '{"body":512}')) 137 | local ctx2 = assert(wasm.on_configure(plugin2, '{"body":256}')) 138 | assert(wasm.on_http_request_headers(ctx1)) 139 | assert(wasm.on_http_request_headers(ctx2)) 140 | } 141 | } 142 | --- grep_error_log eval 143 | qr/run http ctx \d+ with conf \S+ on http request headers,/ 144 | --- grep_error_log_out 145 | run http ctx 2 with conf {"body":512} on http request headers, 146 | run http ctx 2 with conf {"body":256} on http request headers, 147 | 148 | 149 | 150 | === TEST 6: on response headers 151 | --- config 152 | location /t { 153 | content_by_lua_block { 154 | local wasm = require("resty.proxy-wasm") 155 | local plugin = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 156 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 157 | ngx.ctx.ctx = ctx 158 | assert(wasm.on_http_request_headers(ctx)) 159 | } 160 | header_filter_by_lua_block { 161 | local wasm = require("resty.proxy-wasm") 162 | local ctx = ngx.ctx.ctx 163 | assert(wasm.on_http_response_headers(ctx)) 164 | } 165 | } 166 | --- grep_error_log eval 167 | qr/run http ctx \d+ with conf \S+ on [^,]+,/ 168 | --- grep_error_log_out 169 | run http ctx 2 with conf {"body":512} on http request headers, 170 | run http ctx 2 with conf {"body":512} on http response headers, 171 | 172 | 173 | 174 | === TEST 7: crash on response headers 175 | --- config 176 | location /t { 177 | content_by_lua_block { 178 | local wasm = require("resty.proxy-wasm") 179 | local plugin = assert(wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm")) 180 | ngx.ctx.plugin = plugin 181 | } 182 | header_filter_by_lua_block { 183 | local wasm = require("resty.proxy-wasm") 184 | local plugin = ngx.ctx.plugin 185 | local ctx = assert(wasm.on_configure(plugin, 'panic_on_http_response_headers')) 186 | local ok, err = wasm.on_http_response_headers(ctx) 187 | ngx.log(ngx.ERR, err) 188 | } 189 | } 190 | --- error_log 191 | failed to call function 192 | failed to run proxy_on_http_response_headers 193 | 194 | 195 | 196 | === TEST 8: reuse plugin ctx id with freed http ctx 197 | --- config 198 | location /hit { 199 | content_by_lua_block { 200 | local wasm = require("resty.proxy-wasm") 201 | local ctx = package.loaded.ctx 202 | assert(wasm.on_http_request_headers(ctx)) 203 | } 204 | } 205 | location /t { 206 | content_by_lua_block { 207 | local wasm = require("resty.proxy-wasm") 208 | local plugin = wasm.load("plugin", "t/testdata/http_lifecycle/main.go.wasm") 209 | local http = require "resty.http" 210 | local uri = "http://127.0.0.1:" .. ngx.var.server_port 211 | .. "/hit" 212 | for i = 1, 2 do 213 | do 214 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 215 | package.loaded.ctx = ctx 216 | local httpc = http.new() 217 | local res, err = httpc:request_uri(uri) 218 | if not res then 219 | ngx.log(ngx.ERR, err) 220 | return 221 | end 222 | package.loaded.ctx = nil 223 | end 224 | collectgarbage() 225 | end 226 | } 227 | } 228 | --- grep_error_log eval 229 | qr/(create|free) (plugin|http) context \d+/ 230 | --- grep_error_log_out eval 231 | qr/create plugin context 1 232 | create http context 2 233 | free http context 2 234 | free plugin context 1 235 | create plugin context 1 236 | create http context 3 237 | free http context 3 238 | free plugin context 1 239 | / 240 | -------------------------------------------------------------------------------- /t/http_req_body.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: sanity 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 27 | local ctx = assert(wasm.on_configure(plugin, '{}')) 28 | assert(wasm.on_http_request_body(ctx, "1234", false)) 29 | assert(wasm.on_http_request_body(ctx, "12345", true)) 30 | } 31 | } 32 | --- grep_error_log eval 33 | qr/body size \d+, end of stream \w+/ 34 | --- grep_error_log_out 35 | body size 4, end of stream false 36 | body size 5, end of stream true 37 | 38 | 39 | 40 | === TEST 2: no body 41 | --- config 42 | location /t { 43 | content_by_lua_block { 44 | local wasm = require("resty.proxy-wasm") 45 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 46 | local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 47 | assert(wasm.on_http_request_body(ctx, "", true)) 48 | } 49 | } 50 | --- grep_error_log eval 51 | qr/body size \d+, end of stream \w+/ 52 | --- grep_error_log_out 53 | body size 0, end of stream true 54 | --- error_log 55 | get body err: error status returned by host: not found 56 | 57 | 58 | 59 | === TEST 3: get body 60 | --- config 61 | location /t { 62 | content_by_lua_block { 63 | local json = require("cjson") 64 | local wasm = require("resty.proxy-wasm") 65 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 66 | local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 67 | local conf = {action = "offset"} 68 | for _, e in ipairs({ 69 | {0, 2}, 70 | {1, 1}, 71 | {1, 2}, 72 | {0, 10}, 73 | {1, 9}, 74 | {8, 2}, 75 | {9, 1}, 76 | {1, 10}, 77 | }) do 78 | conf.start = e[1] 79 | conf.size = e[2] 80 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 81 | assert(wasm.on_http_request_body(ctx, "helloworld", true)) 82 | end 83 | } 84 | } 85 | --- grep_error_log eval 86 | qr/get body \[\w+\]/ 87 | --- grep_error_log_out 88 | get body [he] 89 | get body [e] 90 | get body [el] 91 | get body [helloworld] 92 | get body [elloworld] 93 | get body [ld] 94 | get body [d] 95 | get body [elloworld] 96 | 97 | 98 | 99 | === TEST 4: get body, bad cases 100 | --- config 101 | location /t { 102 | content_by_lua_block { 103 | local json = require("cjson") 104 | local wasm = require("resty.proxy-wasm") 105 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 106 | local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 107 | local conf = {action = "offset"} 108 | for _, e in ipairs({ 109 | {-1, 2}, 110 | {1, 0}, 111 | {1, -1}, 112 | {10, 2}, 113 | }) do 114 | conf.start = e[1] 115 | conf.size = e[2] 116 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 117 | assert(wasm.on_http_request_body(ctx, "helloworld", true)) 118 | end 119 | } 120 | } 121 | --- error_log 122 | [error] 123 | --- grep_error_log eval 124 | qr/error status returned by host: bad argument/ 125 | --- grep_error_log_out eval 126 | "error status returned by host: bad argument\n" x 4 127 | 128 | 129 | 130 | === TEST 5: callback and local response 131 | --- config 132 | location /t { 133 | content_by_lua_block { 134 | local wasm = require("resty.proxy-wasm") 135 | local json = require("cjson") 136 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 137 | local conf = {host = "127.0.0.1:1980", action = "callback", path = "/?body=helloworld"} 138 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 139 | assert(wasm.on_http_request_body(ctx, "", true)) 140 | } 141 | } 142 | --- error_code: 503 143 | 144 | 145 | 146 | === TEST 6: ensure body is flushed after callback 147 | --- config 148 | location /t { 149 | content_by_lua_block { 150 | local wasm = require("resty.proxy-wasm") 151 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 152 | local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 153 | assert(wasm.on_http_request_body(ctx, "AAA", false)) 154 | assert(wasm.on_http_request_body(ctx, "", true)) 155 | } 156 | } 157 | --- grep_error_log eval 158 | qr/body size \d+, end of stream \w+/ 159 | --- grep_error_log_out 160 | body size 3, end of stream false 161 | body size 0, end of stream true 162 | --- error_log 163 | get body err: error status returned by host: not found 164 | -------------------------------------------------------------------------------- /t/http_resp_body.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: sanity 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 27 | ngx.ctx.ctx = assert(wasm.on_configure(plugin, '{}')) 28 | 29 | ngx.print("1234") 30 | ngx.print("12345") 31 | } 32 | body_filter_by_lua_block { 33 | local wasm = require("resty.proxy-wasm") 34 | ngx.log(ngx.WARN, ngx.arg[1], " ", ngx.arg[2]) 35 | local ctx = ngx.ctx.ctx 36 | assert(wasm.on_http_response_body(ctx)) 37 | } 38 | } 39 | --- grep_error_log eval 40 | qr/body size \d+, end of stream \w+/ 41 | --- grep_error_log_out 42 | body size 4, end of stream false 43 | body size 5, end of stream false 44 | body size 0, end of stream true 45 | 46 | 47 | 48 | === TEST 2: flush 49 | --- config 50 | location /t { 51 | content_by_lua_block { 52 | local wasm = require("resty.proxy-wasm") 53 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 54 | ngx.ctx.ctx = assert(wasm.on_configure(plugin, '{}')) 55 | 56 | ngx.print("1234") 57 | ngx.flush() 58 | ngx.print("12345") 59 | } 60 | body_filter_by_lua_block { 61 | local wasm = require("resty.proxy-wasm") 62 | local ctx = ngx.ctx.ctx 63 | assert(wasm.on_http_response_body(ctx)) 64 | } 65 | } 66 | --- grep_error_log eval 67 | qr/body size \d+, end of stream \w+/ 68 | --- grep_error_log_out 69 | body size 4, end of stream false 70 | body size 0, end of stream false 71 | body size 5, end of stream false 72 | body size 0, end of stream true 73 | 74 | 75 | 76 | === TEST 3: no body 77 | --- config 78 | location /t { 79 | content_by_lua_block { 80 | local wasm = require("resty.proxy-wasm") 81 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 82 | ngx.ctx.ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 83 | } 84 | body_filter_by_lua_block { 85 | local wasm = require("resty.proxy-wasm") 86 | local ctx = ngx.ctx.ctx 87 | assert(wasm.on_http_response_body(ctx)) 88 | } 89 | } 90 | --- grep_error_log eval 91 | qr/body size \d+, end of stream \w+/ 92 | --- grep_error_log_out 93 | body size 0, end of stream true 94 | --- error_log 95 | get body err: error status returned by host: not found 96 | 97 | 98 | 99 | === TEST 4: get body 100 | --- config 101 | location /t { 102 | return 200 "helloworld"; 103 | body_filter_by_lua_block { 104 | if not ngx.arg[2] then 105 | return 106 | end 107 | 108 | local json = require("cjson") 109 | local wasm = require("resty.proxy-wasm") 110 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 111 | local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 112 | local conf = {action = "offset"} 113 | for _, e in ipairs({ 114 | {0, 2}, 115 | {1, 1}, 116 | {1, 2}, 117 | {0, 10}, 118 | {1, 9}, 119 | {8, 2}, 120 | {9, 1}, 121 | {1, 10}, 122 | }) do 123 | conf.start = e[1] 124 | conf.size = e[2] 125 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 126 | ngx.arg[1] = "helloworld" 127 | assert(wasm.on_http_response_body(ctx)) 128 | end 129 | } 130 | } 131 | --- grep_error_log eval 132 | qr/get body \[\w+\]/ 133 | --- grep_error_log_out 134 | get body [he] 135 | get body [e] 136 | get body [el] 137 | get body [helloworld] 138 | get body [elloworld] 139 | get body [ld] 140 | get body [d] 141 | get body [elloworld] 142 | 143 | 144 | 145 | === TEST 5: get body, bad cases 146 | --- config 147 | location /t { 148 | return 200 "helloworld"; 149 | body_filter_by_lua_block { 150 | if not ngx.arg[2] then 151 | return 152 | end 153 | 154 | local json = require("cjson") 155 | local wasm = require("resty.proxy-wasm") 156 | local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm")) 157 | local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}')) 158 | local conf = {action = "offset"} 159 | for _, e in ipairs({ 160 | {-1, 2}, 161 | {1, 0}, 162 | {1, -1}, 163 | {10, 2}, 164 | }) do 165 | conf.start = e[1] 166 | conf.size = e[2] 167 | local ctx = assert(wasm.on_configure(plugin, json.encode(conf))) 168 | ngx.arg[1] = "helloworld" 169 | assert(wasm.on_http_response_body(ctx)) 170 | end 171 | } 172 | } 173 | --- error_log 174 | [error] 175 | --- grep_error_log eval 176 | qr/error status returned by host: bad argument/ 177 | --- grep_error_log_out eval 178 | "error status returned by host: bad argument\n" x 4 179 | -------------------------------------------------------------------------------- /t/http_send_response.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: sanity 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | local plugin = assert(wasm.load("plugin", "t/testdata/http_send_response/main.go.wasm")) 27 | local ctx = assert(wasm.on_configure(plugin, '403_body')) 28 | assert(wasm.on_http_request_headers(ctx)) 29 | } 30 | } 31 | --- error_code: 403 32 | --- response_body chomp 33 | should not pass 34 | --- response_headers 35 | powered-by: proxy-wasm-go-sdk!! 36 | 37 | 38 | 39 | === TEST 2: without body 40 | --- config 41 | location /t { 42 | access_by_lua_block { 43 | local wasm = require("resty.proxy-wasm") 44 | local plugin = assert(wasm.load("plugin", "t/testdata/http_send_response/main.go.wasm")) 45 | local ctx = assert(wasm.on_configure(plugin, '502')) 46 | assert(wasm.on_http_request_headers(ctx)) 47 | } 48 | content_by_lua_block { 49 | ngx.log(ngx.ERR, "should not reach") 50 | } 51 | } 52 | --- error_code: 502 53 | --- response_body_like eval 54 | qr/502 Bad Gateway<\/title>/ 55 | 56 | 57 | 58 | === TEST 3: multi headers 59 | --- config 60 | location /t { 61 | content_by_lua_block { 62 | local wasm = require("resty.proxy-wasm") 63 | local plugin = assert(wasm.load("plugin", "t/testdata/http_send_response/main.go.wasm")) 64 | local ctx = assert(wasm.on_configure(plugin, 'multi_hdrs')) 65 | assert(wasm.on_http_request_headers(ctx)) 66 | } 67 | } 68 | --- error_code: 401 69 | --- response_headers 70 | hi: spacewander 71 | hello: spacewander, world 72 | -------------------------------------------------------------------------------- /t/hup.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | BEGIN { 16 | $ENV{TEST_NGINX_USE_HUP} = 1; 17 | } 18 | 19 | use t::WASM 'no_plan'; 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: sanity 26 | --- http_config 27 | init_worker_by_lua_block { 28 | local wasm = require("resty.proxy-wasm") 29 | local p = wasm.load("plugin", "t/testdata/log/main.go.wasm") 30 | wasm.on_configure(p, "blah") 31 | } 32 | --- grep_error_log eval 33 | qr/\[\w+\] \d+#\d+: ouch, something is wrong/ 34 | --- grep_error_log_out eval 35 | qr/\[emerg\] \d+#\d+: ouch, something is wrong 36 | \[error\] \d+#\d+: ouch, something is wrong 37 | \[warn\] \d+#\d+: ouch, something is wrong 38 | \[info\] \d+#\d+: ouch, something is wrong 39 | / 40 | --- config 41 | location /t { 42 | return 200; 43 | } 44 | --- no_error_log 45 | [alert] 46 | 47 | 48 | 49 | === TEST 2: after receiving SIGHUP 50 | --- http_config 51 | init_worker_by_lua_block { 52 | local wasm = require("resty.proxy-wasm") 53 | local p = wasm.load("plugin", "t/testdata/log/main.go.wasm") 54 | wasm.on_configure(p, "blah") 55 | } 56 | --- grep_error_log eval 57 | qr/\[\w+\] \d+#\d+: ouch, something is wrong/ 58 | --- grep_error_log_out eval 59 | qr/\[emerg\] \d+#\d+: ouch, something is wrong 60 | \[error\] \d+#\d+: ouch, something is wrong 61 | \[warn\] \d+#\d+: ouch, something is wrong 62 | \[info\] \d+#\d+: ouch, something is wrong 63 | / 64 | --- config 65 | location /t { 66 | return 200; 67 | } 68 | --- no_error_log 69 | [alert] 70 | -------------------------------------------------------------------------------- /t/log.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: sanity 22 | --- log_level: debug 23 | --- config 24 | location /t { 25 | content_by_lua_block { 26 | local wasm = require("resty.proxy-wasm") 27 | local p = wasm.load("plugin", "t/testdata/log/main.go.wasm") 28 | wasm.on_configure(p, "blah") 29 | } 30 | } 31 | --- grep_error_log eval 32 | qr/\[\w+\] \d+#\d+: ouch, something is wrong/ 33 | --- grep_error_log_out eval 34 | qr/\[emerg\] \d+#\d+: ouch, something is wrong 35 | \[error\] \d+#\d+: ouch, something is wrong 36 | \[warn\] \d+#\d+: ouch, something is wrong 37 | \[info\] \d+#\d+: ouch, something is wrong 38 | \[debug\] \d+#\d+: ouch, something is wrong 39 | \[debug\] \d+#\d+: ouch, something is wrong 40 | / 41 | --- no_error_log 42 | [alert] 43 | 44 | 45 | 46 | === TEST 2: log in http request 47 | --- config 48 | location /t { 49 | content_by_lua_block { 50 | local wasm = require("resty.proxy-wasm") 51 | local plugin = assert(wasm.load("plugin", "t/testdata/log/main.go.wasm")) 52 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 53 | assert(wasm.on_http_request_headers(ctx)) 54 | } 55 | } 56 | --- grep_error_log eval 57 | qr/(create|run) http ctx \d+, client/ 58 | --- grep_error_log_out 59 | create http ctx 2, client 60 | run http ctx 2, client 61 | --- no_error_log 62 | [alert] 63 | -------------------------------------------------------------------------------- /t/metric.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | log_level('debug'); 19 | 20 | 21 | __DATA__ 22 | 23 | === TEST 1: load metric plugin 24 | --- 25 | --- config 26 | location /t { 27 | content_by_lua_block { 28 | local wasm = require("resty.proxy-wasm") 29 | local plugin = assert(wasm.load("plugin", "t/testdata/metric/main.go.wasm")) 30 | local ctx = assert(wasm.on_configure(plugin, '{}')) 31 | assert(wasm.on_http_request_headers(ctx)) 32 | } 33 | } 34 | --- request 35 | GET /t 36 | --- error_log 37 | Record success 38 | -------------------------------------------------------------------------------- /t/plugin_lifecycle.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: load 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | assert(wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm")) 27 | } 28 | } 29 | 30 | 31 | 32 | === TEST 2: load repeatedly 33 | --- config 34 | location /t { 35 | content_by_lua_block { 36 | local wasm = require("resty.proxy-wasm") 37 | for i = 1, 3 do 38 | assert(wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm")) 39 | end 40 | } 41 | } 42 | 43 | 44 | 45 | === TEST 3: on_configure 46 | --- config 47 | location /t { 48 | content_by_lua_block { 49 | local wasm = require("resty.proxy-wasm") 50 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 51 | assert(wasm.on_configure(plugin, '{"body":512}')) 52 | } 53 | } 54 | --- grep_error_log eval 55 | qr/writeFile failed/ 56 | --- grep_error_log_out 57 | writeFile failed 58 | --- error_log 59 | plugin config: {"body":512} 60 | 61 | 62 | 63 | === TEST 4: on_configure, repeatedly 64 | --- config 65 | location /t { 66 | content_by_lua_block { 67 | local wasm = require("resty.proxy-wasm") 68 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 69 | local manager = { 70 | plugin = plugin, 71 | ctxs = {} 72 | } 73 | for i = 1, 3 do 74 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 75 | table.insert(manager.ctxs, ctx) 76 | end 77 | } 78 | } 79 | --- grep_error_log eval 80 | qr/proxy_on_configure \d+/ 81 | --- grep_error_log_out eval 82 | qr/proxy_on_configure 1 83 | proxy_on_configure 2 84 | proxy_on_configure 3 85 | / 86 | --- shutdown_error_log eval 87 | qr/proxy_on_done [123]/ 88 | 89 | 90 | 91 | === TEST 5: reuse plugin ctx id 92 | --- config 93 | location /t { 94 | content_by_lua_block { 95 | local wasm = require("resty.proxy-wasm") 96 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 97 | local ref 98 | for i = 1, 2 do 99 | do 100 | for j = 1, 2 do 101 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 102 | end 103 | if i == 1 then 104 | ref = assert(wasm.on_configure(plugin, '{"body":512}')) 105 | end 106 | 107 | collectgarbage() 108 | end 109 | end 110 | } 111 | } 112 | --- grep_error_log eval 113 | qr/proxy_on_(configure|done) \d+/ 114 | --- grep_error_log_out eval 115 | qr/proxy_on_configure 1 116 | proxy_on_configure 2 117 | proxy_on_configure 3 118 | proxy_on_done [12] 119 | proxy_on_done [12] 120 | proxy_on_configure [12] 121 | proxy_on_configure [12] 122 | / 123 | --- shutdown_error_log 124 | proxy_on_done 3 125 | 126 | 127 | 128 | === TEST 6: free plugin after all ctxs are gone 129 | --- config 130 | location /t { 131 | content_by_lua_block { 132 | local wasm = require("resty.proxy-wasm") 133 | local ref 134 | do 135 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 136 | for i = 1, 2 do 137 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 138 | end 139 | ref = assert(wasm.on_configure(plugin, '{"body":512}')) 140 | 141 | collectgarbage() 142 | end 143 | } 144 | } 145 | --- grep_error_log eval 146 | qr/proxy_on_(configure|done) \d+/ 147 | --- grep_error_log_out eval 148 | qr/proxy_on_configure 1 149 | proxy_on_configure 2 150 | proxy_on_configure 3 151 | proxy_on_done [12] 152 | proxy_on_done [12] 153 | / 154 | --- shutdown_error_log 155 | proxy_on_done 3 156 | unloaded wasm plugin 157 | 158 | 159 | 160 | === TEST 7: plugin ctx id counts separately 161 | --- config 162 | location /t { 163 | content_by_lua_block { 164 | local wasm = require("resty.proxy-wasm") 165 | for i = 1, 2 do 166 | do 167 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 168 | for j = 1, 2 do 169 | local ctx = assert(wasm.on_configure(plugin, '{"body":512}')) 170 | end 171 | end 172 | collectgarbage() 173 | end 174 | } 175 | } 176 | --- grep_error_log eval 177 | qr/(proxy_on_(configure|done) \d+|unloaded wasm plugin)/ 178 | --- grep_error_log_out eval 179 | qr/proxy_on_configure 1 180 | proxy_on_configure 2 181 | proxy_on_done [12] 182 | proxy_on_done [12] 183 | unloaded wasm plugin 184 | proxy_on_configure 1 185 | proxy_on_configure 2 186 | proxy_on_done [12] 187 | proxy_on_done [12] 188 | unloaded wasm plugin 189 | / 190 | 191 | 192 | 193 | === TEST 8: delete ctx even failed to done 194 | --- config 195 | location /t { 196 | content_by_lua_block { 197 | local wasm = require("resty.proxy-wasm") 198 | local ctx 199 | do 200 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 201 | ctx = assert(wasm.on_configure(plugin, 'failed in proxy_on_done')) 202 | collectgarbage() 203 | end 204 | } 205 | } 206 | --- shutdown_error_log 207 | failed to mark context 1 as done 208 | 209 | 210 | 211 | === TEST 9: empty configure 212 | --- config 213 | location /t { 214 | content_by_lua_block { 215 | local wasm = require("resty.proxy-wasm") 216 | local plugin = wasm.load("plugin", "t/testdata/plugin_lifecycle/main.go.wasm") 217 | local _, err = wasm.on_configure(plugin, '') 218 | ngx.say(err) 219 | } 220 | } 221 | --- response_body 222 | bad conf 223 | -------------------------------------------------------------------------------- /t/property.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: get_property (host) 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local test_cases = { 26 | "plugin_root_id", "scheme", "host", "uri", "arg_test", "request_uri", 27 | } 28 | 29 | local wasm = require("resty.proxy-wasm") 30 | local plugin = wasm.load("plugin", "t/testdata/property/get.go.wasm") 31 | for _, case in ipairs(test_cases) do 32 | local plugin_ctx, err = wasm.on_configure(plugin, case) 33 | assert(wasm.on_http_request_headers(plugin_ctx)) 34 | end 35 | } 36 | } 37 | --- request 38 | GET /t?test=yeah 39 | --- error_log 40 | get property: plugin_root_id = plugin 41 | get property: scheme = http 42 | get property: host = localhost 43 | get property: uri = /t 44 | get property: arg_test = yeah 45 | get property: request_uri = /t?test=yeah 46 | 47 | 48 | 49 | === TEST 2: get_property (missing) 50 | --- config 51 | location /t { 52 | content_by_lua_block { 53 | local wasm = require("resty.proxy-wasm") 54 | local plugin = wasm.load("plugin", "t/testdata/property/get.go.wasm") 55 | local plugin_ctx, err = wasm.on_configure(plugin, "none") 56 | assert(wasm.on_http_request_headers(plugin_ctx)) 57 | } 58 | } 59 | --- request 60 | GET /t?test=yeah 61 | --- error_log 62 | error get property: error status returned by host: not found 63 | 64 | 65 | 66 | === TEST 3: set_property (custom variable: success) 67 | --- config 68 | location /t { 69 | set $for_test origin_value; 70 | content_by_lua_block { 71 | local wasm = require("resty.proxy-wasm") 72 | local plugin = wasm.load("plugin", "t/testdata/property/set.go.wasm") 73 | local plugin_ctx, err = wasm.on_configure(plugin, 'for_test|new_value') 74 | ngx.say(ngx.var.for_test) 75 | ngx.say(#ngx.var.for_test) 76 | assert(wasm.on_http_request_headers(plugin_ctx)) 77 | ngx.say(ngx.var.for_test) 78 | ngx.say(#ngx.var.for_test) 79 | } 80 | } 81 | --- response_body 82 | origin_value 83 | 12 84 | new_value 85 | 9 86 | --- error_log 87 | set property success: for_test = new_value 88 | 89 | 90 | 91 | === TEST 4: set_property (host: unchangeable) 92 | --- config 93 | location /t { 94 | content_by_lua_block { 95 | local wasm = require("resty.proxy-wasm") 96 | local plugin = wasm.load("plugin", "t/testdata/property/set.go.wasm") 97 | local plugin_ctx, err = wasm.on_configure(plugin, 'host|test.com') 98 | assert(wasm.on_http_request_headers(plugin_ctx)) 99 | } 100 | } 101 | --- error_log 102 | variable "host" not changeable 103 | 104 | 105 | 106 | === TEST 5: set_property (non-existent variable) 107 | --- config 108 | location /t { 109 | content_by_lua_block { 110 | local wasm = require("resty.proxy-wasm") 111 | local plugin = wasm.load("plugin", "t/testdata/property/set.go.wasm") 112 | local plugin_ctx, err = wasm.on_configure(plugin, 'other|new_value') 113 | assert(wasm.on_http_request_headers(plugin_ctx)) 114 | } 115 | } 116 | --- error_log 117 | variable "other" not found for writing 118 | error set property: error status returned by host: not found 119 | -------------------------------------------------------------------------------- /t/rust/fault-injection.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: fault injection 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | local plugin = assert(wasm.load("fault_injection", 27 | "t/testdata/rust/fault-injection/target/wasm32-wasi/debug/fault_injection.wasm")) 28 | local ctx = assert(wasm.on_configure(plugin, '{"http_status": 403, "body": "body"}')) 29 | assert(wasm.on_http_request_headers(ctx)) 30 | } 31 | } 32 | --- error_code: 403 33 | --- response_body chomp 34 | body 35 | 36 | 37 | 38 | === TEST 2: fault injection (without body) 39 | --- config 40 | location /t { 41 | content_by_lua_block { 42 | local wasm = require("resty.proxy-wasm") 43 | local plugin = assert(wasm.load("fault_injection", 44 | "t/testdata/rust/fault-injection/target/wasm32-wasi/debug/fault_injection.wasm")) 45 | local ctx = assert(wasm.on_configure(plugin, '{"http_status": 401}')) 46 | assert(wasm.on_http_request_headers(ctx)) 47 | } 48 | } 49 | --- error_code: 401 50 | --- response_body_like eval 51 | qr/<title>401 Authorization Required<\/title>/ 52 | 53 | 54 | 55 | === TEST 3: fault injection (0 percentage) 56 | --- config 57 | location /t { 58 | content_by_lua_block { 59 | local wasm = require("resty.proxy-wasm") 60 | local plugin = assert(wasm.load("fault_injection", 61 | "t/testdata/rust/fault-injection/target/wasm32-wasi/debug/fault_injection.wasm")) 62 | local ctx = assert(wasm.on_configure(plugin, '{"http_status": 401, "percentage": 0}')) 63 | assert(wasm.on_http_request_headers(ctx)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /t/rust/http-call.t: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | use t::WASM 'no_plan'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: get header 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local wasm = require("resty.proxy-wasm") 26 | local plugin = assert(wasm.load("fault_injection", 27 | "t/testdata/rust/http-call/target/wasm32-wasi/debug/http_call.wasm")) 28 | local ctx = assert(wasm.on_configure(plugin, '{}')) 29 | assert(wasm.on_http_request_headers(ctx)) 30 | } 31 | } 32 | --- response_headers 33 | resp-status: 200 34 | resp-content-type: text/plain 35 | --- error_code: 403 36 | -------------------------------------------------------------------------------- /t/testdata/README.md: -------------------------------------------------------------------------------- 1 | <!-- 2 | ~ Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | ~ 16 | --> 17 | Test data writtern in Go should be compiled with https://github.com/tetratelabs/proxy-wasm-go-sdk. 18 | -------------------------------------------------------------------------------- /t/testdata/assemblyscript/assembly/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | export * from "@solo-io/proxy-runtime/proxy"; // this exports the required functions for the proxy to interact with us. 18 | //this.setEffectiveContext(callback.origin_context_id); 19 | import { 20 | RootContext, Context, registerRootContext, log, 21 | LogLevelValues, FilterHeadersStatusValues, 22 | FilterTrailersStatusValues, GrpcStatusValues, 23 | send_local_response 24 | } from "@solo-io/proxy-runtime"; 25 | 26 | class FaultInjection extends RootContext { 27 | status: i32 = 403; 28 | body: string = ""; 29 | createContext(context_id: u32): Context { 30 | log(LogLevelValues.info, "FaultInjection createContext"); 31 | return new FaultInjector(context_id, this); 32 | } 33 | onConfigure(configuration_size: u32): bool { 34 | let ok = super.onConfigure(configuration_size); 35 | if (!ok) { 36 | return false; 37 | } 38 | this.body = this.configuration_; 39 | return true; 40 | } 41 | } 42 | 43 | class FaultInjector extends Context { 44 | constructor(context_id: u32, root_context: FaultInjection) { 45 | super(context_id, root_context); 46 | } 47 | 48 | onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { 49 | log(LogLevelValues.info, "onRequestHeaders called!"); 50 | let root_context = this.root_context as FaultInjection; 51 | log(LogLevelValues.info, root_context.body); 52 | send_local_response(root_context.status, "", String.UTF8.encode(root_context.body), [], GrpcStatusValues.Internal); 53 | 54 | return FilterHeadersStatusValues.StopIteration; 55 | } 56 | 57 | } 58 | 59 | registerRootContext((context_id: u32) => { 60 | return new FaultInjection(context_id); 61 | }, "FaultInjection"); 62 | -------------------------------------------------------------------------------- /t/testdata/assemblyscript/assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../node_modules/assemblyscript/std/assembly.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /t/testdata/assemblyscript/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | const fs = require("fs"); 18 | const loader = require("@assemblyscript/loader"); 19 | module.exports = loader.instantiateSync(fs.readFileSync(__dirname + "/build/optimized.wasm"), { /* imports */ }) 20 | -------------------------------------------------------------------------------- /t/testdata/assemblyscript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assemblyscript", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@solo-io/proxy-runtime": ">=0.1.12", 9 | "assemblyscript": "^0.14.9" 10 | }, 11 | "devDependencies": { 12 | "assemblyscript": "^0.14.9" 13 | } 14 | }, 15 | "node_modules/@solo-io/proxy-runtime": { 16 | "version": "0.1.12", 17 | "resolved": "https://registry.npmmirror.com/@solo-io/proxy-runtime/download/@solo-io/proxy-runtime-0.1.12.tgz", 18 | "integrity": "sha1-TnH4yJJ2JMSGyl5SrHCxKPgnoOE=" 19 | }, 20 | "node_modules/assemblyscript": { 21 | "version": "0.14.13", 22 | "resolved": "https://registry.npmmirror.com/assemblyscript/download/assemblyscript-0.14.13.tgz?cache=0&sync_timestamp=1632964240348&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fassemblyscript%2Fdownload%2Fassemblyscript-0.14.13.tgz", 23 | "integrity": "sha1-kbXF3WAR0nngZT95hG5YFhs/Vgc=", 24 | "dev": true, 25 | "dependencies": { 26 | "binaryen": "97.0.0-nightly.20200929", 27 | "long": "^4.0.0" 28 | }, 29 | "bin": { 30 | "asc": "bin/asc", 31 | "asinit": "bin/asinit" 32 | } 33 | }, 34 | "node_modules/binaryen": { 35 | "version": "97.0.0-nightly.20200929", 36 | "resolved": "https://registry.npmmirror.com/binaryen/download/binaryen-97.0.0-nightly.20200929.tgz", 37 | "integrity": "sha1-zuDPL4r0+tmQ1Wyj9X7DRQLz6XQ=", 38 | "dev": true, 39 | "bin": { 40 | "wasm-opt": "bin/wasm-opt" 41 | } 42 | }, 43 | "node_modules/long": { 44 | "version": "4.0.0", 45 | "resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz", 46 | "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", 47 | "dev": true 48 | } 49 | }, 50 | "dependencies": { 51 | "@solo-io/proxy-runtime": { 52 | "version": "0.1.12", 53 | "resolved": "https://registry.npmmirror.com/@solo-io/proxy-runtime/download/@solo-io/proxy-runtime-0.1.12.tgz", 54 | "integrity": "sha1-TnH4yJJ2JMSGyl5SrHCxKPgnoOE=" 55 | }, 56 | "assemblyscript": { 57 | "version": "0.14.13", 58 | "resolved": "https://registry.npmmirror.com/assemblyscript/download/assemblyscript-0.14.13.tgz?cache=0&sync_timestamp=1632964240348&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fassemblyscript%2Fdownload%2Fassemblyscript-0.14.13.tgz", 59 | "integrity": "sha1-kbXF3WAR0nngZT95hG5YFhs/Vgc=", 60 | "dev": true, 61 | "requires": { 62 | "binaryen": "97.0.0-nightly.20200929", 63 | "long": "^4.0.0" 64 | } 65 | }, 66 | "binaryen": { 67 | "version": "97.0.0-nightly.20200929", 68 | "resolved": "https://registry.npmmirror.com/binaryen/download/binaryen-97.0.0-nightly.20200929.tgz", 69 | "integrity": "sha1-zuDPL4r0+tmQ1Wyj9X7DRQLz6XQ=", 70 | "dev": true 71 | }, 72 | "long": { 73 | "version": "4.0.0", 74 | "resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz", 75 | "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", 76 | "dev": true 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /t/testdata/assemblyscript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --use abort=abort_proc_exit --sourceMap --debug", 4 | "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --use abort=abort_proc_exit --sourceMap --optimize", 5 | "asbuild": "npm run asbuild:untouched" 6 | }, 7 | "dependencies": { 8 | "@solo-io/proxy-runtime": ">=0.1.12", 9 | "assemblyscript": "^0.14.9" 10 | }, 11 | "devDependencies": { 12 | "assemblyscript": "^0.14.9" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /t/testdata/http_body/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | "github.com/valyala/fastjson" 21 | ) 22 | 23 | func main() { 24 | proxywasm.SetVMContext(&vmContext{}) 25 | } 26 | 27 | type vmContext struct { 28 | types.DefaultVMContext 29 | } 30 | 31 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 32 | return &pluginContext{} 33 | } 34 | 35 | type pluginContext struct { 36 | types.DefaultPluginContext 37 | } 38 | 39 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 40 | return &httpContext{contextID: contextID} 41 | } 42 | 43 | type httpContext struct { 44 | types.DefaultHttpContext 45 | contextID uint32 46 | } 47 | 48 | func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action { 49 | data, err := proxywasm.GetPluginConfiguration() 50 | if err != nil { 51 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 52 | return types.ActionContinue 53 | } 54 | 55 | proxywasm.LogWarnf("body size %d, end of stream %v", bodySize, endOfStream) 56 | 57 | var action string 58 | var conf *fastjson.Value 59 | var p fastjson.Parser 60 | conf, err = p.ParseBytes(data) 61 | if err != nil { 62 | proxywasm.LogErrorf("error decoding plugin configuration: %v", err) 63 | return types.ActionContinue 64 | } 65 | 66 | action = string(conf.GetStringBytes("action")) 67 | 68 | switch action { 69 | case "offset": 70 | start := conf.GetInt("start") 71 | size := conf.GetInt("size") 72 | body, err := proxywasm.GetHttpRequestBody(start, size) 73 | if err != nil { 74 | proxywasm.LogErrorf("get body err: %v", err) 75 | return types.ActionContinue 76 | } 77 | proxywasm.LogWarnf("get body [%s]", string(body)) 78 | 79 | case "callback": 80 | host := conf.GetStringBytes("host") 81 | path := conf.GetStringBytes("path") 82 | callback := func(numHeaders int, bodySize int, numTrailers int) { 83 | if err := proxywasm.SendHttpResponse(503, nil, nil, -1); err != nil { 84 | proxywasm.LogErrorf("send http failed: %v", err) 85 | } 86 | } 87 | _, err := proxywasm.DispatchHttpCall(string(host), [][2]string{{":path", string(path)}}, 88 | []byte(""), nil, 5000, callback) 89 | if err != nil { 90 | proxywasm.LogErrorf("httpcall failed: %v", err) 91 | } 92 | } 93 | 94 | return types.ActionContinue 95 | } 96 | 97 | func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action { 98 | data, err := proxywasm.GetPluginConfiguration() 99 | if err != nil { 100 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 101 | return types.ActionContinue 102 | } 103 | 104 | proxywasm.LogWarnf("body size %d, end of stream %v", bodySize, endOfStream) 105 | 106 | var action string 107 | var conf *fastjson.Value 108 | var p fastjson.Parser 109 | conf, err = p.ParseBytes(data) 110 | if err != nil { 111 | proxywasm.LogErrorf("error decoding plugin configuration: %v", err) 112 | return types.ActionContinue 113 | } 114 | 115 | action = string(conf.GetStringBytes("action")) 116 | 117 | switch action { 118 | case "offset": 119 | if !endOfStream { 120 | return types.ActionContinue 121 | } 122 | start := conf.GetInt("start") 123 | size := conf.GetInt("size") 124 | body, err := proxywasm.GetHttpRequestBody(start, size) 125 | if err != nil { 126 | proxywasm.LogErrorf("get body err: %v", err) 127 | return types.ActionContinue 128 | } 129 | proxywasm.LogWarnf("get body [%s]", string(body)) 130 | } 131 | 132 | return types.ActionContinue 133 | } 134 | -------------------------------------------------------------------------------- /t/testdata/http_call/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "sort" 19 | 20 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 21 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 22 | "github.com/valyala/fastjson" 23 | ) 24 | 25 | func main() { 26 | proxywasm.SetVMContext(&vmContext{}) 27 | } 28 | 29 | type vmContext struct { 30 | types.DefaultVMContext 31 | } 32 | 33 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 34 | return &pluginContext{contextID: contextID} 35 | } 36 | 37 | type pluginContext struct { 38 | types.DefaultPluginContext 39 | contextID uint32 40 | } 41 | 42 | func (pluginCtx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 43 | ctx := &httpContext{contextID: contextID, pluginCtxID: pluginCtx.contextID} 44 | ctx.callback = func(numHeaders, bodySize, numTrailers int) { 45 | proxywasm.LogInfof("called for contextID = %d", ctx.contextID) 46 | } 47 | return ctx 48 | } 49 | 50 | type httpContext struct { 51 | types.DefaultHttpContext 52 | contextID uint32 53 | pluginCtxID uint32 54 | callback func(numHeaders, bodySize, numTrailers int) 55 | } 56 | 57 | func (ctx *httpContext) dispatchHttpCall(elem *fastjson.Value) { 58 | host := elem.GetStringBytes("host") 59 | path := elem.GetStringBytes("path") 60 | method := elem.GetStringBytes("method") 61 | scheme := elem.GetStringBytes("scheme") 62 | headers := elem.GetArray("headers") 63 | body := elem.GetStringBytes("body") 64 | 65 | timeout := uint32(elem.GetInt("timeout")) 66 | if timeout == 0 { 67 | timeout = 5000 68 | } 69 | 70 | hs := [][2]string{} 71 | if len(path) > 0 { 72 | hs = append(hs, [2]string{":path", string(path)}) 73 | } 74 | if len(method) > 0 { 75 | hs = append(hs, [2]string{":method", string(method)}) 76 | } 77 | if len(scheme) > 0 { 78 | hs = append(hs, [2]string{":scheme", string(scheme)}) 79 | } 80 | if len(headers) > 0 { 81 | for _, e := range headers { 82 | kv := e.GetArray() 83 | k := string(kv[0].GetStringBytes()) 84 | v := string(kv[1].GetStringBytes()) 85 | 86 | proxywasm.LogInfof("with header %s: %s", k, v) 87 | 88 | hs = append(hs, [2]string{k, v}) 89 | } 90 | } 91 | 92 | action := string(elem.GetStringBytes("action")) 93 | switch action { 94 | case "panic": 95 | ctx.callback = func(numHeaders int, bodySize int, numTrailers int) { 96 | panic("OUCH") 97 | } 98 | case "call_and_send": 99 | if err := proxywasm.SendHttpResponse(503, nil, nil, -1); err != nil { 100 | proxywasm.LogErrorf("send http failed: %v", err) 101 | } 102 | case "headers": 103 | ctx.callback = func(numHeaders int, bodySize int, numTrailers int) { 104 | proxywasm.LogWarnf("get numHeaders %d", numHeaders) 105 | hs, err := proxywasm.GetHttpCallResponseHeaders() 106 | if err != nil { 107 | proxywasm.LogErrorf("callback err: %v", err) 108 | return 109 | } 110 | 111 | sort.Slice(hs, func(i, j int) bool { 112 | if hs[i][0] == hs[j][0] { 113 | return hs[i][1] < hs[j][1] 114 | } 115 | return hs[i][0] < hs[j][0] 116 | }) 117 | for _, h := range hs { 118 | proxywasm.LogWarnf("get header %s: %s", h[0], h[1]) 119 | } 120 | } 121 | case "body": 122 | ctx.callback = func(numHeaders int, bodySize int, numTrailers int) { 123 | proxywasm.LogWarnf("get bodySize %d", bodySize) 124 | start := elem.GetInt("start") 125 | size := elem.GetInt("size") 126 | body, err := proxywasm.GetHttpCallResponseBody(start, size) 127 | if err != nil { 128 | proxywasm.LogErrorf("callback err: %v", err) 129 | return 130 | } 131 | proxywasm.LogWarnf("get body [%s]", string(body)) 132 | } 133 | case "then_send": 134 | ctx.callback = func(numHeaders int, bodySize int, numTrailers int) { 135 | if err := proxywasm.SendHttpResponse(503, nil, nil, -1); err != nil { 136 | proxywasm.LogErrorf("send http failed: %v", err) 137 | } 138 | } 139 | case "then_call": 140 | ctx.callback = func(numHeaders int, bodySize int, numTrailers int) { 141 | cb := func(numHeaders int, bodySize int, numTrailers int) { 142 | if err := proxywasm.SendHttpResponse(403, nil, nil, -1); err != nil { 143 | proxywasm.LogErrorf("send http failed: %v", err) 144 | } 145 | } 146 | calloutID, err := proxywasm.DispatchHttpCall(string(host), hs, body, nil, 147 | timeout, cb) 148 | if err != nil { 149 | proxywasm.LogErrorf("httpcall failed: %v", err) 150 | return 151 | } 152 | proxywasm.LogInfof("httpcall calloutID %d, pluginCtxID %d", calloutID, ctx.pluginCtxID) 153 | } 154 | case "then_call_again": 155 | cb_builder := func(next func(numHeaders int, bodySize int, numTrailers int)) func(numHeaders int, bodySize int, numTrailers int) { 156 | return func(numHeaders int, bodySize int, numTrailers int) { 157 | calloutID, err := proxywasm.DispatchHttpCall(string(host), hs, body, nil, 158 | timeout, next) 159 | if err != nil { 160 | proxywasm.LogErrorf("httpcall failed: %v", err) 161 | return 162 | } 163 | proxywasm.LogInfof("httpcall calloutID %d, pluginCtxID %d", calloutID, ctx.pluginCtxID) 164 | } 165 | } 166 | cb := func(numHeaders int, bodySize int, numTrailers int) { 167 | if err := proxywasm.SendHttpResponse(401, nil, nil, -1); err != nil { 168 | proxywasm.LogErrorf("send http failed: %v", err) 169 | } 170 | } 171 | ctx.callback = cb_builder(cb_builder(cb)) 172 | } 173 | 174 | calloutID, err := proxywasm.DispatchHttpCall(string(host), hs, body, nil, 175 | timeout, ctx.callback) 176 | if err != nil { 177 | proxywasm.LogErrorf("httpcall failed: %v", err) 178 | return 179 | } 180 | proxywasm.LogInfof("httpcall calloutID %d, pluginCtxID %d", calloutID, ctx.pluginCtxID) 181 | } 182 | 183 | func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 184 | data, err := proxywasm.GetPluginConfiguration() 185 | if err != nil { 186 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 187 | return types.ActionContinue 188 | } 189 | 190 | var p fastjson.Parser 191 | v, err := p.ParseBytes(data) 192 | if err != nil { 193 | proxywasm.LogErrorf("error decoding plugin configuration: %v", err) 194 | return types.ActionContinue 195 | } 196 | 197 | if v.Type() == fastjson.TypeArray { 198 | arr := v.GetArray() 199 | for _, e := range arr { 200 | ctx.dispatchHttpCall(e) 201 | } 202 | 203 | } else { 204 | ctx.dispatchHttpCall(v) 205 | } 206 | 207 | return types.ActionContinue 208 | } 209 | 210 | func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { 211 | data, err := proxywasm.GetPluginConfiguration() 212 | if err != nil { 213 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 214 | return types.ActionContinue 215 | } 216 | 217 | action := string(data) 218 | if action == "call_in_resp" { 219 | hs := [][2]string{ 220 | {":method", "GET"}, {"host", "some_authority"}, 221 | {":path", "/path/to/service"}, {"accept", "*/*"}, 222 | } 223 | if _, err := proxywasm.DispatchHttpCall("web_service", hs, nil, nil, 224 | 5000, ctx.callback); err != nil { 225 | proxywasm.LogErrorf("httpcall failed: %v", err) 226 | } 227 | } 228 | 229 | return types.ActionContinue 230 | } 231 | -------------------------------------------------------------------------------- /t/testdata/http_header/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | "github.com/valyala/fastjson" 21 | ) 22 | 23 | func main() { 24 | proxywasm.SetVMContext(&vmContext{}) 25 | } 26 | 27 | type vmContext struct { 28 | types.DefaultVMContext 29 | } 30 | 31 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 32 | return &pluginContext{} 33 | } 34 | 35 | type pluginContext struct { 36 | types.DefaultPluginContext 37 | } 38 | 39 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 40 | return &httpContext{contextID: contextID} 41 | } 42 | 43 | type httpContext struct { 44 | types.DefaultHttpContext 45 | contextID uint32 46 | } 47 | 48 | func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 49 | data, err := proxywasm.GetPluginConfiguration() 50 | if err != nil { 51 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 52 | return types.ActionContinue 53 | } 54 | 55 | var action string 56 | var conf *fastjson.Value 57 | if data[0] == '{' { 58 | var p fastjson.Parser 59 | conf, err = p.ParseBytes(data) 60 | if err != nil { 61 | proxywasm.LogErrorf("error decoding plugin configuration: %v", err) 62 | return types.ActionContinue 63 | } 64 | 65 | action = string(conf.GetStringBytes("action")) 66 | 67 | } else { 68 | action = string(data) 69 | } 70 | 71 | switch action { 72 | case "req_hdr_get_all": 73 | hdrs, err := proxywasm.GetHttpRequestHeaders() 74 | if err != nil { 75 | proxywasm.LogErrorf("error getting headers: %v", err) 76 | return types.ActionContinue 77 | } 78 | 79 | for _, kv := range hdrs { 80 | proxywasm.LogWarnf("get request header: %v %v", kv[0], kv[1]) 81 | } 82 | 83 | case "req_hdr_get": 84 | res, err := proxywasm.GetHttpRequestHeader("X-API") 85 | if err != nil { 86 | proxywasm.LogErrorf("error get request header: %v", err) 87 | return types.ActionContinue 88 | } 89 | proxywasm.LogWarnf("get request header: %v", res) 90 | 91 | case "req_hdr_get_caseless": 92 | res, err := proxywasm.GetHttpRequestHeader("x-api") 93 | if err != nil { 94 | proxywasm.LogErrorf("error get request header: %v", err) 95 | return types.ActionContinue 96 | } 97 | proxywasm.LogWarnf("get request header: %v", res) 98 | 99 | case "req_hdr_get_abnormal": 100 | hdr := string(conf.GetStringBytes("header")) 101 | res, err := proxywasm.GetHttpRequestHeader(hdr) 102 | if err != nil { 103 | proxywasm.LogErrorf("error get request header: %v", err) 104 | return types.ActionContinue 105 | } 106 | proxywasm.LogWarnf("get request header: %v", res) 107 | 108 | case "req_hdr_set": 109 | proxywasm.ReplaceHttpRequestHeader("foo", "bar") 110 | 111 | case "req_hdr_add": 112 | proxywasm.AddHttpRequestHeader("foo", "bar") 113 | 114 | case "req_hdr_del": 115 | proxywasm.RemoveHttpRequestHeader("foo") 116 | 117 | case "req_hdr_set_abnormal": 118 | hdr := string(conf.GetStringBytes("header")) 119 | v := string(conf.GetStringBytes("value")) 120 | err := proxywasm.ReplaceHttpRequestHeader(hdr, v) 121 | if err != nil { 122 | proxywasm.LogErrorf("error set request header: %v", err) 123 | return types.ActionContinue 124 | } 125 | 126 | case "req_path_get": 127 | res, err := proxywasm.GetHttpRequestHeader(":path") 128 | if err != nil { 129 | proxywasm.LogErrorf("error get request path: %v", err) 130 | return types.ActionContinue 131 | } 132 | proxywasm.LogWarnf("get request path: %v", res) 133 | 134 | case "req_method_get": 135 | res, err := proxywasm.GetHttpRequestHeader(":method") 136 | if err != nil { 137 | proxywasm.LogErrorf("error get request method: %v", err) 138 | return types.ActionContinue 139 | } 140 | proxywasm.LogWarnf("get request method: %v", res) 141 | 142 | case "req_scheme_get": 143 | res, err := proxywasm.GetHttpRequestHeader(":scheme") 144 | if err != nil { 145 | proxywasm.LogErrorf("error get request scheme: %v", err) 146 | return types.ActionContinue 147 | } 148 | proxywasm.LogWarnf("get request scheme: %v", res) 149 | 150 | case "req_hdr_get_authority": 151 | authority, err := proxywasm.GetHttpRequestHeader(":authority") 152 | if err != nil { 153 | proxywasm.LogErrorf("error get request authority: %v", err) 154 | return types.ActionContinue 155 | } 156 | proxywasm.LogWarnf("get request authority: %v", authority) 157 | 158 | case "req_pseduo_del": 159 | proxywasm.RemoveHttpRequestHeader(":method") 160 | proxywasm.RemoveHttpRequestHeader(":path") 161 | 162 | case "req_path_set": 163 | val := string(conf.GetStringBytes("value")) 164 | proxywasm.ReplaceHttpRequestHeader(":path", val) 165 | 166 | case "req_method_set": 167 | val := string(conf.GetStringBytes("value")) 168 | proxywasm.ReplaceHttpRequestHeader(":method", val) 169 | 170 | } 171 | 172 | return types.ActionContinue 173 | } 174 | 175 | func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { 176 | data, err := proxywasm.GetPluginConfiguration() 177 | if err != nil { 178 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 179 | return types.ActionContinue 180 | } 181 | 182 | var action string 183 | var conf *fastjson.Value 184 | if data[0] == '{' { 185 | var p fastjson.Parser 186 | conf, err = p.ParseBytes(data) 187 | if err != nil { 188 | proxywasm.LogErrorf("error decoding plugin configuration: %v", err) 189 | return types.ActionContinue 190 | } 191 | 192 | action = string(conf.GetStringBytes("action")) 193 | 194 | } else { 195 | action = string(data) 196 | } 197 | 198 | if action == "resp_hdr_get_all" { 199 | hdrs, err := proxywasm.GetHttpResponseHeaders() 200 | if err != nil { 201 | proxywasm.LogErrorf("error getting headers: %v", err) 202 | return types.ActionContinue 203 | } 204 | 205 | for _, kv := range hdrs { 206 | proxywasm.LogWarnf("get response header %v %v", kv[0], kv[1]) 207 | } 208 | return types.ActionContinue 209 | } 210 | 211 | proxywasm.AddHttpResponseHeader("add", "foo") 212 | 213 | switch action { 214 | case "resp_hdr_add": 215 | proxywasm.AddHttpResponseHeader("add", "bar") 216 | case "resp_hdr_set": 217 | proxywasm.ReplaceHttpResponseHeader("add", "bar") 218 | case "resp_hdr_set_empty": 219 | proxywasm.ReplaceHttpResponseHeader("add", "") 220 | case "resp_hdr_del": 221 | proxywasm.RemoveHttpResponseHeader("add") 222 | case "resp_hdr_del_all": 223 | proxywasm.AddHttpResponseHeader("add", "bar") 224 | proxywasm.RemoveHttpResponseHeader("add") 225 | case "resp_hdr_del_miss": 226 | proxywasm.RemoveHttpResponseHeader("aa") 227 | case "resp_hdr_set_abnormal": 228 | hdr := string(conf.GetStringBytes("header")) 229 | v := string(conf.GetStringBytes("value")) 230 | err := proxywasm.ReplaceHttpResponseHeader(hdr, v) 231 | if err != nil { 232 | proxywasm.LogErrorf("error set request header: %v", err) 233 | return types.ActionContinue 234 | } 235 | 236 | case "resp_hdr_get": 237 | res, err := proxywasm.GetHttpResponseHeader("add") 238 | if err != nil { 239 | proxywasm.LogErrorf("error get response header: %v", err) 240 | return types.ActionContinue 241 | } 242 | proxywasm.LogWarnf("get response header: %v", res) 243 | case "resp_hdr_get_status": 244 | res, err := proxywasm.GetHttpResponseHeader(":status") 245 | if err != nil { 246 | proxywasm.LogErrorf("error get response header: %v", err) 247 | return types.ActionContinue 248 | } 249 | proxywasm.LogWarnf("get response header: :status %v", res) 250 | case "resp_hdr_get_miss": 251 | res, err := proxywasm.GetHttpResponseHeader("") 252 | if err != nil { 253 | proxywasm.LogErrorf("error get response header: %v", err) 254 | return types.ActionContinue 255 | } 256 | proxywasm.LogWarnf("get response header: [%v]", res) 257 | case "resp_hdr_get_first": 258 | proxywasm.AddHttpResponseHeader("add", "bar") 259 | res, err := proxywasm.GetHttpResponseHeader("add") 260 | if err != nil { 261 | proxywasm.LogErrorf("error get response header: %v", err) 262 | return types.ActionContinue 263 | } 264 | proxywasm.LogWarnf("get response header: %v", res) 265 | case "resp_hdr_get_abnormal": 266 | hdr := string(conf.GetStringBytes("header")) 267 | res, err := proxywasm.GetHttpResponseHeader(hdr) 268 | if err != nil { 269 | proxywasm.LogErrorf("error get response header: %v", err) 270 | return types.ActionContinue 271 | } 272 | proxywasm.LogWarnf("get response header: %v", res) 273 | 274 | case "resp_hdr_set_status": 275 | proxywasm.ReplaceHttpResponseHeader(":status", "501") 276 | case "resp_hdr_set_status_bad_val": 277 | proxywasm.ReplaceHttpResponseHeader(":status", "") 278 | case "resp_hdr_del_status": 279 | proxywasm.RemoveHttpResponseHeader(":status") 280 | } 281 | 282 | return types.ActionContinue 283 | } 284 | -------------------------------------------------------------------------------- /t/testdata/http_lifecycle/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | ) 21 | 22 | func main() { 23 | proxywasm.SetVMContext(&vmContext{}) 24 | } 25 | 26 | type vmContext struct { 27 | // Embed the default VM context here, 28 | // so that we don't need to reimplement all the methods. 29 | types.DefaultVMContext 30 | } 31 | 32 | // Override types.DefaultVMContext. 33 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 34 | return &pluginContext{} 35 | } 36 | 37 | type pluginContext struct { 38 | // Embed the default plugin context here, 39 | // so that we don't need to reimplement all the methods. 40 | types.DefaultPluginContext 41 | } 42 | 43 | // Override types.DefaultPluginContext. 44 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 45 | return &httpLifecycle{contextID: contextID} 46 | } 47 | 48 | type httpLifecycle struct { 49 | // Embed the default http context here, 50 | // so that we don't need to reimplement all the methods. 51 | types.DefaultHttpContext 52 | contextID uint32 53 | } 54 | 55 | func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 56 | data, err := proxywasm.GetPluginConfiguration() 57 | if err != nil { 58 | proxywasm.LogCriticalf("error reading plugin configuration: %v", err) 59 | return types.ActionContinue 60 | } 61 | 62 | proxywasm.LogWarnf("run http ctx %d with conf %s on http request headers", 63 | ctx.contextID, string(data)) 64 | return types.ActionContinue 65 | } 66 | 67 | func (ctx *httpLifecycle) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { 68 | data, err := proxywasm.GetPluginConfiguration() 69 | if err != nil { 70 | proxywasm.LogCriticalf("error reading plugin configuration: %v", err) 71 | return types.ActionContinue 72 | } 73 | 74 | action := string(data) 75 | 76 | if action == "panic_on_http_response_headers" { 77 | panic("OUCH") 78 | } 79 | 80 | proxywasm.LogWarnf("run http ctx %d with conf %s on http response headers", 81 | ctx.contextID, string(data)) 82 | return types.ActionContinue 83 | } 84 | -------------------------------------------------------------------------------- /t/testdata/http_send_response/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | ) 21 | 22 | func main() { 23 | proxywasm.SetVMContext(&vmContext{}) 24 | } 25 | 26 | type vmContext struct { 27 | // Embed the default VM context here, 28 | // so that we don't need to reimplement all the methods. 29 | types.DefaultVMContext 30 | } 31 | 32 | // Override types.DefaultVMContext. 33 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 34 | return &pluginContext{} 35 | } 36 | 37 | type pluginContext struct { 38 | // Embed the default plugin context here, 39 | // so that we don't need to reimplement all the methods. 40 | types.DefaultPluginContext 41 | } 42 | 43 | // Override types.DefaultPluginContext. 44 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 45 | return &httpContext{contextID: contextID} 46 | } 47 | 48 | type httpContext struct { 49 | // Embed the default http context here, 50 | // so that we don't need to reimplement all the methods. 51 | types.DefaultHttpContext 52 | contextID uint32 53 | } 54 | 55 | // Override types.DefaultHttpContext. 56 | func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 57 | data, err := proxywasm.GetPluginConfiguration() 58 | if err != nil { 59 | proxywasm.LogCriticalf("error reading plugin configuration: %v", err) 60 | return types.ActionContinue 61 | } 62 | 63 | action := string(data) 64 | 65 | if action == "403_body" { 66 | body := "should not pass" 67 | err = proxywasm.SendHttpResponse(403, [][2]string{ 68 | {"powered-by", "proxy-wasm-go-sdk!!"}, 69 | }, []byte(body), -1) 70 | } else if action == "502" { 71 | err = proxywasm.SendHttpResponse(502, nil, nil, -1) 72 | } else if action == "multi_hdrs" { 73 | err = proxywasm.SendHttpResponse(401, [][2]string{ 74 | {"hello", "spacewander"}, 75 | {"hi", "spacewander"}, 76 | {"hello", "world"}, 77 | }, nil, -1) 78 | } 79 | 80 | if err != nil { 81 | proxywasm.LogErrorf("failed to send local response: %v", err) 82 | return types.ActionContinue 83 | } 84 | return types.ActionPause 85 | } 86 | -------------------------------------------------------------------------------- /t/testdata/log/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | ) 21 | 22 | func main() { 23 | proxywasm.SetVMContext(&vmContext{}) 24 | } 25 | 26 | type vmContext struct { 27 | types.DefaultVMContext 28 | } 29 | 30 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 31 | return &pluginLifecycle{} 32 | } 33 | 34 | type pluginLifecycle struct { 35 | types.DefaultPluginContext 36 | contextID uint32 37 | } 38 | 39 | func (ctx *pluginLifecycle) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { 40 | proxywasm.LogCriticalf("%s", "ouch, something is wrong") 41 | proxywasm.LogError("ouch, something is wrong") 42 | proxywasm.LogWarn("ouch, something is wrong") 43 | proxywasm.LogInfof("ouch,%s", " something is wrong") 44 | proxywasm.LogDebug("ouch, something is wrong") 45 | proxywasm.LogTrace("ouch, something is wrong") 46 | return types.OnPluginStartStatusOK 47 | } 48 | 49 | func (*pluginLifecycle) NewHttpContext(contextID uint32) types.HttpContext { 50 | proxywasm.LogWarnf("create http ctx %d", contextID) 51 | return &httpLifecycle{contextID: contextID} 52 | } 53 | 54 | type httpLifecycle struct { 55 | types.DefaultHttpContext 56 | contextID uint32 57 | } 58 | 59 | func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 60 | proxywasm.LogWarnf("run http ctx %d", ctx.contextID) 61 | return types.ActionContinue 62 | } 63 | -------------------------------------------------------------------------------- /t/testdata/metric/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | ) 21 | 22 | func main() { 23 | proxywasm.SetVMContext(&vmContext{}) 24 | } 25 | 26 | type vmContext struct { 27 | types.DefaultVMContext 28 | } 29 | 30 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 31 | return &pluginContext{} 32 | } 33 | 34 | type pluginContext struct { 35 | types.DefaultPluginContext 36 | } 37 | 38 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 39 | return &httpContext{ 40 | contextID: contextID, 41 | metrics: NewMetrics(), 42 | } 43 | } 44 | 45 | type httpContext struct { 46 | types.DefaultHttpContext 47 | metrics *TestMetrics 48 | contextID uint32 49 | } 50 | 51 | // Override types.DefaultHttpContext. 52 | func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 53 | data, err := proxywasm.GetPluginConfiguration() 54 | if err != nil { 55 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 56 | return types.ActionContinue 57 | } 58 | 59 | proxywasm.LogWarnf("OnHttpRequestHeaders: %s", string(data)) 60 | 61 | ctx.metrics.incrementCounter("test") 62 | ctx.metrics.recordMetricHistogram("test") 63 | 64 | return types.ActionContinue 65 | } 66 | 67 | type TestMetrics struct { 68 | c map[string]proxywasm.MetricCounter 69 | h map[string]proxywasm.MetricHistogram 70 | } 71 | 72 | func NewMetrics() *TestMetrics { 73 | return &TestMetrics{ 74 | c: make(map[string]proxywasm.MetricCounter), 75 | h: make(map[string]proxywasm.MetricHistogram), 76 | } 77 | } 78 | 79 | // ProxyDefineMetric 80 | // ProxyIncrementMetric 81 | func (m *TestMetrics) incrementCounter(name string) { 82 | counter, ok := m.c[name] 83 | if !ok { 84 | counter = proxywasm.DefineCounterMetric(name) 85 | m.c[name] = counter 86 | } 87 | proxywasm.LogWarnf("DefineCounterMetric success: %s", name) 88 | 89 | counter.Increment(1) 90 | proxywasm.LogWarnf("Increment success: %s", name) 91 | 92 | } 93 | 94 | // ProxyGetMetric 95 | func (m *TestMetrics) getMetric(name string) uint64 { 96 | return m.c[name].Value() 97 | } 98 | 99 | func (m *TestMetrics) recordMetricHistogram(name string) { 100 | h, ok := m.h[name] 101 | if !ok { 102 | h = proxywasm.DefineHistogramMetric(name) 103 | m.h[name] = h 104 | } 105 | proxywasm.LogWarnf("DefineHistogramMetric success: %s", name) 106 | 107 | h.Record(uint64(1)) 108 | proxywasm.LogWarnf("Record success: %s", name) 109 | } 110 | -------------------------------------------------------------------------------- /t/testdata/plugin_lifecycle/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "log" 19 | "math/rand" 20 | "os" 21 | "time" 22 | 23 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 24 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 25 | ) 26 | 27 | const tickMilliseconds uint32 = 1000 28 | 29 | func main() { 30 | proxywasm.SetVMContext(&vmContext{}) 31 | } 32 | 33 | type vmContext struct { 34 | // Embed the default VM context here, 35 | // so that we don't need to reimplement all the methods. 36 | types.DefaultVMContext 37 | } 38 | 39 | // Override types.DefaultVMContext. 40 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 41 | return &pluginLifecycle{contextID: contextID} 42 | } 43 | 44 | type pluginLifecycle struct { 45 | // Embed the default plugin context here, 46 | // so that we don't need to reimplement all the methods. 47 | types.DefaultPluginContext 48 | contextID uint32 49 | action string 50 | } 51 | 52 | func writeFile(name string, data []byte, perm os.FileMode) error { 53 | f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 54 | if err != nil { 55 | return err 56 | } 57 | _, err = f.Write(data) 58 | if err1 := f.Close(); err1 != nil && err == nil { 59 | err = err1 60 | } 61 | return err 62 | } 63 | 64 | // Override types.DefaultPluginContext. 65 | func (ctx *pluginLifecycle) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { 66 | rand.Seed(time.Now().UnixNano()) 67 | 68 | data, err := proxywasm.GetPluginConfiguration() 69 | if err != nil { 70 | proxywasm.LogCriticalf("error reading plugin configuration: %v", err) 71 | return types.OnPluginStartStatusFailed 72 | } 73 | 74 | proxywasm.LogInfof("plugin config: %s", string(data)) 75 | 76 | ctx.action = string(data) 77 | 78 | // the HTTP support is TODO yet in tinygo: https://github.com/tinygo-org/tinygo/issues/1961 79 | //_, err := http.Get("http://www.baidu.com/robots.txt") 80 | 81 | err = writeFile("/tmp/x", []byte("Hello, World"), 0666) 82 | if err != nil { 83 | // should be ENOTCAPABLE in wasm's sandbox environment. For details, see: 84 | // https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md#executing-in-wasmtime-runtime 85 | log.Printf("writeFile failed\n") 86 | } 87 | 88 | proxywasm.LogWarnf("proxy_on_configure %d", ctx.contextID) 89 | return types.OnPluginStartStatusOK 90 | } 91 | 92 | func (ctx *pluginLifecycle) OnPluginDone() bool { 93 | proxywasm.LogWarnf("proxy_on_done %d", ctx.contextID) 94 | 95 | if ctx.action == "failed in proxy_on_done" { 96 | return false 97 | } 98 | return true 99 | } 100 | -------------------------------------------------------------------------------- /t/testdata/property/get.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 19 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 20 | ) 21 | 22 | func main() { 23 | proxywasm.SetVMContext(&vmContext{}) 24 | } 25 | 26 | type vmContext struct { 27 | // Embed the default VM context here, 28 | // so that we don't need to reimplement all the methods. 29 | types.DefaultVMContext 30 | } 31 | 32 | // Override types.DefaultVMContext. 33 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 34 | return &pluginContext{} 35 | } 36 | 37 | type pluginContext struct { 38 | // Embed the default plugin context here, 39 | // so that we don't need to reimplement all the methods. 40 | types.DefaultPluginContext 41 | } 42 | 43 | // Override types.DefaultPluginContext. 44 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 45 | return &httpLifecycle{contextID: contextID} 46 | } 47 | 48 | func (*pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { 49 | return true 50 | } 51 | 52 | type httpLifecycle struct { 53 | // Embed the default http context here, 54 | // so that we don't need to reimplement all the methods. 55 | types.DefaultHttpContext 56 | contextID uint32 57 | } 58 | 59 | func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 60 | data, err := proxywasm.GetPluginConfiguration() 61 | if err != nil { 62 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 63 | return types.ActionContinue 64 | } 65 | 66 | name := string(data) 67 | value, err := proxywasm.GetProperty([]string{name}) 68 | if err != nil { 69 | proxywasm.LogErrorf("error get property: %v", err) 70 | return types.ActionContinue 71 | } 72 | proxywasm.LogWarnf("get property: %v = %v", name, string(value)) 73 | return types.ActionContinue 74 | } 75 | -------------------------------------------------------------------------------- /t/testdata/property/set.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | package main 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 21 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 22 | ) 23 | 24 | func main() { 25 | proxywasm.SetVMContext(&vmContext{}) 26 | } 27 | 28 | type vmContext struct { 29 | // Embed the default VM context here, 30 | // so that we don't need to reimplement all the methods. 31 | types.DefaultVMContext 32 | } 33 | 34 | // Override types.DefaultVMContext. 35 | func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { 36 | return &pluginContext{} 37 | } 38 | 39 | type pluginContext struct { 40 | // Embed the default plugin context here, 41 | // so that we don't need to reimplement all the methods. 42 | types.DefaultPluginContext 43 | } 44 | 45 | // Override types.DefaultPluginContext. 46 | func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 47 | return &httpLifecycle{contextID: contextID} 48 | } 49 | 50 | func (*pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { 51 | return true 52 | } 53 | 54 | type httpLifecycle struct { 55 | // Embed the default http context here, 56 | // so that we don't need to reimplement all the methods. 57 | types.DefaultHttpContext 58 | contextID uint32 59 | } 60 | 61 | func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 62 | data, err := proxywasm.GetPluginConfiguration() 63 | if err != nil { 64 | proxywasm.LogErrorf("error reading plugin configuration: %v", err) 65 | return types.ActionContinue 66 | } 67 | 68 | configure := strings.Split(string(data), "|") 69 | err = proxywasm.SetProperty([]string{configure[0]}, []byte(configure[1])) 70 | if err != nil { 71 | proxywasm.LogErrorf("error set property: %v", err) 72 | return types.ActionContinue 73 | } 74 | proxywasm.LogWarnf("set property success: %v = %v", configure[0], configure[1]) 75 | return types.ActionContinue 76 | } 77 | -------------------------------------------------------------------------------- /t/testdata/rust/fault-injection/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "cfg-if" 18 | version = "1.0.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 21 | 22 | [[package]] 23 | name = "fault-injection" 24 | version = "0.1.0" 25 | dependencies = [ 26 | "log", 27 | "proxy-wasm", 28 | "rand", 29 | "serde", 30 | "serde_derive", 31 | "serde_json", 32 | ] 33 | 34 | [[package]] 35 | name = "getrandom" 36 | version = "0.2.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 39 | dependencies = [ 40 | "cfg-if", 41 | "libc", 42 | "wasi", 43 | ] 44 | 45 | [[package]] 46 | name = "hashbrown" 47 | version = "0.12.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" 50 | dependencies = [ 51 | "ahash", 52 | ] 53 | 54 | [[package]] 55 | name = "itoa" 56 | version = "0.4.8" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 59 | 60 | [[package]] 61 | name = "libc" 62 | version = "0.2.104" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" 65 | 66 | [[package]] 67 | name = "log" 68 | version = "0.4.14" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 71 | dependencies = [ 72 | "cfg-if", 73 | ] 74 | 75 | [[package]] 76 | name = "once_cell" 77 | version = "1.8.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 80 | 81 | [[package]] 82 | name = "ppv-lite86" 83 | version = "0.2.15" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 86 | 87 | [[package]] 88 | name = "proc-macro2" 89 | version = "1.0.30" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" 92 | dependencies = [ 93 | "unicode-xid", 94 | ] 95 | 96 | [[package]] 97 | name = "proxy-wasm" 98 | version = "0.2.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "3927081c2674366adadef4d5c5d34c4d849ab764a17bfe4ff2bd04436efb593d" 101 | dependencies = [ 102 | "hashbrown", 103 | "log", 104 | ] 105 | 106 | [[package]] 107 | name = "quote" 108 | version = "1.0.10" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 111 | dependencies = [ 112 | "proc-macro2", 113 | ] 114 | 115 | [[package]] 116 | name = "rand" 117 | version = "0.8.4" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 120 | dependencies = [ 121 | "libc", 122 | "rand_chacha", 123 | "rand_core", 124 | "rand_hc", 125 | ] 126 | 127 | [[package]] 128 | name = "rand_chacha" 129 | version = "0.3.1" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 132 | dependencies = [ 133 | "ppv-lite86", 134 | "rand_core", 135 | ] 136 | 137 | [[package]] 138 | name = "rand_core" 139 | version = "0.6.3" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 142 | dependencies = [ 143 | "getrandom", 144 | ] 145 | 146 | [[package]] 147 | name = "rand_hc" 148 | version = "0.3.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 151 | dependencies = [ 152 | "rand_core", 153 | ] 154 | 155 | [[package]] 156 | name = "ryu" 157 | version = "1.0.5" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 160 | 161 | [[package]] 162 | name = "serde" 163 | version = "1.0.130" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 166 | dependencies = [ 167 | "serde_derive", 168 | ] 169 | 170 | [[package]] 171 | name = "serde_derive" 172 | version = "1.0.130" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 175 | dependencies = [ 176 | "proc-macro2", 177 | "quote", 178 | "syn", 179 | ] 180 | 181 | [[package]] 182 | name = "serde_json" 183 | version = "1.0.68" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" 186 | dependencies = [ 187 | "itoa", 188 | "ryu", 189 | "serde", 190 | ] 191 | 192 | [[package]] 193 | name = "syn" 194 | version = "1.0.80" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" 197 | dependencies = [ 198 | "proc-macro2", 199 | "quote", 200 | "unicode-xid", 201 | ] 202 | 203 | [[package]] 204 | name = "unicode-xid" 205 | version = "0.2.2" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 208 | 209 | [[package]] 210 | name = "version_check" 211 | version = "0.9.3" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 214 | 215 | [[package]] 216 | name = "wasi" 217 | version = "0.10.2+wasi-snapshot-preview1" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 220 | -------------------------------------------------------------------------------- /t/testdata/rust/fault-injection/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | [package] 16 | name = "fault-injection" 17 | version = "0.1.0" 18 | edition = "2021" 19 | 20 | [lib] 21 | crate-type = ["cdylib"] 22 | path = "./fault_injection.rs" 23 | 24 | [dependencies] 25 | log = "0.4" 26 | proxy-wasm = "0.2" 27 | rand = "0.8.4" 28 | serde = { version = "1.0", features = ["derive"] } 29 | serde_derive = { version = "1.0", default-features = false } 30 | serde_json = { version = "1.0", default-features = false, features = ["alloc"] } 31 | -------------------------------------------------------------------------------- /t/testdata/rust/fault-injection/fault_injection.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | use log::*; 16 | use rand::Rng; 17 | use serde::{Deserialize, Serialize}; 18 | use proxy_wasm::traits::*; 19 | use proxy_wasm::types::*; 20 | 21 | #[no_mangle] 22 | pub fn _start() { 23 | proxy_wasm::set_log_level(LogLevel::Info); 24 | proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { 25 | Box::new(FaultInjectionRoot { 26 | conf: Conf{ 27 | body: String::new(), 28 | http_status: 200, 29 | percentage: 100, 30 | }, 31 | }) 32 | }); 33 | } 34 | 35 | #[derive(Serialize, Deserialize, Clone)] 36 | struct Conf { 37 | #[serde(default)] 38 | body: String, 39 | http_status: u32, 40 | #[serde(default = "default_percentage")] 41 | percentage: u8, 42 | } 43 | 44 | fn default_percentage() -> u8 { 45 | 100 46 | } 47 | 48 | struct FaultInjectionRoot { 49 | conf: Conf, 50 | } 51 | 52 | impl Context for FaultInjectionRoot {} 53 | 54 | impl RootContext for FaultInjectionRoot { 55 | fn on_configure(&mut self, _: usize) -> bool { 56 | if let Some(config_bytes) = self.get_plugin_configuration() { 57 | let conf : Conf = serde_json::from_str(&*String::from_utf8(config_bytes).unwrap() 58 | ).unwrap(); 59 | self.conf = conf; 60 | } 61 | true 62 | } 63 | 64 | fn get_type(&self) -> Option<ContextType> { 65 | Some(ContextType::HttpContext) 66 | } 67 | 68 | fn create_http_context(&self, _context_id: u32) -> Option<Box<dyn HttpContext>> { 69 | Some(Box::new(FaultInjection { 70 | conf: self.conf.clone(), 71 | })) 72 | } 73 | } 74 | 75 | struct FaultInjection { 76 | conf: Conf, 77 | } 78 | 79 | impl Context for FaultInjection {} 80 | 81 | impl HttpContext for FaultInjection { 82 | fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { 83 | let mut rng = rand::thread_rng(); 84 | 85 | let rd = rng.gen_range(0..100); 86 | info!("random {}, percentage {}", rd, self.conf.percentage); 87 | 88 | if self.conf.percentage <= rd { 89 | return Action::Continue 90 | } 91 | self.send_http_response( 92 | self.conf.http_status, 93 | vec![], 94 | Some(self.conf.body.as_bytes()), 95 | ); 96 | Action::Pause 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /t/testdata/rust/http-call/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "cfg-if" 18 | version = "1.0.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 21 | 22 | [[package]] 23 | name = "getrandom" 24 | version = "0.2.6" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" 27 | dependencies = [ 28 | "cfg-if", 29 | "libc", 30 | "wasi", 31 | ] 32 | 33 | [[package]] 34 | name = "hashbrown" 35 | version = "0.12.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" 38 | dependencies = [ 39 | "ahash", 40 | ] 41 | 42 | [[package]] 43 | name = "http_call" 44 | version = "0.1.0" 45 | dependencies = [ 46 | "log", 47 | "proxy-wasm", 48 | ] 49 | 50 | [[package]] 51 | name = "libc" 52 | version = "0.2.125" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" 55 | 56 | [[package]] 57 | name = "log" 58 | version = "0.4.17" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 61 | dependencies = [ 62 | "cfg-if", 63 | ] 64 | 65 | [[package]] 66 | name = "once_cell" 67 | version = "1.10.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 70 | 71 | [[package]] 72 | name = "proxy-wasm" 73 | version = "0.2.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "3927081c2674366adadef4d5c5d34c4d849ab764a17bfe4ff2bd04436efb593d" 76 | dependencies = [ 77 | "hashbrown", 78 | "log", 79 | ] 80 | 81 | [[package]] 82 | name = "version_check" 83 | version = "0.9.4" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 86 | 87 | [[package]] 88 | name = "wasi" 89 | version = "0.10.2+wasi-snapshot-preview1" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 92 | -------------------------------------------------------------------------------- /t/testdata/rust/http-call/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | [package] 16 | name = "http_call" 17 | version = "0.1.0" 18 | edition = "2021" 19 | 20 | [lib] 21 | crate-type = ["cdylib"] 22 | path = "./http_call.rs" 23 | 24 | [dependencies] 25 | log = "0.4" 26 | proxy-wasm = "0.2" 27 | -------------------------------------------------------------------------------- /t/testdata/rust/http-call/http_call.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | use proxy_wasm::traits::*; 16 | use proxy_wasm::types::*; 17 | use std::time::Duration; 18 | 19 | proxy_wasm::main! {{ 20 | proxy_wasm::set_log_level(LogLevel::Trace); 21 | proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(HttpCall) }); 22 | }} 23 | 24 | struct HttpCall; 25 | 26 | impl HttpContext for HttpCall { 27 | fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { 28 | self.dispatch_http_call( 29 | "127.0.0.1:1980", 30 | vec![ 31 | (":method", "GET"), 32 | (":path", "/bytes/1"), 33 | (":authority", "httpbin.org"), 34 | ], 35 | None, 36 | vec![], 37 | Duration::from_secs(5), 38 | ) 39 | .unwrap(); 40 | Action::Pause 41 | } 42 | } 43 | 44 | impl Context for HttpCall { 45 | fn on_http_call_response(&mut self, _: u32, _: usize, _: usize, _: usize) { 46 | let s = self.get_http_call_response_header("Content-Type").unwrap(); 47 | let st = self.get_http_call_response_header(":status").unwrap(); 48 | self.send_http_response( 49 | 403, 50 | vec![("Resp-Content-Type", &s), ("Resp-Status", &st)], 51 | Some(b"Access forbidden.\n"), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /utils/check-test-code-style.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | set -x -euo pipefail 17 | 18 | find t -name '*.t' -exec grep -E "\-\-\-\s+(SKIP|ONLY|LAST|FIRST)$" {} + > /tmp/error.log || true 19 | if [ -s /tmp/error.log ]; then 20 | echo "Forbidden directives to found. Bypass test cases without reason are not allowed." 21 | cat /tmp/error.log 22 | exit 1 23 | fi 24 | 25 | find t -name '*.t' -exec ./utils/reindex {} + > \ 26 | /tmp/check.log 2>&1 || (cat /tmp/check.log && exit 1) 27 | 28 | grep "done." /tmp/check.log > /tmp/error.log || true 29 | if [ -s /tmp/error.log ]; then 30 | echo "=====bad style=====" 31 | cat /tmp/error.log 32 | echo "you need to run 'reindex' to fix them. Read CONTRIBUTING.md for more details." 33 | exit 1 34 | fi 35 | --------------------------------------------------------------------------------