Repository: proxy-wasm/proxy-wasm-rust-sdk Branch: main Commit: b997631e87d9 Files: 80 Total size: 243.7 KB Directory structure: gitextract_d2hvdrgx/ ├── .actrc ├── .bazelrc ├── .bazelversion ├── .bcr/ │ ├── metadata.template.json │ ├── presubmit.yml │ └── source.template.json ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── publish-to-bcr.yml │ └── rust.yml ├── .gitignore ├── BUILD ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── Cargo.toml ├── DEVELOPMENT.md ├── LICENSE ├── MODULE.bazel ├── README.md ├── WORKSPACE ├── WORKSPACE.bzlmod ├── bazel/ │ ├── BUILD │ ├── cargo/ │ │ ├── BUILD │ │ └── remote/ │ │ ├── BUILD.allocator-api2-0.2.21.bazel │ │ ├── BUILD.bazel │ │ ├── BUILD.equivalent-1.0.2.bazel │ │ ├── BUILD.foldhash-0.2.0.bazel │ │ ├── BUILD.hashbrown-0.16.0.bazel │ │ ├── BUILD.log-0.4.27.bazel │ │ ├── alias_rules.bzl │ │ ├── crates.bzl │ │ └── defs.bzl │ ├── dependencies_bazel.bzl │ ├── dependencies_compat.bzl │ ├── dependencies_crates.bzl │ ├── extensions.bzl │ └── repositories.bzl ├── build.rs ├── examples/ │ ├── envoy_filter_metadata/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── envoy.yaml │ │ └── src/ │ │ └── lib.rs │ ├── grpc_auth_random/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── envoy.yaml │ │ └── src/ │ │ └── lib.rs │ ├── hello_world/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── envoy.yaml │ │ └── src/ │ │ └── lib.rs │ ├── http_auth_random/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── envoy.yaml │ │ └── src/ │ │ └── lib.rs │ ├── http_body/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── envoy.yaml │ │ └── src/ │ │ └── lib.rs │ ├── http_config/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── envoy.yaml │ │ └── src/ │ │ └── lib.rs │ └── http_headers/ │ ├── Cargo.toml │ ├── README.md │ ├── docker-compose.yaml │ ├── envoy.yaml │ └── src/ │ └── lib.rs └── src/ ├── allocator.rs ├── dispatcher.rs ├── hostcalls.rs ├── lib.rs ├── logger.rs ├── traits.rs └── types.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .actrc ================================================ -P ubuntu-24.04=catthehacker/ubuntu:act-24.04 ================================================ FILE: .bazelrc ================================================ # Force Bazel to use --target=wasm32-wasip1 build --platforms=@rules_rust//rust/platform:wasi ================================================ FILE: .bazelversion ================================================ 8.5.1 ================================================ FILE: .bcr/metadata.template.json ================================================ { "homepage": "https://github.com/proxy-wasm/proxy-wasm-rust-sdk", "maintainers": [ { "github": "PiotrSikora", "github_user_id": 190297, "name": "Piotr Sikora" } ], "repository": [ "github:proxy-wasm/proxy-wasm-rust-sdk" ], "versions": [], "yanked_versions": {} } ================================================ FILE: .bcr/presubmit.yml ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. matrix: platform: - debian11 - ubuntu2004 - ubuntu2004_arm64 - macos - macos_arm64 bazel: [7.x, 8.x, 9.x] tasks: verify_targets: name: Verify build targets platform: ${{ platform }} bazel: ${{ bazel }} build_targets: - "@proxy-wasm-rust-sdk//:proxy_wasm" ================================================ FILE: .bcr/source.template.json ================================================ { "integrity": "", "strip_prefix": "proxy-wasm-rust-sdk-{VERSION}", "url": "https://github.com/{OWNER}/{REPO}/archive/refs/tags/v{VERSION}.tar.gz" } ================================================ FILE: .github/dependabot.yml ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: 2 updates: - package-ecosystem: bazel directory: / schedule: interval: weekly - package-ecosystem: cargo directories: - / - examples/envoy_filter_metadata - examples/grpc_auth_random - examples/hello_world - examples/http_auth_random - examples/http_body - examples/http_config - examples/http_headers schedule: interval: weekly - package-ecosystem: github-actions directory: / schedule: interval: weekly ================================================ FILE: .github/workflows/publish-to-bcr.yml ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Publish to BCR on: # Automatically trigger when a version tag is pushed. push: tags: - "v*.*.*" # Allow manual dispatch from the GitHub UI for retries. workflow_dispatch: inputs: tag_name: description: "Git tag to publish (e.g. v0.2.5)" required: true type: string jobs: publish: uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v1.1.0 with: attest: false open_pull_request: false registry_fork: PiotrSikora/bazel-central-registry tag_name: ${{ inputs.tag_name || github.ref_name }} permissions: attestations: write contents: write id-token: write secrets: publish_token: ${{ secrets.BCR_PUBLISH_TOKEN }} ================================================ FILE: .github/workflows/rust.yml ================================================ # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Rust on: pull_request: branches: - main push: branches: - main schedule: - cron: '0 0 * * *' jobs: licenses: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: '^1.24' - name: Cache if: ${{ env.ACT }} uses: actions/cache@v5 with: path: | ~/go/pkg/mod key: licenses-${{ hashFiles('.github/workflows/rust.yml') }} - name: Install dependencies run: | go install github.com/google/addlicense@v1.2.0 export PATH=$PATH:$(go env GOPATH)/bin - name: Check licenses run: | addlicense -check -ignore "bazel/cargo/remote/**" . bazel: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: '^1.24' - name: Cache uses: actions/cache@v5 with: path: | ~/.cache/bazel ~/.cache/bazelisk ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/go/pkg/mod key: bazel-${{ hashFiles('.github/workflows/rust.yml', 'BUILD', 'WORKSPACE', '.bazelrc', '.bazelversion', 'bazel/cargo/Cargo.Bazel.lock', 'bazel/dependencies.bzl', 'bazel/repositories.bzl') }} - name: Install dependencies run: | go install github.com/bazelbuild/bazelisk@v1.28.1 go install github.com/bazelbuild/buildtools/buildifier@v0.0.0-20260121081817-bbf01ec6cb49 export PATH=$PATH:$(go env GOPATH)/bin - name: Build (wasm32-unknown-unknown) run: bazelisk --noworkspace_rc build --noenable_bzlmod --enable_workspace --platforms=@rules_rust//rust/platform:wasm //... - name: Build (wasm32-unknown-unknown/bzlmod) run: bazelisk --noworkspace_rc build --enable_bzlmod --noenable_workspace --platforms=@rules_rust//rust/platform:wasm //... - name: Build (wasm32-wasip1) run: bazelisk --noworkspace_rc build --noenable_bzlmod --enable_workspace --platforms=@rules_rust//rust/platform:wasi //... - name: Build (wasm32-wasip1/bzlmod) run: bazelisk --noworkspace_rc build --enable_bzlmod --noenable_workspace --platforms=@rules_rust//rust/platform:wasi //... - name: Format (buildifier) run: | buildifier -mode=check -r . - name: Format (rules_rust) run: | bazelisk --noworkspace_rc run --noenable_bzlmod --enable_workspace //bazel/cargo:crates_vendor git diff --exit-code - name: Format (MODULE.bazel.lock) run: bazelisk mod deps --lockfile_mode=error msrv: runs-on: ubuntu-24.04 env: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v6 - name: Cache if: ${{ env.ACT }} uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup **/target key: msrv-${{ hashFiles('Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y --default-toolchain 1.68.0 rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Update Rust run: | rustup toolchain install 1.68.0 --component clippy --component rustfmt rustup default 1.68.0 rustup target add wasm32-unknown-unknown rustup target add wasm32-wasi - name: Build (wasm32-unknown-unknown) run: cargo build --release --all-targets --target=wasm32-unknown-unknown - name: Clippy (wasm32-unknown-unknown) run: cargo clippy --release --all-targets --target=wasm32-unknown-unknown - name: Build (wasm32-wasi) run: cargo build --release --all-targets --target=wasm32-wasi - name: Clippy (wasm32-wasi) run: cargo clippy --release --all-targets --target=wasm32-wasi - name: Test run: cargo test # Re-enable once our MSRV is Rust v1.71+. # - name: Test (no leaks) # run: cargo test --features mockalloc - name: Format (rustfmt) run: cargo fmt -- --check - name: Format (manifest) run: cargo verify-project - name: Package (docs) run: cargo doc --no-deps --target=wasm32-unknown-unknown - name: Package (publish) run: cargo publish --dry-run --target=wasm32-unknown-unknown stable: runs-on: ubuntu-24.04 env: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v6 - name: Cache if: ${{ env.ACT }} uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup **/target key: stable-${{ hashFiles('Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y --default-toolchain stable rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Update Rust run: | rustup toolchain install stable --component clippy --component rustfmt rustup target add wasm32-unknown-unknown rustup target add wasm32-wasip1 - name: Build (wasm32-unknown-unknown) run: cargo build --release --all-targets --target=wasm32-unknown-unknown - name: Clippy (wasm32-unknown-unknown) run: cargo clippy --release --all-targets --target=wasm32-unknown-unknown - name: Build (wasm32-wasip1) run: cargo build --release --all-targets --target=wasm32-wasip1 - name: Clippy (wasm32-wasip1) run: cargo clippy --release --all-targets --target=wasm32-wasip1 - name: Test run: cargo test - name: Test (no leaks) run: cargo test --features mockalloc - name: Format (rustfmt) run: cargo fmt -- --check - name: Format (manifest) run: cargo verify-project - name: Package (docs) run: cargo doc --no-deps --target=wasm32-unknown-unknown - name: Package (publish) run: cargo publish --dry-run --target=wasm32-unknown-unknown nightly: runs-on: ubuntu-24.04 env: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v6 - name: Cache if: ${{ env.ACT }} uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup **/target key: nightly-${{ hashFiles('Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y --default-toolchain nightly rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Update Rust run: | rustup toolchain install nightly --component clippy --component rustfmt rustup default nightly rustup target add wasm32-unknown-unknown rustup target add wasm32-wasip1 - name: Build (wasm32-unknown-unknown) run: cargo build --release --all-targets --target=wasm32-unknown-unknown - name: Clippy (wasm32-unknown-unknown) run: cargo clippy --release --all-targets --target=wasm32-unknown-unknown - name: Build (wasm32-wasip1) run: cargo build --release --all-targets --target=wasm32-wasip1 - name: Clippy (wasm32-wasip1) run: cargo clippy --release --all-targets --target=wasm32-wasip1 - name: Test run: cargo test - name: Test (no leaks) run: cargo test --features mockalloc - name: Bench run: cargo bench - name: Format (rustfmt) run: cargo fmt -- --check - name: Format (manifest) run: cargo verify-project - name: Package (docs) run: cargo doc --no-deps --target=wasm32-unknown-unknown - name: Package (publish) run: cargo publish --dry-run --target=wasm32-unknown-unknown outdated: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - name: Cache uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup key: outdated-${{ hashFiles('.github/workflows/rust.yml', 'Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dependencies run: | cargo install cargo-outdated --version 0.17.0 - name: Run cargo outdated (main) run: | cargo outdated --root-deps-only --exit-code 1 - name: Run cargo outdated (examples) run: | for example in $(find examples -name Cargo.toml); do \ cd $(dirname $GITHUB_WORKSPACE/$example); \ cargo outdated --root-deps-only --exit-code 1; \ done audit: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - name: Cache uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup key: audit-${{ hashFiles('.github/workflows/rust.yml', 'Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install dependencies run: | cargo install cargo-audit --version 0.22.0 - name: Run cargo audit (main) run: | cp -p bazel/cargo/Cargo.Bazel.lock Cargo.lock cargo audit - name: Run cargo audit (examples) run: | for example in $(find examples -name Cargo.toml); do \ cd $(dirname $GITHUB_WORKSPACE/$example); \ cargo audit; \ done examples: runs-on: ubuntu-24.04 strategy: matrix: example: - 'hello_world' - 'http_auth_random' - 'http_body' - 'http_config' - 'http_headers' - 'grpc_auth_random' - 'envoy_filter_metadata' defaults: run: working-directory: ./examples/${{ matrix.example }} env: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v6 - name: Cache if: ${{ env.ACT }} uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup **/target key: example-${{ matrix.example }}-${{ hashFiles('Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Update Rust run: | rustup toolchain install stable --component clippy --component rustfmt rustup target add wasm32-wasip1 - name: Build (wasm32-wasip1) run: cargo build --release --target=wasm32-wasip1 - name: Clippy (wasm32-wasip1) run: cargo clippy --release --target=wasm32-wasip1 - name: Format (rustfmt) run: cargo fmt -- --check - name: Format (manifest) run: cargo verify-project - name: Validate Envoy config run: | docker run --rm \ -v $(pwd)/envoy.yaml:/envoy.yaml \ -v $(pwd)/target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins \ envoyproxy/envoy:v1.34-latest \ --mode validate \ -c envoy.yaml reactors: runs-on: ubuntu-24.04 strategy: matrix: example: - 'hello_world' - 'http_auth_random' - 'http_body' - 'http_config' - 'http_headers' - 'grpc_auth_random' - 'envoy_filter_metadata' defaults: run: working-directory: ./examples/${{ matrix.example }} env: RUSTFLAGS: -D warnings -Z wasi-exec-model=reactor steps: - uses: actions/checkout@v6 - name: Cache if: ${{ env.ACT }} uses: actions/cache@v5 with: path: | ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/advisory-db ~/.cargo/bin ~/.cargo/registry ~/.rustup **/target key: reactor-${{ matrix.example }}-${{ hashFiles('Cargo.toml') }} - name: Install Rustup if: ${{ env.ACT }} run: | curl -OL https://static.rust-lang.org/rustup/rustup-init.sh chmod +x ./rustup-init.sh ./rustup-init.sh -y --default-toolchain nightly rm rustup-init.sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Update Rust run: | rustup toolchain install nightly --component clippy --component rustfmt rustup default nightly rustup target add wasm32-wasip1 - name: Change crate type from library to binary run: | grep -v '^\[lib\]' Cargo.toml > Cargo.tmp grep -v '^crate-type' Cargo.tmp > Cargo.toml mv src/lib.rs src/main.rs - name: Build (wasm32-wasip1) run: cargo build --release --target=wasm32-wasip1 - name: Clippy (wasm32-wasip1) run: cargo clippy --release --target=wasm32-wasip1 - name: Format (rustfmt) run: cargo fmt -- --check - name: Format (manifest) run: cargo verify-project - name: Rename .wasm to match expected filename run: | cd target/wasm32-wasip1/release for file in $(ls -1 *.wasm); do \ mv $file $(echo $file | sed 's/-/_/g'); \ done - name: Validate Envoy config run: | docker run --rm \ -v $(pwd)/envoy.yaml:/envoy.yaml \ -v $(pwd)/target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins \ envoyproxy/envoy:v1.34-latest \ --mode validate \ -c envoy.yaml ================================================ FILE: .gitignore ================================================ /bazel-* target Cargo.lock ================================================ FILE: BUILD ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@rules_rust//cargo:defs.bzl", "cargo_build_script") load("@rules_rust//rust:defs.bzl", "rust_library", "rust_shared_library") exports_files([ "Cargo.toml", ]) cargo_build_script( name = "proxy_wasm_build_script", srcs = ["build.rs"], edition = "2018", tags = ["manual"], visibility = ["//visibility:private"], ) rust_library( name = "proxy_wasm", srcs = glob(["src/*.rs"]), edition = "2018", visibility = ["//visibility:public"], deps = [ ":proxy_wasm_build_script", "//bazel/cargo/remote:hashbrown", "//bazel/cargo/remote:log", ], ) rust_shared_library( name = "http_auth_random", srcs = ["examples/http_auth_random/src/lib.rs"], edition = "2018", rustc_flags = ["-Cstrip=debuginfo"], visibility = ["//visibility:private"], deps = [ ":proxy_wasm", "//bazel/cargo/remote:log", ], ) ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [0.2.4] - 2025-10-01 ### Fixed - Fixed a memory leak in `get_map` and `get_map_bytes` hostcalls. This issue has been introduced in [0.2.3]. Thanks [@JaatadiaMulesoft](https://github.com/JaatadiaMulesoft)! ## [0.2.3] - 2025-06-04 ### Changed - An empty value (`Some("")` or `Some([])`) is now returned when retrieving value of a HTTP header or trailer with an empty value. This is consistent with the representation when retrieving a full HTTP header or trailer map. Previously, a "no value" (`None`) was being returned, which made an empty value indistinguishable from a non-existent HTTP header or trailer. Thanks [@prembhaskal](https://github.com/prembhaskal)! ### Added - Added support for foreign function callbacks. Thanks [@casimiro](https://github.com/casimiro)! - Added convenience functions to remove headers and trailers. Thanks [@itsLucario](https://github.com/itsLucario)! - Added convenience function to remove shared data. ## [0.2.2] - 2024-07-21 ### Fixed - Fixed support for nested gRPC callouts. Thanks [@andytesti](https://github.com/andytesti)! - Fixed panic on unknown `token_id` in `on_grpc_receive_initial_metadata` and `on_grpc_receive_trailing_metadata`. Thanks [@erikness-doordash](https://github.com/erikness-doordash)! - Fixed panic on unexpected failures in `get_property`. Thanks [@alexsnaps](https://github.com/alexsnaps)! - Fixed panic on unexpected failures in `call_foreign_function`. Reported by [@geNAZt](https://github.com/geNAZt). ### Added - Added support for sending error responses with gRPC status codes. Thanks [@juanmolle](https://github.com/juanmolle)! ## [0.2.1] - 2022-11-22 ### Fixed - Fixed panic on unknown `token_id` in `on_grpc_close`. Thanks [@Protryon](https://github.com/Protryon)! ### Changed - Changed MSRV to v1.61.0. ### Removed - Removed `wee-alloc` feature, because that crate is no longer maintained and it leaks memory. ## [0.2.0] - 2022-04-08 ### Fixed - Fixed performance degradation with `wasm32-wasi` target in Rust v1.56.0 or newer by adding `proxy_wasm::main` macro that should be used instead of custom `_start`, `_initialize` and/or `main` exports. ### Changed - Updated ABI to Proxy-Wasm ABI v0.2.1. ### Added - Added support for calling foreign functions. Thanks [@Gsantomaggio](https://github.com/Gsantomaggio)! ## [0.1.4] - 2021-07-01 ### Added - Added support for gRPC callouts. Thanks [@Shikugawa](https://github.com/Shikugawa)! ## [0.1.3] - 2020-12-04 ### Fixed - Fixed support for nested HTTP callouts. Thanks [@SvetlinZarev](https://github.com/SvetlinZarev)! ### Changed - Changed `wee-alloc` to an optional feature. Thanks [@yuval-k](https://github.com/yuval-k)! ### Added - Added support for building for `wasm32-wasi` target. - Added support for metrics. - Added support for `RootContext` to create child contexts for streams. Thanks [@dgn](https://github.com/dgn)! - Added support for setting network buffers. ## [0.1.2] - 2020-08-05 ### Changed - Updated `MapType` values to match updated Proxy-Wasm ABI v0.1.0. Thanks [@yskopets](https://github.com/yskopets)! ## [0.1.1] - 2020-08-05 ### Added - Added support for building with Bazel. - Added support for setting HTTP bodies. Thanks [@gbrail](https://github.com/gbrail)! ## [0.1.0] - 2020-02-29 ### Added - Initial release. [0.2.4]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.2.3...v0.2.4 [0.2.3]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.1.4...v0.2.0 [0.1.4]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.1.3...v0.1.4 [0.1.3]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.1.2...v0.1.3 [0.1.2]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/compare/v0.1.0...v0.1.1 [0.1.0]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/releases/tag/v0.1.0 ================================================ FILE: CODEOWNERS ================================================ * @PiotrSikora ================================================ FILE: CONTRIBUTING.md ================================================ # How to Contribute We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. ## Contributor License Agreement Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project. Head over to to see your current agreements on file or to sign a new one. You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. ## Code reviews All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. ## Community Guidelines This project follows [Google's Open Source Community Guidelines](https://opensource.google/conduct/). ================================================ FILE: Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] name = "proxy-wasm" version = "0.2.5-dev" authors = ["Piotr Sikora "] rust-version = "1.68" description = "WebAssembly for Proxies" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/proxy-wasm/proxy-wasm-rust-sdk" edition = "2018" build = "build.rs" [dependencies] hashbrown = "0.16" log = "0.4" mockalloc = { version = "0.1", optional = true } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" [profile.test] inherits = "release" debug = true [profile.bench] inherits = "release" debug = true ================================================ FILE: DEVELOPMENT.md ================================================ # Development ## Testing GitHub Actions can be executed locally using the [`act`] tool. All tests can be executed using: act Individual tests can be executed using `-j` and `--matrix` parameters, e.g.: act -j bazel act -j stable act -j nightly act -j examples --matrix example:http_auth_random By default, all jobs are cached in `~/.cache/actcache`. This can be disabled using the `--no-cache-server` parameter. ## Updating Bazel dependencies When adding or updating Cargo dependencies, the existing Bazel `BUILD` files must be regenerated using the [`bazelisk`] tool: ```sh bazelisk run --noenable_bzlmod --enable_workspace //bazel/cargo:crates_vendor -- --repin all ``` [`act`]: https://github.com/nektos/act [`bazelisk`]: https://github.com/bazelbuild/bazelisk ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MODULE.bazel ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. module( name = "proxy-wasm-rust-sdk", version = "0.2.5-dev", repo_name = "proxy_wasm_rust_sdk", ) # Regular dependencies (sorted alphabetically). bazel_dep(name = "bazel_features", version = "1.38.0") bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "rules_cc", version = "0.2.14") bazel_dep(name = "rules_rust", version = "0.68.1") # Configure Rust toolchain. rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") rust.toolchain( edition = "2018", versions = ["1.91.1"], ) use_repo(rust, "rust_toolchains") register_toolchains( "@rust_toolchains//:all", # Dummy C/C++ toolchains for Wasm targets. "@rules_rust//rust/private/dummy_cc_toolchain:dummy_cc_wasm32_toolchain", "@rules_rust//rust/private/dummy_cc_toolchain:dummy_cc_wasm64_toolchain", ) # Cargo dependencies. crates_deps = use_extension("//bazel:extensions.bzl", "crates_deps") use_repo( crates_deps, "crates_vendor", "crates_vendor__hashbrown-0.16.0", "crates_vendor__log-0.4.27", ) ================================================ FILE: README.md ================================================ # WebAssembly for Proxies (Rust SDK) [![Build Status][build-badge]][build-link] [![Crate][crate-badge]][crate-link] [![Documentation][docs-badge]][docs-link] [![Apache 2.0 License][license-badge]][license-link] [build-badge]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/workflows/Rust/badge.svg?branch=main [build-link]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/actions?query=workflow%3ARust+branch%3Amain [crate-badge]: https://img.shields.io/crates/v/proxy-wasm.svg [crate-link]: https://crates.io/crates/proxy-wasm [docs-badge]: https://docs.rs/proxy-wasm/badge.svg [docs-link]: https://docs.rs/proxy-wasm [license-badge]: https://img.shields.io/github/license/proxy-wasm/proxy-wasm-rust-sdk [license-link]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/blob/main/LICENSE ## Examples - [Hello World](./examples/hello_world/) - [HTTP Auth (random)](./examples/http_auth_random/) - [HTTP Headers](./examples/http_headers/) - [HTTP Response body](./examples/http_body/) - [HTTP Configuration](./examples/http_config/) - [gRPC Auth (random)](./examples/grpc_auth_random/) - [Envoy filter metadata](./examples/envoy_filter_metadata/) ## Articles & blog posts from the community - [Extending Envoy with WASM and Rust](https://antweiss.com/blog/extending-envoy-with-wasm-and-rust/) - [Writing Envoy filters in Rust with WebAssembly](https://content.red-badger.com/resources/extending-istio-with-rust-and-webassembly) ## Contributing changes See [CONTRIBUTING.md](./CONTRIBUTING.md) and [DEVELOPMENT.md](./DEVELOPMENT.md) files. ================================================ FILE: WORKSPACE ================================================ workspace(name = "proxy_wasm_rust_sdk") load("@proxy_wasm_rust_sdk//bazel:repositories.bzl", "proxy_wasm_rust_sdk_repositories") proxy_wasm_rust_sdk_repositories() load("@proxy_wasm_rust_sdk//bazel:dependencies_bazel.bzl", "proxy_wasm_rust_sdk_dependencies_bazel") proxy_wasm_rust_sdk_dependencies_bazel() load("@proxy_wasm_rust_sdk//bazel:dependencies_compat.bzl", "proxy_wasm_rust_sdk_dependencies_compat") proxy_wasm_rust_sdk_dependencies_compat() load("@proxy_wasm_rust_sdk//bazel:dependencies_crates.bzl", "proxy_wasm_rust_sdk_dependencies_crates") proxy_wasm_rust_sdk_dependencies_crates() ================================================ FILE: WORKSPACE.bzlmod ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ================================================ FILE: bazel/BUILD ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ================================================ FILE: bazel/cargo/BUILD ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@rules_rust//crate_universe:defs.bzl", "crates_vendor") exports_files([ "Cargo.Bazel.lock", ]) crates_vendor( name = "crates_vendor", cargo_lockfile = "//bazel/cargo:Cargo.Bazel.lock", manifests = ["//:Cargo.toml"], mode = "remote", tags = ["manual"], vendor_path = "//bazel/cargo/remote", ) ================================================ FILE: bazel/cargo/remote/BUILD.allocator-api2-0.2.21.bazel ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") load("@rules_rust//rust:defs.bzl", "rust_library") package(default_visibility = ["//visibility:public"]) cargo_toml_env_vars( name = "cargo_toml_env_vars", src = "Cargo.toml", ) rust_library( name = "allocator_api2", srcs = glob( include = ["**/*.rs"], allow_empty = True, ), compile_data = glob( include = ["**"], allow_empty = True, exclude = [ "**/* *", ".tmp_git_root/**/*", "BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel", ], ), crate_features = [ "alloc", ], crate_root = "src/lib.rs", edition = "2018", rustc_env_files = [ ":cargo_toml_env_vars", ], rustc_flags = [ "--cap-lints=allow", ], tags = [ "cargo-bazel", "crate-name=allocator-api2", "manual", "noclippy", "norustfmt", ], target_compatible_with = select({ "@rules_rust//rust/platform:aarch64-apple-darwin": [], "@rules_rust//rust/platform:aarch64-apple-ios": [], "@rules_rust//rust/platform:aarch64-apple-ios-sim": [], "@rules_rust//rust/platform:aarch64-linux-android": [], "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], "@rules_rust//rust/platform:aarch64-unknown-fuchsia": [], "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [], "@rules_rust//rust/platform:aarch64-unknown-uefi": [], "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:armv7-linux-androideabi": [], "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:i686-apple-darwin": [], "@rules_rust//rust/platform:i686-linux-android": [], "@rules_rust//rust/platform:i686-pc-windows-msvc": [], "@rules_rust//rust/platform:i686-unknown-freebsd": [], "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [], "@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [], "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], "@rules_rust//rust/platform:thumbv7em-none-eabi": [], "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [], "@rules_rust//rust/platform:wasm32-unknown-emscripten": [], "@rules_rust//rust/platform:wasm32-unknown-unknown": [], "@rules_rust//rust/platform:wasm32-wasip1": [], "@rules_rust//rust/platform:wasm32-wasip1-threads": [], "@rules_rust//rust/platform:wasm32-wasip2": [], "@rules_rust//rust/platform:x86_64-apple-darwin": [], "@rules_rust//rust/platform:x86_64-apple-ios": [], "@rules_rust//rust/platform:x86_64-linux-android": [], "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], "@rules_rust//rust/platform:x86_64-unknown-fuchsia": [], "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-none": [], "@rules_rust//rust/platform:x86_64-unknown-uefi": [], "//conditions:default": ["@platforms//:incompatible"], }), version = "0.2.21", ) ================================================ FILE: bazel/cargo/remote/BUILD.bazel ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### package(default_visibility = ["//visibility:public"]) exports_files( [ "cargo-bazel.json", "crates.bzl", "defs.bzl", ] + glob( include = ["*.bazel"], allow_empty = True, ), ) filegroup( name = "srcs", srcs = glob( include = [ "*.bazel", "*.bzl", ], allow_empty = True, ), ) # Workspace Member Dependencies alias( name = "hashbrown-0.16.0", actual = "@crates_vendor__hashbrown-0.16.0//:hashbrown", tags = ["manual"], ) alias( name = "hashbrown", actual = "@crates_vendor__hashbrown-0.16.0//:hashbrown", tags = ["manual"], ) alias( name = "log-0.4.27", actual = "@crates_vendor__log-0.4.27//:log", tags = ["manual"], ) alias( name = "log", actual = "@crates_vendor__log-0.4.27//:log", tags = ["manual"], ) alias( name = "proxy-wasm-0.2.5-dev", actual = "@crates_vendor__proxy-wasm-0.2.5-dev//:proxy_wasm", tags = ["manual"], ) alias( name = "proxy-wasm", actual = "@crates_vendor__proxy-wasm-0.2.5-dev//:proxy_wasm", tags = ["manual"], ) ================================================ FILE: bazel/cargo/remote/BUILD.equivalent-1.0.2.bazel ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") load("@rules_rust//rust:defs.bzl", "rust_library") package(default_visibility = ["//visibility:public"]) cargo_toml_env_vars( name = "cargo_toml_env_vars", src = "Cargo.toml", ) rust_library( name = "equivalent", srcs = glob( include = ["**/*.rs"], allow_empty = True, ), compile_data = glob( include = ["**"], allow_empty = True, exclude = [ "**/* *", ".tmp_git_root/**/*", "BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel", ], ), crate_root = "src/lib.rs", edition = "2015", rustc_env_files = [ ":cargo_toml_env_vars", ], rustc_flags = [ "--cap-lints=allow", ], tags = [ "cargo-bazel", "crate-name=equivalent", "manual", "noclippy", "norustfmt", ], target_compatible_with = select({ "@rules_rust//rust/platform:aarch64-apple-darwin": [], "@rules_rust//rust/platform:aarch64-apple-ios": [], "@rules_rust//rust/platform:aarch64-apple-ios-sim": [], "@rules_rust//rust/platform:aarch64-linux-android": [], "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], "@rules_rust//rust/platform:aarch64-unknown-fuchsia": [], "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [], "@rules_rust//rust/platform:aarch64-unknown-uefi": [], "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:armv7-linux-androideabi": [], "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:i686-apple-darwin": [], "@rules_rust//rust/platform:i686-linux-android": [], "@rules_rust//rust/platform:i686-pc-windows-msvc": [], "@rules_rust//rust/platform:i686-unknown-freebsd": [], "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [], "@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [], "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], "@rules_rust//rust/platform:thumbv7em-none-eabi": [], "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [], "@rules_rust//rust/platform:wasm32-unknown-emscripten": [], "@rules_rust//rust/platform:wasm32-unknown-unknown": [], "@rules_rust//rust/platform:wasm32-wasip1": [], "@rules_rust//rust/platform:wasm32-wasip1-threads": [], "@rules_rust//rust/platform:wasm32-wasip2": [], "@rules_rust//rust/platform:x86_64-apple-darwin": [], "@rules_rust//rust/platform:x86_64-apple-ios": [], "@rules_rust//rust/platform:x86_64-linux-android": [], "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], "@rules_rust//rust/platform:x86_64-unknown-fuchsia": [], "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-none": [], "@rules_rust//rust/platform:x86_64-unknown-uefi": [], "//conditions:default": ["@platforms//:incompatible"], }), version = "1.0.2", ) ================================================ FILE: bazel/cargo/remote/BUILD.foldhash-0.2.0.bazel ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") load("@rules_rust//rust:defs.bzl", "rust_library") package(default_visibility = ["//visibility:public"]) cargo_toml_env_vars( name = "cargo_toml_env_vars", src = "Cargo.toml", ) rust_library( name = "foldhash", srcs = glob( include = ["**/*.rs"], allow_empty = True, ), compile_data = glob( include = ["**"], allow_empty = True, exclude = [ "**/* *", ".tmp_git_root/**/*", "BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel", ], ), crate_root = "src/lib.rs", edition = "2021", rustc_env_files = [ ":cargo_toml_env_vars", ], rustc_flags = [ "--cap-lints=allow", ], tags = [ "cargo-bazel", "crate-name=foldhash", "manual", "noclippy", "norustfmt", ], target_compatible_with = select({ "@rules_rust//rust/platform:aarch64-apple-darwin": [], "@rules_rust//rust/platform:aarch64-apple-ios": [], "@rules_rust//rust/platform:aarch64-apple-ios-sim": [], "@rules_rust//rust/platform:aarch64-linux-android": [], "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], "@rules_rust//rust/platform:aarch64-unknown-fuchsia": [], "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [], "@rules_rust//rust/platform:aarch64-unknown-uefi": [], "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:armv7-linux-androideabi": [], "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:i686-apple-darwin": [], "@rules_rust//rust/platform:i686-linux-android": [], "@rules_rust//rust/platform:i686-pc-windows-msvc": [], "@rules_rust//rust/platform:i686-unknown-freebsd": [], "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [], "@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [], "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], "@rules_rust//rust/platform:thumbv7em-none-eabi": [], "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [], "@rules_rust//rust/platform:wasm32-unknown-emscripten": [], "@rules_rust//rust/platform:wasm32-unknown-unknown": [], "@rules_rust//rust/platform:wasm32-wasip1": [], "@rules_rust//rust/platform:wasm32-wasip1-threads": [], "@rules_rust//rust/platform:wasm32-wasip2": [], "@rules_rust//rust/platform:x86_64-apple-darwin": [], "@rules_rust//rust/platform:x86_64-apple-ios": [], "@rules_rust//rust/platform:x86_64-linux-android": [], "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], "@rules_rust//rust/platform:x86_64-unknown-fuchsia": [], "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-none": [], "@rules_rust//rust/platform:x86_64-unknown-uefi": [], "//conditions:default": ["@platforms//:incompatible"], }), version = "0.2.0", ) ================================================ FILE: bazel/cargo/remote/BUILD.hashbrown-0.16.0.bazel ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") load("@rules_rust//rust:defs.bzl", "rust_library") package(default_visibility = ["//visibility:public"]) cargo_toml_env_vars( name = "cargo_toml_env_vars", src = "Cargo.toml", ) rust_library( name = "hashbrown", srcs = glob( include = ["**/*.rs"], allow_empty = True, ), compile_data = glob( include = ["**"], allow_empty = True, exclude = [ "**/* *", ".tmp_git_root/**/*", "BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel", ], ), crate_features = [ "allocator-api2", "default", "default-hasher", "equivalent", "inline-more", "raw-entry", ], crate_root = "src/lib.rs", edition = "2021", rustc_env_files = [ ":cargo_toml_env_vars", ], rustc_flags = [ "--cap-lints=allow", ], tags = [ "cargo-bazel", "crate-name=hashbrown", "manual", "noclippy", "norustfmt", ], target_compatible_with = select({ "@rules_rust//rust/platform:aarch64-apple-darwin": [], "@rules_rust//rust/platform:aarch64-apple-ios": [], "@rules_rust//rust/platform:aarch64-apple-ios-sim": [], "@rules_rust//rust/platform:aarch64-linux-android": [], "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], "@rules_rust//rust/platform:aarch64-unknown-fuchsia": [], "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [], "@rules_rust//rust/platform:aarch64-unknown-uefi": [], "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:armv7-linux-androideabi": [], "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:i686-apple-darwin": [], "@rules_rust//rust/platform:i686-linux-android": [], "@rules_rust//rust/platform:i686-pc-windows-msvc": [], "@rules_rust//rust/platform:i686-unknown-freebsd": [], "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [], "@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [], "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], "@rules_rust//rust/platform:thumbv7em-none-eabi": [], "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [], "@rules_rust//rust/platform:wasm32-unknown-emscripten": [], "@rules_rust//rust/platform:wasm32-unknown-unknown": [], "@rules_rust//rust/platform:wasm32-wasip1": [], "@rules_rust//rust/platform:wasm32-wasip1-threads": [], "@rules_rust//rust/platform:wasm32-wasip2": [], "@rules_rust//rust/platform:x86_64-apple-darwin": [], "@rules_rust//rust/platform:x86_64-apple-ios": [], "@rules_rust//rust/platform:x86_64-linux-android": [], "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], "@rules_rust//rust/platform:x86_64-unknown-fuchsia": [], "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-none": [], "@rules_rust//rust/platform:x86_64-unknown-uefi": [], "//conditions:default": ["@platforms//:incompatible"], }), version = "0.16.0", deps = [ "@crates_vendor__allocator-api2-0.2.21//:allocator_api2", "@crates_vendor__equivalent-1.0.2//:equivalent", "@crates_vendor__foldhash-0.2.0//:foldhash", ], ) ================================================ FILE: bazel/cargo/remote/BUILD.log-0.4.27.bazel ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") load("@rules_rust//rust:defs.bzl", "rust_library") package(default_visibility = ["//visibility:public"]) cargo_toml_env_vars( name = "cargo_toml_env_vars", src = "Cargo.toml", ) rust_library( name = "log", srcs = glob( include = ["**/*.rs"], allow_empty = True, ), compile_data = glob( include = ["**"], allow_empty = True, exclude = [ "**/* *", ".tmp_git_root/**/*", "BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel", ], ), crate_root = "src/lib.rs", edition = "2021", rustc_env_files = [ ":cargo_toml_env_vars", ], rustc_flags = [ "--cap-lints=allow", ], tags = [ "cargo-bazel", "crate-name=log", "manual", "noclippy", "norustfmt", ], target_compatible_with = select({ "@rules_rust//rust/platform:aarch64-apple-darwin": [], "@rules_rust//rust/platform:aarch64-apple-ios": [], "@rules_rust//rust/platform:aarch64-apple-ios-sim": [], "@rules_rust//rust/platform:aarch64-linux-android": [], "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], "@rules_rust//rust/platform:aarch64-unknown-fuchsia": [], "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [], "@rules_rust//rust/platform:aarch64-unknown-uefi": [], "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:armv7-linux-androideabi": [], "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], "@rules_rust//rust/platform:i686-apple-darwin": [], "@rules_rust//rust/platform:i686-linux-android": [], "@rules_rust//rust/platform:i686-pc-windows-msvc": [], "@rules_rust//rust/platform:i686-unknown-freebsd": [], "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [], "@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu": [], "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [], "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], "@rules_rust//rust/platform:thumbv7em-none-eabi": [], "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [], "@rules_rust//rust/platform:wasm32-unknown-emscripten": [], "@rules_rust//rust/platform:wasm32-unknown-unknown": [], "@rules_rust//rust/platform:wasm32-wasip1": [], "@rules_rust//rust/platform:wasm32-wasip1-threads": [], "@rules_rust//rust/platform:wasm32-wasip2": [], "@rules_rust//rust/platform:x86_64-apple-darwin": [], "@rules_rust//rust/platform:x86_64-apple-ios": [], "@rules_rust//rust/platform:x86_64-linux-android": [], "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], "@rules_rust//rust/platform:x86_64-unknown-fuchsia": [], "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], "@rules_rust//rust/platform:x86_64-unknown-none": [], "@rules_rust//rust/platform:x86_64-unknown-uefi": [], "//conditions:default": ["@platforms//:incompatible"], }), version = "0.4.27", ) ================================================ FILE: bazel/cargo/remote/alias_rules.bzl ================================================ """Alias that transitions its target to `compilation_mode=opt`. Use `transition_alias="opt"` to enable.""" load("@rules_cc//cc:defs.bzl", "CcInfo") load("@rules_rust//rust:rust_common.bzl", "COMMON_PROVIDERS") def _transition_alias_impl(ctx): # `ctx.attr.actual` is a list of 1 item due to the transition providers = [ctx.attr.actual[0][provider] for provider in COMMON_PROVIDERS] if CcInfo in ctx.attr.actual[0]: providers.append(ctx.attr.actual[0][CcInfo]) return providers def _change_compilation_mode(compilation_mode): def _change_compilation_mode_impl(_settings, _attr): return { "//command_line_option:compilation_mode": compilation_mode, } return transition( implementation = _change_compilation_mode_impl, inputs = [], outputs = [ "//command_line_option:compilation_mode", ], ) def _transition_alias_rule(compilation_mode): return rule( implementation = _transition_alias_impl, provides = COMMON_PROVIDERS, attrs = { "actual": attr.label( mandatory = True, doc = "`rust_library()` target to transition to `compilation_mode=opt`.", providers = COMMON_PROVIDERS, cfg = _change_compilation_mode(compilation_mode), ), "_allowlist_function_transition": attr.label( default = "@bazel_tools//tools/allowlists/function_transition_allowlist", ), }, doc = "Transitions a Rust library crate to the `compilation_mode=opt`.", ) transition_alias_dbg = _transition_alias_rule("dbg") transition_alias_fastbuild = _transition_alias_rule("fastbuild") transition_alias_opt = _transition_alias_rule("opt") ================================================ FILE: bazel/cargo/remote/crates.bzl ================================================ ############################################################################### # @generated # This file is auto-generated by the cargo-bazel tool. # # DO NOT MODIFY: Local changes may be replaced in future executions. ############################################################################### """Rules for defining repositories for remote `crates_vendor` repositories""" load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") # buildifier: disable=bzl-visibility load("@proxy_wasm_rust_sdk//bazel/cargo/remote:defs.bzl", _crate_repositories = "crate_repositories") # buildifier: disable=bzl-visibility load("@rules_rust//crate_universe/private:crates_vendor.bzl", "crates_vendor_remote_repository") def crate_repositories(): """Generates repositories for vendored crates. Returns: A list of repos visible to the module through the module extension. """ maybe( crates_vendor_remote_repository, name = "crates_vendor", build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.bazel"), defs_module = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:defs.bzl"), ) direct_deps = [struct(repo = "crates_vendor", is_dev_dep = False)] direct_deps.extend(_crate_repositories()) return direct_deps ================================================ FILE: bazel/cargo/remote/defs.bzl ================================================ ############################################################################### # @generated # DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To # regenerate this file, run the following: # # bazel run @//bazel/cargo:crates_vendor ############################################################################### """ # `crates_repository` API - [aliases](#aliases) - [crate_deps](#crate_deps) - [all_crate_deps](#all_crate_deps) - [crate_repositories](#crate_repositories) """ load("@bazel_skylib//lib:selects.bzl", "selects") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") ############################################################################### # MACROS API ############################################################################### # An identifier that represent common dependencies (unconditional). _COMMON_CONDITION = "" def _flatten_dependency_maps(all_dependency_maps): """Flatten a list of dependency maps into one dictionary. Dependency maps have the following structure: ```python DEPENDENCIES_MAP = { # The first key in the map is a Bazel package # name of the workspace this file is defined in. "workspace_member_package": { # Not all dependencies are supported for all platforms. # the condition key is the condition required to be true # on the host platform. "condition": { # An alias to a crate target. # The label of the crate target the # Aliases are only crate names. # package name refers to. "package_name": "@full//:label", } } } ``` Args: all_dependency_maps (list): A list of dicts as described above Returns: dict: A dictionary as described above """ dependencies = {} for workspace_deps_map in all_dependency_maps: for pkg_name, conditional_deps_map in workspace_deps_map.items(): if pkg_name not in dependencies: non_frozen_map = dict() for key, values in conditional_deps_map.items(): non_frozen_map.update({key: dict(values.items())}) dependencies.setdefault(pkg_name, non_frozen_map) continue for condition, deps_map in conditional_deps_map.items(): # If the condition has not been recorded, do so and continue if condition not in dependencies[pkg_name]: dependencies[pkg_name].setdefault(condition, dict(deps_map.items())) continue # Alert on any miss-matched dependencies inconsistent_entries = [] for crate_name, crate_label in deps_map.items(): existing = dependencies[pkg_name][condition].get(crate_name) if existing and existing != crate_label: inconsistent_entries.append((crate_name, existing, crate_label)) dependencies[pkg_name][condition].update({crate_name: crate_label}) return dependencies def crate_deps(deps, package_name = None): """Finds the fully qualified label of the requested crates for the package where this macro is called. Args: deps (list): The desired list of crate targets. package_name (str, optional): The package name of the set of dependencies to look up. Defaults to `native.package_name()`. Returns: list: A list of labels to generated rust targets (str) """ if not deps: return [] if package_name == None: package_name = native.package_name() # Join both sets of dependencies dependencies = _flatten_dependency_maps([ _NORMAL_DEPENDENCIES, _NORMAL_DEV_DEPENDENCIES, _PROC_MACRO_DEPENDENCIES, _PROC_MACRO_DEV_DEPENDENCIES, _BUILD_DEPENDENCIES, _BUILD_PROC_MACRO_DEPENDENCIES, ]).pop(package_name, {}) # Combine all conditional packages so we can easily index over a flat list # TODO: Perhaps this should actually return select statements and maintain # the conditionals of the dependencies flat_deps = {} for deps_set in dependencies.values(): for crate_name, crate_label in deps_set.items(): flat_deps.update({crate_name: crate_label}) missing_crates = [] crate_targets = [] for crate_target in deps: if crate_target not in flat_deps: missing_crates.append(crate_target) else: crate_targets.append(flat_deps[crate_target]) if missing_crates: fail("Could not find crates `{}` among dependencies of `{}`. Available dependencies were `{}`".format( missing_crates, package_name, dependencies, )) return crate_targets def all_crate_deps( normal = False, normal_dev = False, proc_macro = False, proc_macro_dev = False, build = False, build_proc_macro = False, package_name = None): """Finds the fully qualified label of all requested direct crate dependencies \ for the package where this macro is called. If no parameters are set, all normal dependencies are returned. Setting any one flag will otherwise impact the contents of the returned list. Args: normal (bool, optional): If True, normal dependencies are included in the output list. normal_dev (bool, optional): If True, normal dev dependencies will be included in the output list.. proc_macro (bool, optional): If True, proc_macro dependencies are included in the output list. proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are included in the output list. build (bool, optional): If True, build dependencies are included in the output list. build_proc_macro (bool, optional): If True, build proc_macro dependencies are included in the output list. package_name (str, optional): The package name of the set of dependencies to look up. Defaults to `native.package_name()` when unset. Returns: list: A list of labels to generated rust targets (str) """ if package_name == None: package_name = native.package_name() # Determine the relevant maps to use all_dependency_maps = [] if normal: all_dependency_maps.append(_NORMAL_DEPENDENCIES) if normal_dev: all_dependency_maps.append(_NORMAL_DEV_DEPENDENCIES) if proc_macro: all_dependency_maps.append(_PROC_MACRO_DEPENDENCIES) if proc_macro_dev: all_dependency_maps.append(_PROC_MACRO_DEV_DEPENDENCIES) if build: all_dependency_maps.append(_BUILD_DEPENDENCIES) if build_proc_macro: all_dependency_maps.append(_BUILD_PROC_MACRO_DEPENDENCIES) # Default to always using normal dependencies if not all_dependency_maps: all_dependency_maps.append(_NORMAL_DEPENDENCIES) dependencies = _flatten_dependency_maps(all_dependency_maps).pop(package_name, None) if not dependencies: if dependencies == None: fail("Tried to get all_crate_deps for package " + package_name + " but that package had no Cargo.toml file") else: return [] crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values()) for condition, deps in dependencies.items(): crate_deps += selects.with_or({ tuple(_CONDITIONS[condition]): deps.values(), "//conditions:default": [], }) return crate_deps def aliases( normal = False, normal_dev = False, proc_macro = False, proc_macro_dev = False, build = False, build_proc_macro = False, package_name = None): """Produces a map of Crate alias names to their original label If no dependency kinds are specified, `normal` and `proc_macro` are used by default. Setting any one flag will otherwise determine the contents of the returned dict. Args: normal (bool, optional): If True, normal dependencies are included in the output list. normal_dev (bool, optional): If True, normal dev dependencies will be included in the output list.. proc_macro (bool, optional): If True, proc_macro dependencies are included in the output list. proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are included in the output list. build (bool, optional): If True, build dependencies are included in the output list. build_proc_macro (bool, optional): If True, build proc_macro dependencies are included in the output list. package_name (str, optional): The package name of the set of dependencies to look up. Defaults to `native.package_name()` when unset. Returns: dict: The aliases of all associated packages """ if package_name == None: package_name = native.package_name() # Determine the relevant maps to use all_aliases_maps = [] if normal: all_aliases_maps.append(_NORMAL_ALIASES) if normal_dev: all_aliases_maps.append(_NORMAL_DEV_ALIASES) if proc_macro: all_aliases_maps.append(_PROC_MACRO_ALIASES) if proc_macro_dev: all_aliases_maps.append(_PROC_MACRO_DEV_ALIASES) if build: all_aliases_maps.append(_BUILD_ALIASES) if build_proc_macro: all_aliases_maps.append(_BUILD_PROC_MACRO_ALIASES) # Default to always using normal aliases if not all_aliases_maps: all_aliases_maps.append(_NORMAL_ALIASES) all_aliases_maps.append(_PROC_MACRO_ALIASES) aliases = _flatten_dependency_maps(all_aliases_maps).pop(package_name, None) if not aliases: return dict() common_items = aliases.pop(_COMMON_CONDITION, {}).items() # If there are only common items in the dictionary, immediately return them if not len(aliases.keys()) == 1: return dict(common_items) # Build a single select statement where each conditional has accounted for the # common set of aliases. crate_aliases = {"//conditions:default": dict(common_items)} for condition, deps in aliases.items(): condition_triples = _CONDITIONS[condition] for triple in condition_triples: if triple in crate_aliases: crate_aliases[triple].update(deps) else: crate_aliases.update({triple: dict(deps.items() + common_items)}) return select(crate_aliases) ############################################################################### # WORKSPACE MEMBER DEPS AND ALIASES ############################################################################### _NORMAL_DEPENDENCIES = { "": { _COMMON_CONDITION: { "hashbrown": Label("@crates_vendor//:hashbrown-0.16.0"), "log": Label("@crates_vendor//:log-0.4.27"), }, }, } _NORMAL_ALIASES = { "": { _COMMON_CONDITION: { }, }, } _NORMAL_DEV_DEPENDENCIES = { "": { }, } _NORMAL_DEV_ALIASES = { "": { }, } _PROC_MACRO_DEPENDENCIES = { "": { }, } _PROC_MACRO_ALIASES = { "": { }, } _PROC_MACRO_DEV_DEPENDENCIES = { "": { }, } _PROC_MACRO_DEV_ALIASES = { "": { }, } _BUILD_DEPENDENCIES = { "": { }, } _BUILD_ALIASES = { "": { }, } _BUILD_PROC_MACRO_DEPENDENCIES = { "": { }, } _BUILD_PROC_MACRO_ALIASES = { "": { }, } _CONDITIONS = { "aarch64-apple-darwin": ["@rules_rust//rust/platform:aarch64-apple-darwin"], "aarch64-apple-ios": ["@rules_rust//rust/platform:aarch64-apple-ios"], "aarch64-apple-ios-sim": ["@rules_rust//rust/platform:aarch64-apple-ios-sim"], "aarch64-linux-android": ["@rules_rust//rust/platform:aarch64-linux-android"], "aarch64-pc-windows-msvc": ["@rules_rust//rust/platform:aarch64-pc-windows-msvc"], "aarch64-unknown-fuchsia": ["@rules_rust//rust/platform:aarch64-unknown-fuchsia"], "aarch64-unknown-linux-gnu": ["@rules_rust//rust/platform:aarch64-unknown-linux-gnu"], "aarch64-unknown-nixos-gnu": ["@rules_rust//rust/platform:aarch64-unknown-nixos-gnu"], "aarch64-unknown-nto-qnx710": ["@rules_rust//rust/platform:aarch64-unknown-nto-qnx710"], "aarch64-unknown-uefi": ["@rules_rust//rust/platform:aarch64-unknown-uefi"], "arm-unknown-linux-gnueabi": ["@rules_rust//rust/platform:arm-unknown-linux-gnueabi"], "armv7-linux-androideabi": ["@rules_rust//rust/platform:armv7-linux-androideabi"], "armv7-unknown-linux-gnueabi": ["@rules_rust//rust/platform:armv7-unknown-linux-gnueabi"], "i686-apple-darwin": ["@rules_rust//rust/platform:i686-apple-darwin"], "i686-linux-android": ["@rules_rust//rust/platform:i686-linux-android"], "i686-pc-windows-msvc": ["@rules_rust//rust/platform:i686-pc-windows-msvc"], "i686-unknown-freebsd": ["@rules_rust//rust/platform:i686-unknown-freebsd"], "i686-unknown-linux-gnu": ["@rules_rust//rust/platform:i686-unknown-linux-gnu"], "powerpc-unknown-linux-gnu": ["@rules_rust//rust/platform:powerpc-unknown-linux-gnu"], "riscv32imc-unknown-none-elf": ["@rules_rust//rust/platform:riscv32imc-unknown-none-elf"], "riscv64gc-unknown-linux-gnu": ["@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu"], "riscv64gc-unknown-none-elf": ["@rules_rust//rust/platform:riscv64gc-unknown-none-elf"], "s390x-unknown-linux-gnu": ["@rules_rust//rust/platform:s390x-unknown-linux-gnu"], "thumbv7em-none-eabi": ["@rules_rust//rust/platform:thumbv7em-none-eabi"], "thumbv8m.main-none-eabi": ["@rules_rust//rust/platform:thumbv8m.main-none-eabi"], "wasm32-unknown-emscripten": ["@rules_rust//rust/platform:wasm32-unknown-emscripten"], "wasm32-unknown-unknown": ["@rules_rust//rust/platform:wasm32-unknown-unknown"], "wasm32-wasip1": ["@rules_rust//rust/platform:wasm32-wasip1"], "wasm32-wasip1-threads": ["@rules_rust//rust/platform:wasm32-wasip1-threads"], "wasm32-wasip2": ["@rules_rust//rust/platform:wasm32-wasip2"], "x86_64-apple-darwin": ["@rules_rust//rust/platform:x86_64-apple-darwin"], "x86_64-apple-ios": ["@rules_rust//rust/platform:x86_64-apple-ios"], "x86_64-linux-android": ["@rules_rust//rust/platform:x86_64-linux-android"], "x86_64-pc-windows-msvc": ["@rules_rust//rust/platform:x86_64-pc-windows-msvc"], "x86_64-unknown-freebsd": ["@rules_rust//rust/platform:x86_64-unknown-freebsd"], "x86_64-unknown-fuchsia": ["@rules_rust//rust/platform:x86_64-unknown-fuchsia"], "x86_64-unknown-linux-gnu": ["@rules_rust//rust/platform:x86_64-unknown-linux-gnu"], "x86_64-unknown-nixos-gnu": ["@rules_rust//rust/platform:x86_64-unknown-nixos-gnu"], "x86_64-unknown-none": ["@rules_rust//rust/platform:x86_64-unknown-none"], "x86_64-unknown-uefi": ["@rules_rust//rust/platform:x86_64-unknown-uefi"], } ############################################################################### def crate_repositories(): """A macro for defining repositories for all generated crates. Returns: A list of repos visible to the module through the module extension. """ maybe( http_archive, name = "crates_vendor__allocator-api2-0.2.21", sha256 = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923", type = "tar.gz", urls = ["https://static.crates.io/crates/allocator-api2/0.2.21/download"], strip_prefix = "allocator-api2-0.2.21", build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.allocator-api2-0.2.21.bazel"), ) maybe( http_archive, name = "crates_vendor__equivalent-1.0.2", sha256 = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f", type = "tar.gz", urls = ["https://static.crates.io/crates/equivalent/1.0.2/download"], strip_prefix = "equivalent-1.0.2", build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.equivalent-1.0.2.bazel"), ) maybe( http_archive, name = "crates_vendor__foldhash-0.2.0", sha256 = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb", type = "tar.gz", urls = ["https://static.crates.io/crates/foldhash/0.2.0/download"], strip_prefix = "foldhash-0.2.0", build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.foldhash-0.2.0.bazel"), ) maybe( http_archive, name = "crates_vendor__hashbrown-0.16.0", sha256 = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d", type = "tar.gz", urls = ["https://static.crates.io/crates/hashbrown/0.16.0/download"], strip_prefix = "hashbrown-0.16.0", build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.hashbrown-0.16.0.bazel"), ) maybe( http_archive, name = "crates_vendor__log-0.4.27", sha256 = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94", type = "tar.gz", urls = ["https://static.crates.io/crates/log/0.4.27/download"], strip_prefix = "log-0.4.27", build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.log-0.4.27.bazel"), ) return [ struct(repo = "crates_vendor__hashbrown-0.16.0", is_dev_dep = False), struct(repo = "crates_vendor__log-0.4.27", is_dev_dep = False), ] ================================================ FILE: bazel/dependencies_bazel.bzl ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@bazel_features//:deps.bzl", "bazel_features_deps") def proxy_wasm_rust_sdk_dependencies_bazel(): bazel_features_deps() ================================================ FILE: bazel/dependencies_compat.bzl ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@rules_cc//cc:extensions.bzl", "compatibility_proxy_repo") def proxy_wasm_rust_sdk_dependencies_compat(): compatibility_proxy_repo() ================================================ FILE: bazel/dependencies_crates.bzl ================================================ # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@proxy_wasm_rust_sdk//bazel/cargo/remote:defs.bzl", "crate_repositories") load("@rules_rust//crate_universe:repositories.bzl", "crate_universe_dependencies") load("@rules_rust//rust:repositories.bzl", "rust_repositories") def proxy_wasm_rust_sdk_dependencies_crates(): rust_repositories(versions = ["1.91.1"]) crate_universe_dependencies() crate_repositories() ================================================ FILE: bazel/extensions.bzl ================================================ # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@bazel_features//:features.bzl", "bazel_features") load("@proxy_wasm_rust_sdk//bazel/cargo/remote:crates.bzl", "crate_repositories") def _crates_deps_impl(module_ctx): deps = [] for repo in crate_repositories(): if not repo.is_dev_dep: deps.append(repo.repo) return module_ctx.extension_metadata( reproducible = bazel_features.external_deps.extension_metadata_has_reproducible, root_module_direct_deps = deps, root_module_direct_dev_deps = [], ) crates_deps = module_extension( doc = "Dependencies for the Proxy-Wasm Rust SDK.", implementation = _crates_deps_impl, ) ================================================ FILE: bazel/repositories.bzl ================================================ # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") def proxy_wasm_rust_sdk_repositories(): maybe( http_archive, name = "bazel_features", sha256 = "07271d0f6b12633777b69020c4cb1eb67b1939c0cf84bb3944dc85cc250c0c01", url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.38.0/bazel_features-v1.38.0.tar.gz", strip_prefix = "bazel_features-1.38.0", ) maybe( http_archive, name = "rules_cc", sha256 = "a2fdfde2ab9b2176bd6a33afca14458039023edb1dd2e73e6823810809df4027", url = "https://github.com/bazelbuild/rules_cc/releases/download/0.2.14/rules_cc-0.2.14.tar.gz", strip_prefix = "rules_cc-0.2.14", ) maybe( http_archive, name = "rules_rust", integrity = "sha256-yKqAbPYGZnmsI0YyQe6ArWkiZdrQRl9RERy74wuJA1I=", url = "https://github.com/bazelbuild/rules_rust/releases/download/0.68.1/rules_rust-0.68.1.tar.gz", ) maybe( http_archive, name = "bazel_skylib", sha256 = "6e78f0e57de26801f6f564fa7c4a48dc8b36873e416257a92bbb0937eeac8446", url = "https://github.com/bazelbuild/bazel-skylib/releases/download/1.8.2/bazel-skylib-1.8.2.tar.gz", ) ================================================ FILE: build.rs ================================================ // Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=RUSTFLAGS"); println!("cargo:rustc-check-cfg=cfg(nightly)"); println!("cargo:rustc-check-cfg=cfg(wasi_exec_model_reactor)"); if let Some(toolchain) = std::env::var_os("RUSTUP_TOOLCHAIN") { if toolchain.to_string_lossy().contains("nightly") { println!("cargo:rustc-cfg=nightly"); } } if let Some(target_os) = std::env::var_os("CARGO_CFG_TARGET_OS") { if target_os != "wasi" { return; } } if let Some(rustflags) = std::env::var_os("CARGO_ENCODED_RUSTFLAGS") { for flag in rustflags.to_string_lossy().split('\x1f') { if flag.ends_with("wasi-exec-model=reactor") { println!("cargo:rustc-cfg=wasi_exec_model_reactor"); return; } } } } ================================================ FILE: examples/envoy_filter_metadata/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-envoy-filter-metadata" version = "0.0.1" authors = ["Martijn Swaagma "] description = "Proxy-Wasm plugin example: Envoy filter metadata" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] proxy-wasm = { path = "../../" } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/envoy_filter_metadata/README.md ================================================ ## Proxy-Wasm plugin example: Envoy metadata Proxy-Wasm plugin that demonstrates reading metadata set by other Envoy filters. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` Send a HTTP request to `localhost:10000` that will return the configured response. ```sh $ curl localhost:10000 Welcome, set the `x-custom-metadata` header to change the response! ``` Send a HTTP request to `localhost:10000` with a `x-custom-metadata` header value to get the uppercased value in the response. The response will also contain a response header `uppercased-metadata: SOME-VALUE`. ```sh $ curl localhost:10000 -H "x-custom-metadata: some-value" Custom response with Envoy metadata: "SOME-VALUE" ``` ================================================ FILE: examples/envoy_filter_metadata/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/envoy_filter_metadata/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_resources: listeners: address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_routes virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/" direct_response: status: 200 body: inline_string: "Welcome, set the `x-custom-metadata` header to change the response!\n" http_filters: # Set uppercase metadata in Lua filter - name: envoy.filters.http.lua typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua default_source_code: inline_string: | function envoy_on_request(request_handle) local headers = request_handle:headers() local data = headers:get("x-custom-metadata") if data then request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua", "uppercased-custom-metadata", string.upper(data)) end request_handle:logInfo("Metadata set by lua filter") end # Read it from a WASM filter - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "envoy_metadata_filter" vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_envoy_filter_metadata.wasm" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ================================================ FILE: examples/envoy_filter_metadata/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use proxy_wasm::traits::*; use proxy_wasm::types::*; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_http_context(|_, _| -> Box { Box::new(MetadataHttp {}) }); }} struct MetadataHttp {} impl Context for MetadataHttp {} impl HttpContext for MetadataHttp { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { // Read data set by the lua filter match self.get_property(vec![ "metadata", "filter_metadata", "envoy.filters.http.lua", "uppercased-custom-metadata", ]) { Some(metadata) => match String::from_utf8(metadata) { Ok(data) => { self.send_http_response( 200, vec![("Powered-By", "proxy-wasm"), ("uppercased-metadata", &data)], Some(format!("Custom response with Envoy metadata: {data:?}\n").as_bytes()), ); Action::Pause } _ => Action::Continue, }, _ => Action::Continue, } } } ================================================ FILE: examples/grpc_auth_random/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-grpc-auth-random" version = "0.0.1" description = "Proxy-Wasm plugin example: gRPC auth (random)" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] log = "0.4" proxy-wasm = { path = "../../" } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/grpc_auth_random/README.md ================================================ ## Proxy-Wasm plugin example: gRPC auth (random) Proxy-Wasm plugin that grants access based on a result of gRPC callout. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` #### Access granted. Send gRPC request to `localhost:10000` service `hello.HelloService`: ```sh $ grpcurl -d '{"greeting": "Rust"}' -plaintext localhost:10000 hello.HelloService/SayHello { "reply": "hello Rust" } ``` Expected Envoy logs: ```console [...] wasm log grpc_auth_random: Access granted. ``` #### Access forbidden. Send gRPC request to `localhost:10000` service `hello.HelloService`: ```sh $ grpcurl -d '{"greeting": "Rust"}' -plaintext localhost:10000 hello.HelloService/SayHello ERROR: Code: Aborted Message: Aborted by Proxy-Wasm! ``` Expected Envoy logs: ```console [...] wasm log grpc_auth_random: Access forbidden. ``` ================================================ FILE: examples/grpc_auth_random/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh depends_on: - grpcbin grpcbin: image: kong/grpcbin hostname: grpcbin ports: - "9000:9000" networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/grpc_auth_random/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_resources: listeners: address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_routes virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/" route: cluster: grpcbin http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "grpc_auth_random" vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_grpc_auth_random.wasm" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: grpcbin connect_timeout: 5s type: STRICT_DNS lb_policy: ROUND_ROBIN http2_protocol_options: {} load_assignment: cluster_name: grpcbin endpoints: - lb_endpoints: - endpoint: address: socket_address: address: grpcbin port_value: 9000 ================================================ FILE: examples/grpc_auth_random/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use log::info; use proxy_wasm::traits::*; use proxy_wasm::types::*; use std::time::Duration; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_http_context(|_, _| -> Box { Box::new(GrpcAuthRandom) }); }} struct GrpcAuthRandom; impl HttpContext for GrpcAuthRandom { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { match self.get_http_request_header("content-type") { Some(value) if value.starts_with("application/grpc") => {} _ => { // Reject non-gRPC clients. self.send_http_response( 503, vec![("Powered-By", "proxy-wasm")], Some(b"Service accessible only to gRPC clients.\n"), ); return Action::Pause; } } match self.get_http_request_header(":path") { Some(value) if value.starts_with("/grpc.reflection") => { // Always allow gRPC calls to the reflection API. Action::Continue } _ => { // Allow other gRPC calls based on the result of grpcbin.GRPCBin/RandomError. self.dispatch_grpc_call( "grpcbin", "grpcbin.GRPCBin", "RandomError", vec![], None, Duration::from_secs(1), ) .unwrap(); Action::Pause } } } fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { self.set_http_response_header("Powered-By", Some("proxy-wasm")); Action::Continue } } impl Context for GrpcAuthRandom { fn on_grpc_call_response(&mut self, _: u32, status_code: u32, _: usize) { #[allow(unknown_lints, clippy::manual_is_multiple_of)] if status_code % 2 == 0 { info!("Access granted."); self.resume_http_request(); } else { info!("Access forbidden."); self.send_grpc_response( GrpcStatusCode::Aborted, Some("Aborted by Proxy-Wasm!"), vec![("Powered-By", b"proxy-wasm")], ); } } } ================================================ FILE: examples/hello_world/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-hello-world" version = "0.0.1" authors = ["Piotr Sikora "] description = "Proxy-Wasm plugin example: Hello World" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] cfg-if = "1.0" chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } log = "0.4" proxy-wasm = { path = "../../" } [target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dependencies] getrandom = "0.4" [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/hello_world/README.md ================================================ ## Proxy-Wasm plugin example: Hello World Proxy-Wasm background service plugin that logs time and random numbers. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` Expected Envoy logs (new line generated every 5s): ```console [...] wasm log: Hello, World! [...] wasm log: It's 2022-11-22 03:39:17.849616 UTC, your lucky number is 41. [...] wasm log: It's 2022-11-22 03:39:22.846531 UTC, your lucky number is 28. [...] wasm log: It's 2022-11-22 03:39:27.847489 UTC, your lucky number is 102. [...] wasm log: It's 2022-11-22 03:39:32.848443 UTC, your lucky number is 250. ``` ================================================ FILE: examples/hello_world/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/hello_world/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. bootstrap_extensions: - name: envoy.bootstrap.wasm typed_config: "@type": type.googleapis.com/envoy.extensions.wasm.v3.WasmService singleton: true config: name: "hello_world" vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_hello_world.wasm" ================================================ FILE: examples/hello_world/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use cfg_if::cfg_if; use chrono::{DateTime, Utc}; use log::info; use proxy_wasm::traits::*; use proxy_wasm::types::*; use std::time::Duration; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box { Box::new(HelloWorld) }); }} struct HelloWorld; impl Context for HelloWorld {} impl RootContext for HelloWorld { fn on_vm_start(&mut self, _: usize) -> bool { info!("Hello, World!"); self.set_tick_period(Duration::from_secs(5)); true } fn on_tick(&mut self) { cfg_if! { if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { let now: DateTime = self.get_current_time().into(); info!("It's {}, there is no lucky number.", now); } else { let now: DateTime = Utc::now(); let mut buf = [0u8; 1]; getrandom::fill(&mut buf).unwrap(); info!("It's {}, your lucky number is {}.", now, buf[0]); } } } } ================================================ FILE: examples/http_auth_random/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-http-auth-random" version = "0.0.1" authors = ["Piotr Sikora "] description = "Proxy-Wasm plugin example: HTTP auth (random)" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] log = "0.4" proxy-wasm = { path = "../../" } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/http_auth_random/README.md ================================================ ## Proxy-Wasm plugin example: HTTP auth (random) Proxy-Wasm plugin that grants access based on a result of HTTP callout. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` #### Access granted. Send HTTP request to `localhost:10000/headers`: ```sh $ curl localhost:10000/headers { "headers": { "Accept": "*/*", "Host": "localhost", "User-Agent": "curl/7.81.0", "X-Amzn-Trace-Id": "Root=1-637c4767-6e31776a0b407a0219b5b570", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" } } ``` Expected Envoy logs: ```console [...] wasm log http_auth_random: Access granted. ``` #### Access forbidden. Send HTTP request to `localhost:10000/headers`: ```sh $ curl localhost:10000/headers Access forbidden. ``` Expected Envoy logs: ```console [...] wasm log http_auth_random: Access forbidden. ``` ================================================ FILE: examples/http_auth_random/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh depends_on: - httpbin httpbin: image: mccutchen/go-httpbin hostname: httpbin ports: - "8080:8080" networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/http_auth_random/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_resources: listeners: address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_routes virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/" route: cluster: httpbin http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "http_auth_random" vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_http_auth_random.wasm" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: httpbin connect_timeout: 5s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: httpbin endpoints: - lb_endpoints: - endpoint: address: socket_address: address: httpbin port_value: 8080 ================================================ FILE: examples/http_auth_random/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use log::info; use proxy_wasm::traits::*; use proxy_wasm::types::*; use std::time::Duration; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_http_context(|_, _| -> Box { Box::new(HttpAuthRandom) }); }} struct HttpAuthRandom; impl HttpContext for HttpAuthRandom { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { self.dispatch_http_call( "httpbin", vec![ (":method", "GET"), (":path", "/bytes/1"), (":authority", "httpbin.org"), ], None, vec![], Duration::from_secs(1), ) .unwrap(); Action::Pause } fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { self.set_http_response_header("Powered-By", Some("proxy-wasm")); Action::Continue } } impl Context for HttpAuthRandom { fn on_http_call_response(&mut self, _: u32, _: usize, body_size: usize, _: usize) { if let Some(body) = self.get_http_call_response_body(0, body_size) { #[allow(unknown_lints, clippy::manual_is_multiple_of)] if !body.is_empty() && body[0] % 2 == 0 { info!("Access granted."); self.resume_http_request(); return; } } info!("Access forbidden."); self.send_http_response( 403, vec![("Powered-By", "proxy-wasm")], Some(b"Access forbidden.\n"), ); } } ================================================ FILE: examples/http_body/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-http-body" version = "0.0.1" authors = ["Piotr Sikora "] description = "Proxy-Wasm plugin example: HTTP body" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] proxy-wasm = { path = "../../" } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/http_body/README.md ================================================ ## Proxy-Wasm plugin example: HTTP body Proxy-Wasm plugin that redacts sensitive HTTP responses. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` #### Response without secrets. Send HTTP request to `localhost:10000/hello`: ```sh $ curl localhost:10000/hello Everyone may read this message. ``` #### Response with (redacted) secrets. Send HTTP request to `localhost:10000/secret`: ```sh $ curl localhost:10000/secret Original message body (50 bytes) redacted. ``` ================================================ FILE: examples/http_body/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/http_body/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_resources: listeners: address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_routes virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/secret" direct_response: status: 200 body: inline_string: "This secret message should not be read by anyone.\n" - match: prefix: "/" direct_response: status: 200 body: inline_string: "Everyone may read this message.\n" http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "http_body" vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_http_body.wasm" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ================================================ FILE: examples/http_body/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use proxy_wasm::traits::*; use proxy_wasm::types::*; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box { Box::new(HttpBodyRoot) }); }} struct HttpBodyRoot; impl Context for HttpBodyRoot {} impl RootContext for HttpBodyRoot { fn get_type(&self) -> Option { Some(ContextType::HttpContext) } fn create_http_context(&self, _: u32) -> Option> { Some(Box::new(HttpBody)) } } struct HttpBody; impl Context for HttpBody {} impl HttpContext for HttpBody { fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { // If there is a Content-Length header and we change the length of // the body later, then clients will break. So remove it. // We must do this here, because once we exit this function we // can no longer modify the response headers. self.set_http_response_header("content-length", None); Action::Continue } fn on_http_response_body(&mut self, body_size: usize, end_of_stream: bool) -> Action { if !end_of_stream { // Wait -- we'll be called again when the complete body is buffered // at the host side. return Action::Pause; } // Replace the message body if it contains the text "secret". // Since we returned "Pause" previuously, this will return the whole body. if let Some(body_bytes) = self.get_http_response_body(0, body_size) { let body_str = String::from_utf8(body_bytes).unwrap(); if body_str.contains("secret") { let new_body = format!("Original message body ({body_size} bytes) redacted.\n"); self.set_http_response_body(0, body_size, &new_body.into_bytes()); } } Action::Continue } } ================================================ FILE: examples/http_config/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-http-config" version = "0.0.1" authors = ["Piotr Sikora "] description = "Proxy-Wasm plugin example: HTTP config" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] proxy-wasm = { path = "../../" } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/http_config/README.md ================================================ ## Proxy-Wasm plugin example: HTTP config Proxy-Wasm plugin that injects HTTP response header with a value from Envoy config. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` Send HTTP request to `localhost:10000/hello`: ```sh $ curl -I localhost:10000/hello HTTP/1.1 200 OK content-length: 40 content-type: text/plain custom-header: The secret to life is meaningless unless you discover it yourself date: Tue, 22 Nov 2022 04:09:05 GMT server: envoy ``` ================================================ FILE: examples/http_config/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/http_config/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_resources: listeners: address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_routes virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/" direct_response: status: 200 body: inline_string: "Inspect the HTTP header: custom-header.\n" http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "http_config" configuration: "@type": "type.googleapis.com/google.protobuf.StringValue" value: The secret to life is meaningless unless you discover it yourself vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_http_config.wasm" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ================================================ FILE: examples/http_config/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use proxy_wasm::traits::*; use proxy_wasm::types::*; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box { Box::new(HttpConfigHeaderRoot { header_content: String::new(), }) }); }} struct HttpConfigHeader { header_content: String, } impl Context for HttpConfigHeader {} impl HttpContext for HttpConfigHeader { fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { self.add_http_response_header("custom-header", self.header_content.as_str()); Action::Continue } } struct HttpConfigHeaderRoot { header_content: String, } impl Context for HttpConfigHeaderRoot {} impl RootContext for HttpConfigHeaderRoot { fn on_configure(&mut self, _: usize) -> bool { if let Some(config_bytes) = self.get_plugin_configuration() { self.header_content = String::from_utf8(config_bytes).unwrap() } true } fn create_http_context(&self, _: u32) -> Option> { Some(Box::new(HttpConfigHeader { header_content: self.header_content.clone(), })) } fn get_type(&self) -> Option { Some(ContextType::HttpContext) } } ================================================ FILE: examples/http_headers/Cargo.toml ================================================ # Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [package] publish = false name = "proxy-wasm-example-http-headers" version = "0.0.1" authors = ["Piotr Sikora "] description = "Proxy-Wasm plugin example: HTTP headers" license = "Apache-2.0" edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] log = "0.4" proxy-wasm = { path = "../../" } [profile.release] lto = true opt-level = 3 codegen-units = 1 panic = "abort" strip = "debuginfo" ================================================ FILE: examples/http_headers/README.md ================================================ ## Proxy-Wasm plugin example: HTTP headers Proxy-Wasm plugin that logs HTTP request/response headers. ### Building ```sh $ cargo build --target wasm32-wasip1 --release ``` ### Using in Envoy This example can be run with [`docker compose`](https://docs.docker.com/compose/install/) and has a matching Envoy configuration. ```sh $ docker compose up ``` Send HTTP request to `localhost:10000/hello`: ```sh $ curl localhost:10000/hello Hello, World! ``` Expected Envoy logs: ```console [...] wasm log http_headers: #2 -> :authority: localhost:10000 [...] wasm log http_headers: #2 -> :path: /hello [...] wasm log http_headers: #2 -> :method: GET [...] wasm log http_headers: #2 -> :scheme: http [...] wasm log http_headers: #2 -> user-agent: curl/7.81.0 [...] wasm log http_headers: #2 -> accept: */* [...] wasm log http_headers: #2 -> x-forwarded-proto: http [...] wasm log http_headers: #2 -> x-request-id: 3ed6eb3b-ddce-4fdc-8862-ddb8f168d406 [...] wasm log http_headers: #2 <- :status: 200 [...] wasm log http_headers: #2 <- hello: World [...] wasm log http_headers: #2 <- powered-by: proxy-wasm [...] wasm log http_headers: #2 <- content-length: 14 [...] wasm log http_headers: #2 <- content-type: text/plain [...] wasm log http_headers: #2 completed. ``` ================================================ FILE: examples/http_headers/docker-compose.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. services: envoy: image: envoyproxy/envoy:v1.34-latest hostname: envoy ports: - "10000:10000" volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml - ./target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins networks: - envoymesh networks: envoymesh: {} ================================================ FILE: examples/http_headers/envoy.yaml ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_resources: listeners: address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_routes virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/" direct_response: status: 200 body: inline_string: "Request /hello and be welcomed!\n" http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "http_headers" vm_config: runtime: "envoy.wasm.runtime.v8" code: local: filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_http_headers.wasm" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ================================================ FILE: examples/http_headers/src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use log::info; use proxy_wasm::traits::*; use proxy_wasm::types::*; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box { Box::new(HttpHeadersRoot) }); }} struct HttpHeadersRoot; impl Context for HttpHeadersRoot {} impl RootContext for HttpHeadersRoot { fn get_type(&self) -> Option { Some(ContextType::HttpContext) } fn create_http_context(&self, context_id: u32) -> Option> { Some(Box::new(HttpHeaders { context_id })) } } struct HttpHeaders { context_id: u32, } impl Context for HttpHeaders {} impl HttpContext for HttpHeaders { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { for (name, value) in &self.get_http_request_headers() { info!("#{} -> {}: {}", self.context_id, name, value); } match self.get_http_request_header(":path") { Some(path) if path == "/hello" => { self.send_http_response( 200, vec![("Hello", "World"), ("Powered-By", "proxy-wasm")], Some(b"Hello, World!\n"), ); Action::Pause } _ => Action::Continue, } } fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { for (name, value) in &self.get_http_response_headers() { info!("#{} <- {}: {}", self.context_id, name, value); } Action::Continue } fn on_log(&mut self) { info!("#{} completed.", self.context_id); } } ================================================ FILE: src/allocator.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::mem::MaybeUninit; #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), export_name = "malloc" )] #[cfg_attr(not(all(target_arch = "wasm32", target_os = "unknown")), no_mangle)] pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 { let mut vec: Vec> = Vec::with_capacity(size); unsafe { vec.set_len(size); } let slice = vec.into_boxed_slice(); Box::into_raw(slice) as *mut u8 } ================================================ FILE: src/dispatcher.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::hostcalls; use crate::traits::*; use crate::types::*; use hashbrown::HashMap; use log::trace; use std::cell::{Cell, RefCell}; thread_local! { static DISPATCHER: Dispatcher = Dispatcher::new(); } pub(crate) fn set_root_context(callback: NewRootContext) { DISPATCHER.with(|dispatcher| dispatcher.set_root_context(callback)); } pub(crate) fn set_stream_context(callback: NewStreamContext) { DISPATCHER.with(|dispatcher| dispatcher.set_stream_context(callback)); } pub(crate) fn set_http_context(callback: NewHttpContext) { DISPATCHER.with(|dispatcher| dispatcher.set_http_context(callback)); } pub(crate) fn register_callout(token_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.register_callout(token_id)); } pub(crate) fn register_grpc_callout(token_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.register_grpc_callout(token_id)); } pub(crate) fn register_grpc_stream(token_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.register_grpc_stream(token_id)); } struct NoopRoot; impl Context for NoopRoot {} impl RootContext for NoopRoot {} struct Dispatcher { new_root: Cell>, roots: RefCell>>, new_stream: Cell>, streams: RefCell>>, new_http_stream: Cell>, http_streams: RefCell>>, active_id: Cell, callouts: RefCell>, grpc_callouts: RefCell>, grpc_streams: RefCell>, } impl Dispatcher { fn new() -> Dispatcher { Dispatcher { new_root: Cell::new(None), roots: RefCell::new(HashMap::new()), new_stream: Cell::new(None), streams: RefCell::new(HashMap::new()), new_http_stream: Cell::new(None), http_streams: RefCell::new(HashMap::new()), active_id: Cell::new(0), callouts: RefCell::new(HashMap::new()), grpc_callouts: RefCell::new(HashMap::new()), grpc_streams: RefCell::new(HashMap::new()), } } fn set_root_context(&self, callback: NewRootContext) { self.new_root.set(Some(callback)); } fn set_stream_context(&self, callback: NewStreamContext) { self.new_stream.set(Some(callback)); } fn set_http_context(&self, callback: NewHttpContext) { self.new_http_stream.set(Some(callback)); } fn register_callout(&self, token_id: u32) { if self .callouts .borrow_mut() .insert(token_id, self.active_id.get()) .is_some() { panic!("duplicate token_id") } } fn register_grpc_stream(&self, token_id: u32) { if self .grpc_streams .borrow_mut() .insert(token_id, self.active_id.get()) .is_some() { panic!("duplicate token_id") } } fn register_grpc_callout(&self, token_id: u32) { if self .grpc_callouts .borrow_mut() .insert(token_id, self.active_id.get()) .is_some() { panic!("duplicate token_id") } } fn create_root_context(&self, context_id: u32) { let new_context = match self.new_root.get() { Some(f) => f(context_id), None => Box::new(NoopRoot), }; if self .roots .borrow_mut() .insert(context_id, new_context) .is_some() { panic!("duplicate context_id") } } fn create_stream_context(&self, context_id: u32, root_context_id: u32) { let new_context = match self.roots.borrow().get(&root_context_id) { Some(root_context) => match self.new_stream.get() { Some(f) => f(context_id, root_context_id), None => match root_context.create_stream_context(context_id) { Some(stream_context) => stream_context, None => panic!("create_stream_context returned None"), }, }, None => panic!("invalid root_context_id"), }; if self .streams .borrow_mut() .insert(context_id, new_context) .is_some() { panic!("duplicate context_id") } } fn create_http_context(&self, context_id: u32, root_context_id: u32) { let new_context = match self.roots.borrow().get(&root_context_id) { Some(root_context) => match self.new_http_stream.get() { Some(f) => f(context_id, root_context_id), None => match root_context.create_http_context(context_id) { Some(stream_context) => stream_context, None => panic!("create_http_context returned None"), }, }, None => panic!("invalid root_context_id"), }; if self .http_streams .borrow_mut() .insert(context_id, new_context) .is_some() { panic!("duplicate context_id") } } fn on_create_context(&self, context_id: u32, root_context_id: u32) { if root_context_id == 0 { self.create_root_context(context_id); } else if self.new_http_stream.get().is_some() { self.create_http_context(context_id, root_context_id); } else if self.new_stream.get().is_some() { self.create_stream_context(context_id, root_context_id); } else if let Some(root_context) = self.roots.borrow().get(&root_context_id) { match root_context.get_type() { Some(ContextType::HttpContext) => { self.create_http_context(context_id, root_context_id) } Some(ContextType::StreamContext) => { self.create_stream_context(context_id, root_context_id) } None => panic!("missing ContextType on root_context"), } } else { panic!("invalid root_context_id and missing constructors"); } } fn on_done(&self, context_id: u32) -> bool { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_done() } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_done() } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); root.on_done() } else { panic!("invalid context_id") } } fn on_log(&self, context_id: u32) { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_log() } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_log() } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); root.on_log() } else { panic!("invalid context_id") } } fn on_delete(&self, context_id: u32) { if !(self.http_streams.borrow_mut().remove(&context_id).is_some() || self.streams.borrow_mut().remove(&context_id).is_some() || self.roots.borrow_mut().remove(&context_id).is_some()) { panic!("invalid context_id") } } fn on_vm_start(&self, context_id: u32, vm_configuration_size: usize) -> bool { if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); root.on_vm_start(vm_configuration_size) } else { panic!("invalid context_id") } } fn on_configure(&self, context_id: u32, plugin_configuration_size: usize) -> bool { if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); root.on_configure(plugin_configuration_size) } else { panic!("invalid context_id") } } fn on_tick(&self, context_id: u32) { if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); root.on_tick() } else { panic!("invalid context_id") } } fn on_queue_ready(&self, context_id: u32, queue_id: u32) { if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); root.on_queue_ready(queue_id) } else { panic!("invalid context_id") } } fn on_new_connection(&self, context_id: u32) -> Action { if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_new_connection() } else { panic!("invalid context_id") } } fn on_downstream_data(&self, context_id: u32, data_size: usize, end_of_stream: bool) -> Action { if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_downstream_data(data_size, end_of_stream) } else { panic!("invalid context_id") } } fn on_downstream_close(&self, context_id: u32, peer_type: PeerType) { if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_downstream_close(peer_type) } else { panic!("invalid context_id") } } fn on_upstream_data(&self, context_id: u32, data_size: usize, end_of_stream: bool) -> Action { if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_upstream_data(data_size, end_of_stream) } else { panic!("invalid context_id") } } fn on_upstream_close(&self, context_id: u32, peer_type: PeerType) { if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); stream.on_upstream_close(peer_type) } else { panic!("invalid context_id") } } fn on_http_request_headers( &self, context_id: u32, num_headers: usize, end_of_stream: bool, ) -> Action { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_http_request_headers(num_headers, end_of_stream) } else { panic!("invalid context_id") } } fn on_http_request_body( &self, context_id: u32, body_size: usize, end_of_stream: bool, ) -> Action { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_http_request_body(body_size, end_of_stream) } else { panic!("invalid context_id") } } fn on_http_request_trailers(&self, context_id: u32, num_trailers: usize) -> Action { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_http_request_trailers(num_trailers) } else { panic!("invalid context_id") } } fn on_http_response_headers( &self, context_id: u32, num_headers: usize, end_of_stream: bool, ) -> Action { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_http_response_headers(num_headers, end_of_stream) } else { panic!("invalid context_id") } } fn on_http_response_body( &self, context_id: u32, body_size: usize, end_of_stream: bool, ) -> Action { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_http_response_body(body_size, end_of_stream) } else { panic!("invalid context_id") } } fn on_http_response_trailers(&self, context_id: u32, num_trailers: usize) -> Action { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); http_stream.on_http_response_trailers(num_trailers) } else { panic!("invalid context_id") } } fn on_http_call_response( &self, token_id: u32, num_headers: usize, body_size: usize, num_trailers: usize, ) { let context_id = self .callouts .borrow_mut() .remove(&token_id) .expect("invalid token_id"); if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_http_call_response(token_id, num_headers, body_size, num_trailers) } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_http_call_response(token_id, num_headers, body_size, num_trailers) } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_http_call_response(token_id, num_headers, body_size, num_trailers) } } fn on_grpc_receive_initial_metadata(&self, token_id: u32, headers: u32) { let context_id = match self.grpc_streams.borrow_mut().get(&token_id) { Some(id) => *id, None => { // TODO: change back to a panic once underlying issue is fixed. trace!("on_grpc_receive_initial_metadata: invalid token_id"); return; } }; if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_grpc_stream_initial_metadata(token_id, headers); } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_grpc_stream_initial_metadata(token_id, headers); } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_grpc_stream_initial_metadata(token_id, headers); } } fn on_grpc_receive(&self, token_id: u32, response_size: usize) { let context_id = self.grpc_callouts.borrow_mut().remove(&token_id); if let Some(context_id) = context_id { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_grpc_call_response(token_id, 0, response_size); } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_grpc_call_response(token_id, 0, response_size); } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_grpc_call_response(token_id, 0, response_size); } } else { let context_id = self.grpc_streams.borrow().get(&token_id).cloned(); if let Some(context_id) = context_id { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_grpc_stream_message(token_id, response_size); } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_grpc_stream_message(token_id, response_size); } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_grpc_stream_message(token_id, response_size); } } else { // TODO: change back to a panic once underlying issue is fixed. trace!("on_grpc_receive_initial_metadata: invalid token_id"); } } } fn on_grpc_receive_trailing_metadata(&self, token_id: u32, trailers: u32) { let context_id = match self.grpc_streams.borrow_mut().get(&token_id) { Some(id) => *id, None => { // TODO: change back to a panic once underlying issue is fixed. trace!("on_grpc_receive_trailing_metadata: invalid token_id"); return; } }; if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_grpc_stream_trailing_metadata(token_id, trailers); } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_grpc_stream_trailing_metadata(token_id, trailers); } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_grpc_stream_trailing_metadata(token_id, trailers); } } fn on_grpc_close(&self, token_id: u32, status_code: u32) { let context_id = self.grpc_callouts.borrow_mut().remove(&token_id); if let Some(context_id) = context_id { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_grpc_call_response(token_id, status_code, 0); } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_grpc_call_response(token_id, status_code, 0); } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_grpc_call_response(token_id, status_code, 0); } } else { let context_id = self.grpc_streams.borrow_mut().remove(&token_id); if let Some(context_id) = context_id { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_grpc_stream_close(token_id, status_code) } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_grpc_stream_close(token_id, status_code) } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_grpc_stream_close(token_id, status_code) } } else { // TODO: change back to a panic once underlying issue is fixed. trace!("on_grpc_close: invalid token_id, a non-connected stream has closed"); } } } fn on_foreign_function(&self, context_id: u32, function_id: u32, arugments_size: usize) { if let Some(http_stream) = self.http_streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); http_stream.on_foreign_function(function_id, arugments_size) } else if let Some(stream) = self.streams.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); stream.on_foreign_function(function_id, arugments_size) } else if let Some(root) = self.roots.borrow_mut().get_mut(&context_id) { self.active_id.set(context_id); hostcalls::set_effective_context(context_id).unwrap(); root.on_foreign_function(function_id, arugments_size) } } } #[no_mangle] pub extern "C" fn proxy_on_context_create(context_id: u32, root_context_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.on_create_context(context_id, root_context_id)) } #[no_mangle] pub extern "C" fn proxy_on_done(context_id: u32) -> bool { DISPATCHER.with(|dispatcher| dispatcher.on_done(context_id)) } #[no_mangle] pub extern "C" fn proxy_on_log(context_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.on_log(context_id)) } #[no_mangle] pub extern "C" fn proxy_on_delete(context_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.on_delete(context_id)) } #[no_mangle] pub extern "C" fn proxy_on_vm_start(context_id: u32, vm_configuration_size: usize) -> bool { DISPATCHER.with(|dispatcher| dispatcher.on_vm_start(context_id, vm_configuration_size)) } #[no_mangle] pub extern "C" fn proxy_on_configure(context_id: u32, plugin_configuration_size: usize) -> bool { DISPATCHER.with(|dispatcher| dispatcher.on_configure(context_id, plugin_configuration_size)) } #[no_mangle] pub extern "C" fn proxy_on_tick(context_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.on_tick(context_id)) } #[no_mangle] pub extern "C" fn proxy_on_queue_ready(context_id: u32, queue_id: u32) { DISPATCHER.with(|dispatcher| dispatcher.on_queue_ready(context_id, queue_id)) } #[no_mangle] pub extern "C" fn proxy_on_new_connection(context_id: u32) -> Action { DISPATCHER.with(|dispatcher| dispatcher.on_new_connection(context_id)) } #[no_mangle] pub extern "C" fn proxy_on_downstream_data( context_id: u32, data_size: usize, end_of_stream: bool, ) -> Action { DISPATCHER .with(|dispatcher| dispatcher.on_downstream_data(context_id, data_size, end_of_stream)) } #[no_mangle] pub extern "C" fn proxy_on_downstream_connection_close(context_id: u32, peer_type: PeerType) { DISPATCHER.with(|dispatcher| dispatcher.on_downstream_close(context_id, peer_type)) } #[no_mangle] pub extern "C" fn proxy_on_upstream_data( context_id: u32, data_size: usize, end_of_stream: bool, ) -> Action { DISPATCHER.with(|dispatcher| dispatcher.on_upstream_data(context_id, data_size, end_of_stream)) } #[no_mangle] pub extern "C" fn proxy_on_upstream_connection_close(context_id: u32, peer_type: PeerType) { DISPATCHER.with(|dispatcher| dispatcher.on_upstream_close(context_id, peer_type)) } #[no_mangle] pub extern "C" fn proxy_on_request_headers( context_id: u32, num_headers: usize, end_of_stream: bool, ) -> Action { DISPATCHER.with(|dispatcher| { dispatcher.on_http_request_headers(context_id, num_headers, end_of_stream) }) } #[no_mangle] pub extern "C" fn proxy_on_request_body( context_id: u32, body_size: usize, end_of_stream: bool, ) -> Action { DISPATCHER .with(|dispatcher| dispatcher.on_http_request_body(context_id, body_size, end_of_stream)) } #[no_mangle] pub extern "C" fn proxy_on_request_trailers(context_id: u32, num_trailers: usize) -> Action { DISPATCHER.with(|dispatcher| dispatcher.on_http_request_trailers(context_id, num_trailers)) } #[no_mangle] pub extern "C" fn proxy_on_response_headers( context_id: u32, num_headers: usize, end_of_stream: bool, ) -> Action { DISPATCHER.with(|dispatcher| { dispatcher.on_http_response_headers(context_id, num_headers, end_of_stream) }) } #[no_mangle] pub extern "C" fn proxy_on_response_body( context_id: u32, body_size: usize, end_of_stream: bool, ) -> Action { DISPATCHER .with(|dispatcher| dispatcher.on_http_response_body(context_id, body_size, end_of_stream)) } #[no_mangle] pub extern "C" fn proxy_on_response_trailers(context_id: u32, num_trailers: usize) -> Action { DISPATCHER.with(|dispatcher| dispatcher.on_http_response_trailers(context_id, num_trailers)) } #[no_mangle] pub extern "C" fn proxy_on_http_call_response( _context_id: u32, token_id: u32, num_headers: usize, body_size: usize, num_trailers: usize, ) { DISPATCHER.with(|dispatcher| { dispatcher.on_http_call_response(token_id, num_headers, body_size, num_trailers) }) } #[no_mangle] pub extern "C" fn proxy_on_grpc_receive_initial_metadata( _context_id: u32, token_id: u32, headers: u32, ) { DISPATCHER.with(|dispatcher| dispatcher.on_grpc_receive_initial_metadata(token_id, headers)) } #[no_mangle] pub extern "C" fn proxy_on_grpc_receive(_context_id: u32, token_id: u32, response_size: usize) { DISPATCHER.with(|dispatcher| dispatcher.on_grpc_receive(token_id, response_size)) } #[no_mangle] pub extern "C" fn proxy_on_grpc_receive_trailing_metadata( _context_id: u32, token_id: u32, trailers: u32, ) { DISPATCHER.with(|dispatcher| dispatcher.on_grpc_receive_trailing_metadata(token_id, trailers)) } #[no_mangle] pub extern "C" fn proxy_on_grpc_close(_context_id: u32, token_id: u32, status_code: u32) { DISPATCHER.with(|dispatcher| dispatcher.on_grpc_close(token_id, status_code)) } #[no_mangle] pub extern "C" fn proxy_on_foreign_function( context_id: u32, function_id: u32, arguments_size: usize, ) { DISPATCHER .with(|dispatcher| dispatcher.on_foreign_function(context_id, function_id, arguments_size)) } ================================================ FILE: src/hostcalls.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::dispatcher; use crate::types::*; use std::ptr::{null, null_mut}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; extern "C" { fn proxy_log(level: LogLevel, message_data: *const u8, message_size: usize) -> Status; } pub fn log(level: LogLevel, message: &str) -> Result<(), Status> { unsafe { match proxy_log(level, message.as_ptr(), message.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_log_level(return_level: *mut LogLevel) -> Status; } pub fn get_log_level() -> Result { let mut return_level: LogLevel = LogLevel::Trace; unsafe { match proxy_get_log_level(&mut return_level) { Status::Ok => Ok(return_level), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_current_time_nanoseconds(return_time: *mut u64) -> Status; } pub fn get_current_time() -> Result { let mut return_time: u64 = 0; unsafe { match proxy_get_current_time_nanoseconds(&mut return_time) { Status::Ok => Ok(UNIX_EPOCH + Duration::from_nanos(return_time)), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_set_tick_period_milliseconds(period: u32) -> Status; } pub fn set_tick_period(period: Duration) -> Result<(), Status> { unsafe { match proxy_set_tick_period_milliseconds(period.as_millis() as u32) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_buffer_bytes( buffer_type: BufferType, start: usize, max_size: usize, return_buffer_data: *mut *mut u8, return_buffer_size: *mut usize, ) -> Status; } pub fn get_buffer( buffer_type: BufferType, start: usize, max_size: usize, ) -> Result, Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_buffer_bytes( buffer_type, start, max_size, &mut return_data, &mut return_size, ) { Status::Ok => { if !return_data.is_null() { Ok(Some(Vec::from_raw_parts( return_data, return_size, return_size, ))) } else { Ok(None) } } Status::NotFound => Ok(None), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_set_buffer_bytes( buffer_type: BufferType, start: usize, size: usize, buffer_data: *const u8, buffer_size: usize, ) -> Status; } pub fn set_buffer( buffer_type: BufferType, start: usize, size: usize, value: &[u8], ) -> Result<(), Status> { unsafe { match proxy_set_buffer_bytes(buffer_type, start, size, value.as_ptr(), value.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } #[cfg(not(all(test, feature = "mockalloc")))] extern "C" { fn proxy_get_header_map_pairs( map_type: MapType, return_map_data: *mut *mut u8, return_map_size: *mut usize, ) -> Status; } #[cfg(all(test, feature = "mockalloc"))] fn proxy_get_header_map_pairs( map_type: MapType, return_map_data: *mut *mut u8, return_map_size: *mut usize, ) -> Status { mocks::proxy_get_header_map_pairs(map_type, return_map_data, return_map_size) } pub fn get_map(map_type: MapType) -> Result, Status> { unsafe { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; match proxy_get_header_map_pairs(map_type, &mut return_data, &mut return_size) { Status::Ok => { if !return_data.is_null() { let serialized_map = Vec::from_raw_parts(return_data, return_size, return_size); Ok(utils::deserialize_map(&serialized_map)) } else { Ok(Vec::new()) } } status => panic!("unexpected status: {}", status as u32), } } } pub fn get_map_bytes(map_type: MapType) -> Result, Status> { unsafe { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; match proxy_get_header_map_pairs(map_type, &mut return_data, &mut return_size) { Status::Ok => { if !return_data.is_null() { let serialized_map = Vec::from_raw_parts(return_data, return_size, return_size); Ok(utils::deserialize_map_bytes(&serialized_map)) } else { Ok(Vec::new()) } } status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_set_header_map_pairs( map_type: MapType, map_data: *const u8, map_size: usize, ) -> Status; } pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status> { let serialized_map = utils::serialize_map(&map); unsafe { match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn set_map_bytes(map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), Status> { let serialized_map = utils::serialize_map_bytes(&map); unsafe { match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_header_map_value( map_type: MapType, key_data: *const u8, key_size: usize, return_value_data: *mut *mut u8, return_value_size: *mut usize, ) -> Status; } pub fn get_map_value(map_type: MapType, key: &str) -> Result, Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_header_map_value( map_type, key.as_ptr(), key.len(), &mut return_data, &mut return_size, ) { Status::Ok => { if !return_data.is_null() { Ok(Some( String::from_utf8(Vec::from_raw_parts( return_data, return_size, return_size, )) .unwrap(), )) } else { Ok(Some(String::new())) } } Status::NotFound => Ok(None), status => panic!("unexpected status: {}", status as u32), } } } pub fn get_map_value_bytes(map_type: MapType, key: &str) -> Result, Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_header_map_value( map_type, key.as_ptr(), key.len(), &mut return_data, &mut return_size, ) { Status::Ok => { if !return_data.is_null() { Ok(Some(Vec::from_raw_parts( return_data, return_size, return_size, ))) } else { Ok(Some(Vec::new())) } } Status::NotFound => Ok(None), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_remove_header_map_value( map_type: MapType, key_data: *const u8, key_size: usize, ) -> Status; } pub fn remove_map_value(map_type: MapType, key: &str) -> Result<(), Status> { unsafe { match proxy_remove_header_map_value(map_type, key.as_ptr(), key.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_replace_header_map_value( map_type: MapType, key_data: *const u8, key_size: usize, value_data: *const u8, value_size: usize, ) -> Status; } pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Result<(), Status> { unsafe { if let Some(value) = value { match proxy_replace_header_map_value( map_type, key.as_ptr(), key.len(), value.as_ptr(), value.len(), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } else { match proxy_remove_header_map_value(map_type, key.as_ptr(), key.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } } pub fn set_map_value_bytes( map_type: MapType, key: &str, value: Option<&[u8]>, ) -> Result<(), Status> { unsafe { if let Some(value) = value { match proxy_replace_header_map_value( map_type, key.as_ptr(), key.len(), value.as_ptr(), value.len(), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } else { match proxy_remove_header_map_value(map_type, key.as_ptr(), key.len()) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } } extern "C" { fn proxy_add_header_map_value( map_type: MapType, key_data: *const u8, key_size: usize, value_data: *const u8, value_size: usize, ) -> Status; } pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<(), Status> { unsafe { match proxy_add_header_map_value( map_type, key.as_ptr(), key.len(), value.as_ptr(), value.len(), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn add_map_value_bytes(map_type: MapType, key: &str, value: &[u8]) -> Result<(), Status> { unsafe { match proxy_add_header_map_value( map_type, key.as_ptr(), key.len(), value.as_ptr(), value.len(), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_property( path_data: *const u8, path_size: usize, return_value_data: *mut *mut u8, return_value_size: *mut usize, ) -> Status; } pub fn get_property(path: Vec<&str>) -> Result, Status> { let serialized_path = utils::serialize_property_path(path); let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_property( serialized_path.as_ptr(), serialized_path.len(), &mut return_data, &mut return_size, ) { Status::Ok => { if !return_data.is_null() { Ok(Some(Vec::from_raw_parts( return_data, return_size, return_size, ))) } else { Ok(None) } } Status::NotFound => Ok(None), Status::SerializationFailure => Err(Status::SerializationFailure), Status::InternalFailure => Err(Status::InternalFailure), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_set_property( path_data: *const u8, path_size: usize, value_data: *const u8, value_size: usize, ) -> Status; } pub fn set_property(path: Vec<&str>, value: Option<&[u8]>) -> Result<(), Status> { let serialized_path = utils::serialize_property_path(path); unsafe { match proxy_set_property( serialized_path.as_ptr(), serialized_path.len(), value.map_or(null(), |value| value.as_ptr()), value.map_or(0, |value| value.len()), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_shared_data( key_data: *const u8, key_size: usize, return_value_data: *mut *mut u8, return_value_size: *mut usize, return_cas: *mut u32, ) -> Status; } pub fn get_shared_data(key: &str) -> Result<(Option, Option), Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; let mut return_cas: u32 = 0; unsafe { match proxy_get_shared_data( key.as_ptr(), key.len(), &mut return_data, &mut return_size, &mut return_cas, ) { Status::Ok => { let cas = match return_cas { 0 => None, cas => Some(cas), }; if !return_data.is_null() { Ok(( Some(Vec::from_raw_parts(return_data, return_size, return_size)), cas, )) } else { Ok((None, cas)) } } Status::NotFound => Ok((None, None)), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_set_shared_data( key_data: *const u8, key_size: usize, value_data: *const u8, value_size: usize, cas: u32, ) -> Status; } pub fn set_shared_data(key: &str, value: Option<&[u8]>, cas: Option) -> Result<(), Status> { unsafe { match proxy_set_shared_data( key.as_ptr(), key.len(), value.map_or(null(), |value| value.as_ptr()), value.map_or(0, |value| value.len()), cas.unwrap_or(0), ) { Status::Ok => Ok(()), Status::CasMismatch => Err(Status::CasMismatch), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_register_shared_queue( name_data: *const u8, name_size: usize, return_id: *mut u32, ) -> Status; } pub fn register_shared_queue(name: &str) -> Result { unsafe { let mut return_id: u32 = 0; match proxy_register_shared_queue(name.as_ptr(), name.len(), &mut return_id) { Status::Ok => Ok(return_id), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_resolve_shared_queue( vm_id_data: *const u8, vm_id_size: usize, name_data: *const u8, name_size: usize, return_id: *mut u32, ) -> Status; } pub fn resolve_shared_queue(vm_id: &str, name: &str) -> Result, Status> { let mut return_id: u32 = 0; unsafe { match proxy_resolve_shared_queue( vm_id.as_ptr(), vm_id.len(), name.as_ptr(), name.len(), &mut return_id, ) { Status::Ok => Ok(Some(return_id)), Status::NotFound => Ok(None), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_dequeue_shared_queue( queue_id: u32, return_value_data: *mut *mut u8, return_value_size: *mut usize, ) -> Status; } pub fn dequeue_shared_queue(queue_id: u32) -> Result, Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_dequeue_shared_queue(queue_id, &mut return_data, &mut return_size) { Status::Ok => { if !return_data.is_null() { Ok(Some(Vec::from_raw_parts( return_data, return_size, return_size, ))) } else { Ok(None) } } Status::Empty => Ok(None), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_enqueue_shared_queue( queue_id: u32, value_data: *const u8, value_size: usize, ) -> Status; } pub fn enqueue_shared_queue(queue_id: u32, value: Option<&[u8]>) -> Result<(), Status> { unsafe { match proxy_enqueue_shared_queue( queue_id, value.map_or(null(), |value| value.as_ptr()), value.map_or(0, |value| value.len()), ) { Status::Ok => Ok(()), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_continue_stream(stream_type: StreamType) -> Status; } pub fn resume_downstream() -> Result<(), Status> { unsafe { match proxy_continue_stream(StreamType::Downstream) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn resume_upstream() -> Result<(), Status> { unsafe { match proxy_continue_stream(StreamType::Upstream) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn resume_http_request() -> Result<(), Status> { unsafe { match proxy_continue_stream(StreamType::HttpRequest) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn resume_http_response() -> Result<(), Status> { unsafe { match proxy_continue_stream(StreamType::HttpResponse) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_close_stream(stream_type: StreamType) -> Status; } pub fn close_downstream() -> Result<(), Status> { unsafe { match proxy_close_stream(StreamType::Downstream) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn close_upstream() -> Result<(), Status> { unsafe { match proxy_close_stream(StreamType::Upstream) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn reset_http_request() -> Result<(), Status> { unsafe { match proxy_close_stream(StreamType::HttpRequest) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn reset_http_response() -> Result<(), Status> { unsafe { match proxy_close_stream(StreamType::HttpResponse) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_send_local_response( status_code: u32, status_code_details_data: *const u8, status_code_details_size: usize, body_data: *const u8, body_size: usize, headers_data: *const u8, headers_size: usize, grpc_status: i32, ) -> Status; } pub fn send_http_response( status_code: u32, headers: Vec<(&str, &str)>, body: Option<&[u8]>, ) -> Result<(), Status> { let serialized_headers = utils::serialize_map(&headers); unsafe { match proxy_send_local_response( status_code, null(), 0, body.map_or(null(), |body| body.as_ptr()), body.map_or(0, |body| body.len()), serialized_headers.as_ptr(), serialized_headers.len(), -1, ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } pub fn send_grpc_response( grpc_status: GrpcStatusCode, grpc_status_message: Option<&str>, custom_metadata: Vec<(&str, &[u8])>, ) -> Result<(), Status> { let serialized_custom_metadata = utils::serialize_map_bytes(&custom_metadata); unsafe { match proxy_send_local_response( 200, null(), 0, grpc_status_message.map_or(null(), |grpc_status_message| grpc_status_message.as_ptr()), grpc_status_message.map_or(0, |grpc_status_message| grpc_status_message.len()), serialized_custom_metadata.as_ptr(), serialized_custom_metadata.len(), grpc_status as i32, ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_http_call( upstream_data: *const u8, upstream_size: usize, headers_data: *const u8, headers_size: usize, body_data: *const u8, body_size: usize, trailers_data: *const u8, trailers_size: usize, timeout: u32, return_token: *mut u32, ) -> Status; } pub fn dispatch_http_call( upstream: &str, headers: Vec<(&str, &str)>, body: Option<&[u8]>, trailers: Vec<(&str, &str)>, timeout: Duration, ) -> Result { let serialized_headers = utils::serialize_map(&headers); let serialized_trailers = utils::serialize_map(&trailers); let mut return_token: u32 = 0; unsafe { match proxy_http_call( upstream.as_ptr(), upstream.len(), serialized_headers.as_ptr(), serialized_headers.len(), body.map_or(null(), |body| body.as_ptr()), body.map_or(0, |body| body.len()), serialized_trailers.as_ptr(), serialized_trailers.len(), timeout.as_millis() as u32, &mut return_token, ) { Status::Ok => { dispatcher::register_callout(return_token); Ok(return_token) } Status::BadArgument => Err(Status::BadArgument), Status::InternalFailure => Err(Status::InternalFailure), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_grpc_call( upstream_data: *const u8, upstream_size: usize, service_name_data: *const u8, service_name_size: usize, method_name_data: *const u8, method_name_size: usize, initial_metadata_data: *const u8, initial_metadata_size: usize, message_data_data: *const u8, message_data_size: usize, timeout: u32, return_callout_id: *mut u32, ) -> Status; } pub fn dispatch_grpc_call( upstream_name: &str, service_name: &str, method_name: &str, initial_metadata: Vec<(&str, &[u8])>, message: Option<&[u8]>, timeout: Duration, ) -> Result { let mut return_callout_id = 0; let serialized_initial_metadata = utils::serialize_map_bytes(&initial_metadata); unsafe { match proxy_grpc_call( upstream_name.as_ptr(), upstream_name.len(), service_name.as_ptr(), service_name.len(), method_name.as_ptr(), method_name.len(), serialized_initial_metadata.as_ptr(), serialized_initial_metadata.len(), message.map_or(null(), |message| message.as_ptr()), message.map_or(0, |message| message.len()), timeout.as_millis() as u32, &mut return_callout_id, ) { Status::Ok => { dispatcher::register_grpc_callout(return_callout_id); Ok(return_callout_id) } Status::ParseFailure => Err(Status::ParseFailure), Status::InternalFailure => Err(Status::InternalFailure), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_grpc_stream( upstream_data: *const u8, upstream_size: usize, service_name_data: *const u8, service_name_size: usize, method_name_data: *const u8, method_name_size: usize, initial_metadata_data: *const u8, initial_metadata_size: usize, return_stream_id: *mut u32, ) -> Status; } pub fn open_grpc_stream( upstream_name: &str, service_name: &str, method_name: &str, initial_metadata: Vec<(&str, &[u8])>, ) -> Result { let mut return_stream_id = 0; let serialized_initial_metadata = utils::serialize_map_bytes(&initial_metadata); unsafe { match proxy_grpc_stream( upstream_name.as_ptr(), upstream_name.len(), service_name.as_ptr(), service_name.len(), method_name.as_ptr(), method_name.len(), serialized_initial_metadata.as_ptr(), serialized_initial_metadata.len(), &mut return_stream_id, ) { Status::Ok => { dispatcher::register_grpc_stream(return_stream_id); Ok(return_stream_id) } Status::ParseFailure => Err(Status::ParseFailure), Status::InternalFailure => Err(Status::InternalFailure), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_grpc_send( token: u32, message_ptr: *const u8, message_len: usize, end_stream: bool, ) -> Status; } pub fn send_grpc_stream_message( token: u32, message: Option<&[u8]>, end_stream: bool, ) -> Result<(), Status> { unsafe { match proxy_grpc_send( token, message.map_or(null(), |message| message.as_ptr()), message.map_or(0, |message| message.len()), end_stream, ) { Status::Ok => Ok(()), Status::BadArgument => Err(Status::BadArgument), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_grpc_cancel(token_id: u32) -> Status; } pub fn cancel_grpc_call(token_id: u32) -> Result<(), Status> { unsafe { match proxy_grpc_cancel(token_id) { Status::Ok => Ok(()), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } pub fn cancel_grpc_stream(token_id: u32) -> Result<(), Status> { unsafe { match proxy_grpc_cancel(token_id) { Status::Ok => Ok(()), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_grpc_close(token_id: u32) -> Status; } pub fn close_grpc_stream(token_id: u32) -> Result<(), Status> { unsafe { match proxy_grpc_close(token_id) { Status::Ok => Ok(()), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_status( return_code: *mut u32, return_message_data: *mut *mut u8, return_message_size: *mut usize, ) -> Status; } pub fn get_grpc_status() -> Result<(u32, Option), Status> { let mut return_code: u32 = 0; let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_status(&mut return_code, &mut return_data, &mut return_size) { Status::Ok => { if !return_data.is_null() { Ok(( return_code, Some( String::from_utf8(Vec::from_raw_parts( return_data, return_size, return_size, )) .unwrap(), ), )) } else { Ok((return_code, None)) } } status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_set_effective_context(context_id: u32) -> Status; } pub fn set_effective_context(context_id: u32) -> Result<(), Status> { unsafe { match proxy_set_effective_context(context_id) { Status::Ok => Ok(()), Status::BadArgument => Err(Status::BadArgument), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_call_foreign_function( function_name_data: *const u8, function_name_size: usize, arguments_data: *const u8, arguments_size: usize, results_data: *mut *mut u8, results_size: *mut usize, ) -> Status; } pub fn call_foreign_function( function_name: &str, arguments: Option<&[u8]>, ) -> Result, Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_call_foreign_function( function_name.as_ptr(), function_name.len(), arguments.map_or(null(), |arguments| arguments.as_ptr()), arguments.map_or(0, |arguments| arguments.len()), &mut return_data, &mut return_size, ) { Status::Ok => { if !return_data.is_null() { Ok(Some(Vec::from_raw_parts( return_data, return_size, return_size, ))) } else { Ok(None) } } Status::NotFound => Err(Status::NotFound), Status::BadArgument => Err(Status::BadArgument), Status::SerializationFailure => Err(Status::SerializationFailure), Status::InternalFailure => Err(Status::InternalFailure), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_done() -> Status; } pub fn done() -> Result<(), Status> { unsafe { match proxy_done() { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_define_metric( metric_type: MetricType, name_data: *const u8, name_size: usize, return_id: *mut u32, ) -> Status; } pub fn define_metric(metric_type: MetricType, name: &str) -> Result { let mut return_id: u32 = 0; unsafe { match proxy_define_metric(metric_type, name.as_ptr(), name.len(), &mut return_id) { Status::Ok => Ok(return_id), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_get_metric(metric_id: u32, return_value: *mut u64) -> Status; } pub fn get_metric(metric_id: u32) -> Result { let mut return_value: u64 = 0; unsafe { match proxy_get_metric(metric_id, &mut return_value) { Status::Ok => Ok(return_value), Status::NotFound => Err(Status::NotFound), Status::BadArgument => Err(Status::BadArgument), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_record_metric(metric_id: u32, value: u64) -> Status; } pub fn record_metric(metric_id: u32, value: u64) -> Result<(), Status> { unsafe { match proxy_record_metric(metric_id, value) { Status::Ok => Ok(()), Status::NotFound => Err(Status::NotFound), status => panic!("unexpected status: {}", status as u32), } } } extern "C" { fn proxy_increment_metric(metric_id: u32, offset: i64) -> Status; } pub fn increment_metric(metric_id: u32, offset: i64) -> Result<(), Status> { unsafe { match proxy_increment_metric(metric_id, offset) { Status::Ok => Ok(()), Status::NotFound => Err(Status::NotFound), Status::BadArgument => Err(Status::BadArgument), status => panic!("unexpected status: {}", status as u32), } } } #[cfg(all(test, feature = "mockalloc"))] mod mocks { use crate::hostcalls::utils::tests::SERIALIZED_MAP; use crate::types::*; use std::alloc::{alloc, Layout}; pub fn proxy_get_header_map_pairs( _map_type: MapType, return_map_data: *mut *mut u8, return_map_size: *mut usize, ) -> Status { let layout = Layout::array::(SERIALIZED_MAP.len()).unwrap(); unsafe { *return_map_data = alloc(layout); *return_map_size = SERIALIZED_MAP.len(); std::ptr::copy( SERIALIZED_MAP.as_ptr(), *return_map_data, SERIALIZED_MAP.len(), ); } Status::Ok } } #[cfg(all(test, feature = "mockalloc"))] mod tests { use crate::types::*; use mockalloc::Mockalloc; use std::alloc::System; #[global_allocator] static ALLOCATOR: Mockalloc = Mockalloc(System); #[mockalloc::test] fn test_get_map_no_leaks() { let result = super::get_map(MapType::HttpRequestHeaders); assert!(result.is_ok()); } #[mockalloc::test] fn test_get_map_bytes_no_leaks() { let result = super::get_map_bytes(MapType::HttpRequestHeaders); assert!(result.is_ok()); } } mod utils { use crate::types::Bytes; use std::convert::TryFrom; pub(super) fn serialize_property_path(path: Vec<&str>) -> Bytes { if path.is_empty() { return Vec::new(); } let mut size: usize = 0; for part in &path { size += part.len() + 1; } let mut bytes: Bytes = Vec::with_capacity(size); for part in &path { bytes.extend_from_slice(part.as_bytes()); bytes.push(0); } bytes.pop(); bytes } pub(super) fn serialize_map(map: &[(&str, &str)]) -> Bytes { let mut size: usize = 4; for (name, value) in map { size += name.len() + value.len() + 10; } let mut bytes: Bytes = Vec::with_capacity(size); bytes.extend_from_slice(&(map.len() as u32).to_le_bytes()); for (name, value) in map { bytes.extend_from_slice(&(name.len() as u32).to_le_bytes()); bytes.extend_from_slice(&(value.len() as u32).to_le_bytes()); } for (name, value) in map { bytes.extend_from_slice(name.as_bytes()); bytes.push(0); bytes.extend_from_slice(value.as_bytes()); bytes.push(0); } bytes } pub(super) fn serialize_map_bytes(map: &[(&str, &[u8])]) -> Bytes { let mut size: usize = 4; for (name, value) in map { size += name.len() + value.len() + 10; } let mut bytes: Bytes = Vec::with_capacity(size); bytes.extend_from_slice(&(map.len() as u32).to_le_bytes()); for (name, value) in map { bytes.extend_from_slice(&(name.len() as u32).to_le_bytes()); bytes.extend_from_slice(&(value.len() as u32).to_le_bytes()); } for (name, value) in map { bytes.extend_from_slice(name.as_bytes()); bytes.push(0); bytes.extend_from_slice(value); bytes.push(0); } bytes } pub(super) fn deserialize_map(bytes: &[u8]) -> Vec<(String, String)> { if bytes.is_empty() { return Vec::new(); } let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[0..4]).unwrap()) as usize; let mut map = Vec::with_capacity(size); let mut p = 4 + size * 8; for n in 0..size { let s = 4 + n * 8; let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s..s + 4]).unwrap()) as usize; let key = bytes[p..p + size].to_vec(); p += size + 1; let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s + 4..s + 8]).unwrap()) as usize; let value = bytes[p..p + size].to_vec(); p += size + 1; map.push(( String::from_utf8(key).unwrap(), String::from_utf8(value).unwrap(), )); } map } pub(super) fn deserialize_map_bytes(bytes: &[u8]) -> Vec<(String, Bytes)> { if bytes.is_empty() { return Vec::new(); } let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[0..4]).unwrap()) as usize; let mut map = Vec::with_capacity(size); let mut p = 4 + size * 8; for n in 0..size { let s = 4 + n * 8; let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s..s + 4]).unwrap()) as usize; let key = bytes[p..p + size].to_vec(); p += size + 1; let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s + 4..s + 8]).unwrap()) as usize; let value = bytes[p..p + size].to_vec(); p += size + 1; map.push((String::from_utf8(key).unwrap(), value)); } map } #[cfg(test)] pub(super) mod tests { use super::*; #[cfg(nightly)] use test::Bencher; static MAP: &[(&str, &str)] = &[ (":method", "GET"), (":path", "/bytes/1"), (":authority", "httpbin.org"), ("Powered-By", "proxy-wasm"), ]; #[rustfmt::skip] pub(in crate::hostcalls) static SERIALIZED_MAP: &[u8] = &[ // num entries 4, 0, 0, 0, // len (":method", "GET") 7, 0, 0, 0, 3, 0, 0, 0, // len (":path", "/bytes/1") 5, 0, 0, 0, 8, 0, 0, 0, // len (":authority", "httpbin.org") 10, 0, 0, 0, 11, 0, 0, 0, // len ("Powered-By", "proxy-wasm") 10, 0, 0, 0, 10, 0, 0, 0, // ":method" 58, 109, 101, 116, 104, 111, 100, 0, // "GET" 71, 69, 84, 0, // ":path" 58, 112, 97, 116, 104, 0, // "/bytes/1" 47, 98, 121, 116, 101, 115, 47, 49, 0, // ":authority" 58, 97, 117, 116, 104, 111, 114, 105, 116, 121, 0, // "httpbin.org" 104, 116, 116, 112, 98, 105, 110, 46, 111, 114, 103, 0, // "Powered-By" 80, 111, 119, 101, 114, 101, 100, 45, 66, 121, 0, // "proxy-wasm" 112, 114, 111, 120, 121, 45, 119, 97, 115, 109, 0, ]; #[test] fn test_serialize_map_empty() { let serialized_map = serialize_map(&[]); assert_eq!(serialized_map, [0, 0, 0, 0]); } #[test] fn test_serialize_map_empty_bytes() { let serialized_map = serialize_map_bytes(&[]); assert_eq!(serialized_map, [0, 0, 0, 0]); } #[test] fn test_deserialize_map_empty() { let map = deserialize_map(&[]); assert_eq!(map, []); let map = deserialize_map(&[0, 0, 0, 0]); assert_eq!(map, []); } #[test] fn test_deserialize_map_empty_bytes() { let map = deserialize_map_bytes(&[]); assert_eq!(map, []); let map = deserialize_map_bytes(&[0, 0, 0, 0]); assert_eq!(map, []); } #[test] fn test_serialize_map() { let serialized_map = serialize_map(MAP); assert_eq!(serialized_map, SERIALIZED_MAP); } #[test] fn test_serialize_map_bytes() { let map: Vec<(&str, &[u8])> = MAP.iter().map(|x| (x.0, x.1.as_bytes())).collect(); let serialized_map = serialize_map_bytes(&map); assert_eq!(serialized_map, SERIALIZED_MAP); } #[test] fn test_deserialize_map() { let map = deserialize_map(SERIALIZED_MAP); assert_eq!(map.len(), MAP.len()); for (got, expected) in map.into_iter().zip(MAP) { assert_eq!(got.0, expected.0); assert_eq!(got.1, expected.1); } } #[test] fn test_deserialize_map_bytes() { let map = deserialize_map_bytes(SERIALIZED_MAP); assert_eq!(map.len(), MAP.len()); for (got, expected) in map.into_iter().zip(MAP) { assert_eq!(got.0, expected.0); assert_eq!(got.1, expected.1.as_bytes()); } } #[test] fn test_deserialize_map_roundtrip() { let map = deserialize_map(SERIALIZED_MAP); // TODO(v0.3): fix arguments, so that maps can be reused without conversion. let map_refs: Vec<(&str, &str)> = map.iter().map(|x| (x.0.as_ref(), x.1.as_ref())).collect(); let serialized_map = serialize_map(&map_refs); assert_eq!(serialized_map, SERIALIZED_MAP); } #[test] fn test_deserialize_map_roundtrip_bytes() { let map = deserialize_map_bytes(SERIALIZED_MAP); // TODO(v0.3): fix arguments, so that maps can be reused without conversion. let map_refs: Vec<(&str, &[u8])> = map.iter().map(|x| (x.0.as_ref(), x.1.as_ref())).collect(); let serialized_map = serialize_map_bytes(&map_refs); assert_eq!(serialized_map, SERIALIZED_MAP); } #[test] fn test_deserialize_map_all_chars() { // 0x00-0x7f are valid single-byte UTF-8 characters. for i in 0..0x7f { let serialized_src = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 99, 0, i, 0]; let map = deserialize_map(&serialized_src); // TODO(v0.3): fix arguments, so that maps can be reused without conversion. let map_refs: Vec<(&str, &str)> = map.iter().map(|x| (x.0.as_ref(), x.1.as_ref())).collect(); let serialized_map = serialize_map(&map_refs); assert_eq!(serialized_map, serialized_src); } // 0x80-0xff are invalid single-byte UTF-8 characters. for i in 0x80..0xff { let serialized_src = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 99, 0, i, 0]; std::panic::set_hook(Box::new(|_| {})); let result = std::panic::catch_unwind(|| { deserialize_map(&serialized_src); }); assert!(result.is_err()); } } #[test] fn test_deserialize_map_all_chars_bytes() { // All 256 single-byte characters are allowed when emitting bytes. for i in 0..0xff { let serialized_src = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 99, 0, i, 0]; let map = deserialize_map_bytes(&serialized_src); // TODO(v0.3): fix arguments, so that maps can be reused without conversion. let map_refs: Vec<(&str, &[u8])> = map.iter().map(|x| (x.0.as_ref(), x.1.as_ref())).collect(); let serialized_map = serialize_map_bytes(&map_refs); assert_eq!(serialized_map, serialized_src); } } #[cfg(nightly)] #[bench] fn bench_serialize_map(b: &mut Bencher) { let map = MAP.to_vec(); b.iter(|| { serialize_map(test::black_box(&map)); }); } #[cfg(nightly)] #[bench] fn bench_serialize_map_bytes(b: &mut Bencher) { let map: Vec<(&str, &[u8])> = MAP.iter().map(|x| (x.0, x.1.as_bytes())).collect(); b.iter(|| { serialize_map_bytes(test::black_box(&map)); }); } #[cfg(nightly)] #[bench] fn bench_deserialize_map(b: &mut Bencher) { let serialized_map = SERIALIZED_MAP.to_vec(); b.iter(|| { deserialize_map(test::black_box(&serialized_map)); }); } #[cfg(nightly)] #[bench] fn bench_deserialize_map_bytes(b: &mut Bencher) { let serialized_map = SERIALIZED_MAP.to_vec(); b.iter(|| { deserialize_map_bytes(test::black_box(&serialized_map)); }); } } } ================================================ FILE: src/lib.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![cfg_attr(all(test, nightly), feature(test))] #[cfg(all(test, nightly))] extern crate test; pub mod hostcalls; pub mod traits; pub mod types; mod allocator; mod dispatcher; mod logger; // For crate-type="cdylib". #[cfg(not(wasi_exec_model_reactor))] #[macro_export] macro_rules! main { ($code:block) => { #[cfg(target_os = "wasi")] extern "C" { fn __wasm_call_ctors(); } #[no_mangle] pub extern "C" fn _initialize() { #[cfg(target_os = "wasi")] unsafe { __wasm_call_ctors(); } $code; } }; } // For crate-type="bin" with RUSTFLAGS="-Z wasi-exec-model=reactor". #[cfg(wasi_exec_model_reactor)] #[macro_export] macro_rules! main { ($code:block) => { pub fn main() -> Result<(), Box> { $code; Ok(()) } }; } pub fn set_log_level(level: types::LogLevel) { logger::set_log_level(level); } pub fn set_root_context(callback: types::NewRootContext) { dispatcher::set_root_context(callback); } pub fn set_stream_context(callback: types::NewStreamContext) { dispatcher::set_stream_context(callback); } pub fn set_http_context(callback: types::NewHttpContext) { dispatcher::set_http_context(callback); } #[no_mangle] pub extern "C" fn proxy_abi_version_0_2_1() {} ================================================ FILE: src/logger.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::hostcalls; use crate::types::LogLevel; use std::panic; use std::sync::atomic::{AtomicBool, Ordering}; struct Logger; static LOGGER: Logger = Logger; static INITIALIZED: AtomicBool = AtomicBool::new(false); pub(crate) fn set_log_level(level: LogLevel) { if !INITIALIZED.load(Ordering::Relaxed) { log::set_logger(&LOGGER).unwrap(); panic::set_hook(Box::new(|panic_info| { hostcalls::log(LogLevel::Critical, &panic_info.to_string()).unwrap(); })); INITIALIZED.store(true, Ordering::Relaxed); } LOGGER.set_log_level(level); } impl Logger { pub fn set_log_level(&self, level: LogLevel) { let filter = match level { LogLevel::Trace => log::LevelFilter::Trace, LogLevel::Debug => log::LevelFilter::Debug, LogLevel::Info => log::LevelFilter::Info, LogLevel::Warn => log::LevelFilter::Warn, LogLevel::Error => log::LevelFilter::Error, LogLevel::Critical => log::LevelFilter::Off, }; log::set_max_level(filter); } } impl log::Log for Logger { fn enabled(&self, metadata: &log::Metadata) -> bool { metadata.level() <= log::max_level() } fn log(&self, record: &log::Record) { if !self.enabled(record.metadata()) { return; } let level = match record.level() { log::Level::Trace => LogLevel::Trace, log::Level::Debug => LogLevel::Debug, log::Level::Info => LogLevel::Info, log::Level::Warn => LogLevel::Warn, log::Level::Error => LogLevel::Error, }; let message = record.args().to_string(); hostcalls::log(level, &message).unwrap(); } fn flush(&self) {} } ================================================ FILE: src/traits.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::hostcalls; use crate::types::*; use std::time::{Duration, SystemTime}; pub trait Context { fn get_current_time(&self) -> SystemTime { hostcalls::get_current_time().unwrap() } fn get_property(&self, path: Vec<&str>) -> Option { hostcalls::get_property(path).unwrap() } fn set_property(&self, path: Vec<&str>, value: Option<&[u8]>) { hostcalls::set_property(path, value).unwrap() } fn get_shared_data(&self, key: &str) -> (Option, Option) { hostcalls::get_shared_data(key).unwrap() } fn set_shared_data( &self, key: &str, value: Option<&[u8]>, cas: Option, ) -> Result<(), Status> { hostcalls::set_shared_data(key, value, cas) } fn remove_shared_data(&self, key: &str, cas: Option) -> Result<(), Status> { hostcalls::set_shared_data(key, None, cas) } fn register_shared_queue(&self, name: &str) -> u32 { hostcalls::register_shared_queue(name).unwrap() } fn resolve_shared_queue(&self, vm_id: &str, name: &str) -> Option { hostcalls::resolve_shared_queue(vm_id, name).unwrap() } fn dequeue_shared_queue(&self, queue_id: u32) -> Result, Status> { hostcalls::dequeue_shared_queue(queue_id) } fn enqueue_shared_queue(&self, queue_id: u32, value: Option<&[u8]>) -> Result<(), Status> { hostcalls::enqueue_shared_queue(queue_id, value) } fn dispatch_http_call( &self, upstream: &str, headers: Vec<(&str, &str)>, body: Option<&[u8]>, trailers: Vec<(&str, &str)>, timeout: Duration, ) -> Result { hostcalls::dispatch_http_call(upstream, headers, body, trailers, timeout) } fn on_http_call_response( &mut self, _token_id: u32, _num_headers: usize, _body_size: usize, _num_trailers: usize, ) { } fn get_http_call_response_headers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap() } fn get_http_call_response_headers_bytes(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::HttpCallResponseHeaders).unwrap() } fn get_http_call_response_header(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpCallResponseHeaders, name).unwrap() } fn get_http_call_response_header_bytes(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::HttpCallResponseHeaders, name).unwrap() } fn get_http_call_response_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap() } fn get_http_call_response_trailers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap() } fn get_http_call_response_trailers_bytes(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::HttpCallResponseTrailers).unwrap() } fn get_http_call_response_trailer(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap() } fn get_http_call_response_trailer_bytes(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::HttpCallResponseTrailers, name).unwrap() } fn dispatch_grpc_call( &self, upstream_name: &str, service_name: &str, method_name: &str, initial_metadata: Vec<(&str, &[u8])>, message: Option<&[u8]>, timeout: Duration, ) -> Result { hostcalls::dispatch_grpc_call( upstream_name, service_name, method_name, initial_metadata, message, timeout, ) } fn on_grpc_call_response(&mut self, _token_id: u32, _status_code: u32, _response_size: usize) {} fn get_grpc_call_response_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::GrpcReceiveBuffer, start, max_size).unwrap() } fn cancel_grpc_call(&self, token_id: u32) { hostcalls::cancel_grpc_call(token_id).unwrap() } fn open_grpc_stream( &self, cluster_name: &str, service_name: &str, method_name: &str, initial_metadata: Vec<(&str, &[u8])>, ) -> Result { hostcalls::open_grpc_stream(cluster_name, service_name, method_name, initial_metadata) } fn on_grpc_stream_initial_metadata(&mut self, _token_id: u32, _num_elements: u32) {} fn get_grpc_stream_initial_metadata(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::GrpcReceiveInitialMetadata).unwrap() } fn get_grpc_stream_initial_metadata_value(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::GrpcReceiveInitialMetadata, name).unwrap() } fn send_grpc_stream_message(&self, token_id: u32, message: Option<&[u8]>, end_stream: bool) { hostcalls::send_grpc_stream_message(token_id, message, end_stream).unwrap() } fn on_grpc_stream_message(&mut self, _token_id: u32, _message_size: usize) {} fn get_grpc_stream_message(&mut self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::GrpcReceiveBuffer, start, max_size).unwrap() } fn on_grpc_stream_trailing_metadata(&mut self, _token_id: u32, _num_elements: u32) {} fn get_grpc_stream_trailing_metadata(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::GrpcReceiveTrailingMetadata).unwrap() } fn get_grpc_stream_trailing_metadata_value(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::GrpcReceiveTrailingMetadata, name).unwrap() } fn cancel_grpc_stream(&self, token_id: u32) { hostcalls::cancel_grpc_stream(token_id).unwrap() } fn close_grpc_stream(&self, token_id: u32) { hostcalls::close_grpc_stream(token_id).unwrap() } fn on_grpc_stream_close(&mut self, _token_id: u32, _status_code: u32) {} fn get_grpc_status(&self) -> (u32, Option) { hostcalls::get_grpc_status().unwrap() } fn on_foreign_function(&mut self, _function_id: u32, _arguments_size: usize) {} fn call_foreign_function( &self, function_name: &str, arguments: Option<&[u8]>, ) -> Result, Status> { hostcalls::call_foreign_function(function_name, arguments) } fn on_done(&mut self) -> bool { true } fn done(&self) { hostcalls::done().unwrap() } } pub trait RootContext: Context { fn on_vm_start(&mut self, _vm_configuration_size: usize) -> bool { true } fn get_vm_configuration(&self) -> Option { hostcalls::get_buffer(BufferType::VmConfiguration, 0, usize::MAX).unwrap() } fn on_configure(&mut self, _plugin_configuration_size: usize) -> bool { true } fn get_plugin_configuration(&self) -> Option { hostcalls::get_buffer(BufferType::PluginConfiguration, 0, usize::MAX).unwrap() } fn set_tick_period(&self, period: Duration) { hostcalls::set_tick_period(period).unwrap() } fn on_tick(&mut self) {} fn on_queue_ready(&mut self, _queue_id: u32) {} fn on_log(&mut self) {} fn create_http_context(&self, _context_id: u32) -> Option> { None } fn create_stream_context(&self, _context_id: u32) -> Option> { None } fn get_type(&self) -> Option { None } } pub trait StreamContext: Context { fn on_new_connection(&mut self) -> Action { Action::Continue } fn on_downstream_data(&mut self, _data_size: usize, _end_of_stream: bool) -> Action { Action::Continue } fn get_downstream_data(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::DownstreamData, start, max_size).unwrap() } fn set_downstream_data(&self, start: usize, size: usize, value: &[u8]) { hostcalls::set_buffer(BufferType::DownstreamData, start, size, value).unwrap() } fn resume_downstream(&self) { hostcalls::resume_downstream().unwrap() } fn close_downstream(&self) { hostcalls::close_downstream().unwrap() } fn on_downstream_close(&mut self, _peer_type: PeerType) {} fn on_upstream_data(&mut self, _data_size: usize, _end_of_stream: bool) -> Action { Action::Continue } fn get_upstream_data(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::UpstreamData, start, max_size).unwrap() } fn set_upstream_data(&self, start: usize, size: usize, value: &[u8]) { hostcalls::set_buffer(BufferType::UpstreamData, start, size, value).unwrap() } fn resume_upstream(&self) { hostcalls::resume_upstream().unwrap() } fn close_upstream(&self) { hostcalls::close_upstream().unwrap() } fn on_upstream_close(&mut self, _peer_type: PeerType) {} fn on_log(&mut self) {} } pub trait HttpContext: Context { fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action { Action::Continue } fn get_http_request_headers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpRequestHeaders).unwrap() } fn get_http_request_headers_bytes(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::HttpRequestHeaders).unwrap() } fn set_http_request_headers(&self, headers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap() } fn set_http_request_headers_bytes(&self, headers: Vec<(&str, &[u8])>) { hostcalls::set_map_bytes(MapType::HttpRequestHeaders, headers).unwrap() } fn get_http_request_header(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap() } fn get_http_request_header_bytes(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::HttpRequestHeaders, name).unwrap() } fn set_http_request_header(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap() } fn set_http_request_header_bytes(&self, name: &str, value: Option<&[u8]>) { hostcalls::set_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap() } fn add_http_request_header(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap() } fn add_http_request_header_bytes(&self, name: &str, value: &[u8]) { hostcalls::add_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap() } fn remove_http_request_header(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpRequestHeaders, name).unwrap() } fn on_http_request_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action { Action::Continue } fn get_http_request_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpRequestBody, start, max_size).unwrap() } fn set_http_request_body(&self, start: usize, size: usize, value: &[u8]) { hostcalls::set_buffer(BufferType::HttpRequestBody, start, size, value).unwrap() } fn on_http_request_trailers(&mut self, _num_trailers: usize) -> Action { Action::Continue } fn get_http_request_trailers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpRequestTrailers).unwrap() } fn get_http_request_trailers_bytes(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::HttpRequestTrailers).unwrap() } fn set_http_request_trailers(&self, trailers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap() } fn set_http_request_trailers_bytes(&self, trailers: Vec<(&str, &[u8])>) { hostcalls::set_map_bytes(MapType::HttpRequestTrailers, trailers).unwrap() } fn get_http_request_trailer(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap() } fn get_http_request_trailer_bytes(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::HttpRequestTrailers, name).unwrap() } fn set_http_request_trailer(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap() } fn set_http_request_trailer_bytes(&self, name: &str, value: Option<&[u8]>) { hostcalls::set_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap() } fn add_http_request_trailer(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap() } fn add_http_request_trailer_bytes(&self, name: &str, value: &[u8]) { hostcalls::add_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap() } fn remove_http_request_trailer(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpRequestTrailers, name).unwrap() } fn resume_http_request(&self) { hostcalls::resume_http_request().unwrap() } fn reset_http_request(&self) { hostcalls::reset_http_request().unwrap() } fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action { Action::Continue } fn get_http_response_headers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpResponseHeaders).unwrap() } fn get_http_response_headers_bytes(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::HttpResponseHeaders).unwrap() } fn set_http_response_headers(&self, headers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap() } fn set_http_response_headers_bytes(&self, headers: Vec<(&str, &[u8])>) { hostcalls::set_map_bytes(MapType::HttpResponseHeaders, headers).unwrap() } fn get_http_response_header(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap() } fn get_http_response_header_bytes(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::HttpResponseHeaders, name).unwrap() } fn set_http_response_header(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap() } fn set_http_response_header_bytes(&self, name: &str, value: Option<&[u8]>) { hostcalls::set_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap() } fn add_http_response_header(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap() } fn add_http_response_header_bytes(&self, name: &str, value: &[u8]) { hostcalls::add_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap() } fn remove_http_response_header(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpResponseHeaders, name).unwrap() } fn on_http_response_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action { Action::Continue } fn get_http_response_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpResponseBody, start, max_size).unwrap() } fn set_http_response_body(&self, start: usize, size: usize, value: &[u8]) { hostcalls::set_buffer(BufferType::HttpResponseBody, start, size, value).unwrap() } fn on_http_response_trailers(&mut self, _num_trailers: usize) -> Action { Action::Continue } fn get_http_response_trailers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpResponseTrailers).unwrap() } fn get_http_response_trailers_bytes(&self) -> Vec<(String, Bytes)> { hostcalls::get_map_bytes(MapType::HttpResponseTrailers).unwrap() } fn set_http_response_trailers(&self, trailers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap() } fn set_http_response_trailers_bytes(&self, trailers: Vec<(&str, &[u8])>) { hostcalls::set_map_bytes(MapType::HttpResponseTrailers, trailers).unwrap() } fn get_http_response_trailer(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap() } fn get_http_response_trailer_bytes(&self, name: &str) -> Option { hostcalls::get_map_value_bytes(MapType::HttpResponseTrailers, name).unwrap() } fn set_http_response_trailer(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap() } fn set_http_response_trailer_bytes(&self, name: &str, value: Option<&[u8]>) { hostcalls::set_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap() } fn add_http_response_trailer(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap() } fn add_http_response_trailer_bytes(&self, name: &str, value: &[u8]) { hostcalls::add_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap() } fn remove_http_response_trailer(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpResponseTrailers, name).unwrap() } fn resume_http_response(&self) { hostcalls::resume_http_response().unwrap() } fn reset_http_response(&self) { hostcalls::reset_http_response().unwrap() } fn send_http_response( &self, status_code: u32, headers: Vec<(&str, &str)>, body: Option<&[u8]>, ) { hostcalls::send_http_response(status_code, headers, body).unwrap() } fn send_grpc_response( &self, grpc_status: GrpcStatusCode, grpc_status_message: Option<&str>, custom_metadata: Vec<(&str, &[u8])>, ) { hostcalls::send_grpc_response(grpc_status, grpc_status_message, custom_metadata).unwrap() } fn on_log(&mut self) {} } ================================================ FILE: src/types.rs ================================================ // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::traits::*; pub type NewRootContext = fn(context_id: u32) -> Box; pub type NewStreamContext = fn(context_id: u32, root_context_id: u32) -> Box; pub type NewHttpContext = fn(context_id: u32, root_context_id: u32) -> Box; #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum LogLevel { Trace = 0, Debug = 1, Info = 2, Warn = 3, Error = 4, Critical = 5, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum Action { Continue = 0, Pause = 1, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum Status { Ok = 0, NotFound = 1, BadArgument = 2, SerializationFailure = 3, ParseFailure = 4, Empty = 7, CasMismatch = 8, InternalFailure = 10, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum ContextType { HttpContext = 0, StreamContext = 1, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum StreamType { HttpRequest = 0, HttpResponse = 1, Downstream = 2, Upstream = 3, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum BufferType { HttpRequestBody = 0, HttpResponseBody = 1, DownstreamData = 2, UpstreamData = 3, HttpCallResponseBody = 4, GrpcReceiveBuffer = 5, VmConfiguration = 6, PluginConfiguration = 7, CallData = 8, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum MapType { HttpRequestHeaders = 0, HttpRequestTrailers = 1, HttpResponseHeaders = 2, HttpResponseTrailers = 3, GrpcReceiveInitialMetadata = 4, GrpcReceiveTrailingMetadata = 5, HttpCallResponseHeaders = 6, HttpCallResponseTrailers = 7, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum PeerType { Unknown = 0, Local = 1, Remote = 2, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum MetricType { Counter = 0, Gauge = 1, Histogram = 2, } #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub enum GrpcStatusCode { Ok = 0, Cancelled = 1, Unknown = 2, InvalidArgument = 3, DeadlineExceeded = 4, NotFound = 5, AlreadyExists = 6, PermissionDenied = 7, ResourceExhausted = 8, FailedPrecondition = 9, Aborted = 10, OutOfRange = 11, Unimplemented = 12, Internal = 13, Unavailable = 14, DataLoss = 15, Unauthenticated = 16, } pub type Bytes = Vec;