diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 02835b36..2ff86288 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -96,7 +96,9 @@ jobs: - name: Format (rules_rust) run: | + sed -i'' -E 's/^default = \[\]/default = \[\"header-value\"\]/' Cargo.toml bazelisk --noworkspace_rc run --noenable_bzlmod //bazel/cargo:crates_vendor + git checkout Cargo.toml git diff --exit-code msrv: @@ -150,9 +152,18 @@ jobs: - name: Clippy (wasm32-wasi) run: cargo clippy --release --all-targets --target=wasm32-wasi + - name: Build (header-value) + run: cargo build --release --all-targets --target=wasm32-wasi --features header-value + + - name: Clippy (header-value) + run: cargo clippy --release --all-targets --target=wasm32-wasi --features header-value + - name: Test run: cargo test + - name: Test (header-value) + run: cargo test --features header-value + - name: Format (rustfmt) run: cargo fmt -- --check @@ -215,9 +226,18 @@ jobs: - name: Clippy (wasm32-wasip1) run: cargo clippy --release --all-targets --target=wasm32-wasip1 + - name: Build (header-value) + run: cargo build --release --all-targets --target=wasm32-wasip1 --features header-value + + - name: Clippy (header-value) + run: cargo clippy --release --all-targets --target=wasm32-wasip1 --features header-value + - name: Test run: cargo test + - name: Test (header-value) + run: cargo test --features header-value + - name: Format (rustfmt) run: cargo fmt -- --check @@ -281,12 +301,24 @@ jobs: - name: Clippy (wasm32-wasip1) run: cargo clippy --release --all-targets --target=wasm32-wasip1 + - name: Build (header-value) + run: cargo build --release --all-targets --target=wasm32-wasip1 --features header-value + + - name: Clippy (header-value) + run: cargo clippy --release --all-targets --target=wasm32-wasip1 --features header-value + - name: Test run: cargo test + - name: Test (header-value) + run: cargo test --features header-value + - name: Bench run: cargo bench + - name: Bench (header-value) + run: cargo bench --features header-value + - name: Format (rustfmt) run: cargo fmt -- --check diff --git a/BUILD b/BUILD index 7f611e4f..eb96231e 100644 --- a/BUILD +++ b/BUILD @@ -39,6 +39,22 @@ rust_library( ], ) +rust_library( + name = "proxy_wasm_header_value", + srcs = glob(["src/*.rs"]), + crate_features = ["header-value"], + crate_name = "proxy_wasm", + edition = "2018", + visibility = ["//visibility:public"], + deps = [ + ":proxy_wasm_build_script", + "//bazel/cargo/remote:bytes", + "//bazel/cargo/remote:hashbrown", + "//bazel/cargo/remote:http", + "//bazel/cargo/remote:log", + ], +) + rust_binary( name = "http_auth_random", srcs = ["examples/http_auth_random/src/lib.rs"], @@ -52,3 +68,17 @@ rust_binary( "//bazel/cargo/remote:log", ], ) + +rust_binary( + name = "grpc_auth_random", + srcs = ["examples/grpc_auth_random/src/lib.rs"], + crate_type = "cdylib", + edition = "2018", + out_binary = True, + rustc_flags = ["-Cstrip=debuginfo"], + visibility = ["//visibility:private"], + deps = [ + ":proxy_wasm_header_value", + "//bazel/cargo/remote:log", + ], +) diff --git a/Cargo.toml b/Cargo.toml index d23f2ce8..ea592ce8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,15 @@ edition = "2018" build = "build.rs" [dependencies] +bytes = { version = "1", optional = true } hashbrown = "0.15" +http = { version = "1", optional = true } log = "0.4" +[features] +default = [] +header-value = ["dep:bytes", "dep:http"] + [profile.release] lto = true opt-level = 3 diff --git a/README.md b/README.md index fa53c221..9b29505c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,13 @@ [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 +## Crate features + +This crate supports the following optional features: + +- `header-value` - uses RFC-compliant `HeaderValue` instead of UTF-8 `String` for HTTP header and trailer values. + This will become the default in future releases. + ## Examples - [Hello World](./examples/hello_world/) diff --git a/bazel/cargo/Cargo.Bazel.lock b/bazel/cargo/Cargo.Bazel.lock index 3426df9d..af59abd0 100644 --- a/bazel/cargo/Cargo.Bazel.lock +++ b/bazel/cargo/Cargo.Bazel.lock @@ -8,12 +8,24 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -31,6 +43,23 @@ dependencies = [ "foldhash", ] +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "log" version = "0.4.27" @@ -41,6 +70,8 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" name = "proxy-wasm" version = "0.2.4-dev" dependencies = [ + "bytes", "hashbrown", + "http", "log", ] diff --git a/bazel/cargo/remote/BUILD.bazel b/bazel/cargo/remote/BUILD.bazel index f2d36366..5453a065 100644 --- a/bazel/cargo/remote/BUILD.bazel +++ b/bazel/cargo/remote/BUILD.bazel @@ -31,6 +31,18 @@ filegroup( ) # Workspace Member Dependencies +alias( + name = "bytes-1.10.1", + actual = "@crates_vendor__bytes-1.10.1//:bytes", + tags = ["manual"], +) + +alias( + name = "bytes", + actual = "@crates_vendor__bytes-1.10.1//:bytes", + tags = ["manual"], +) + alias( name = "hashbrown-0.15.3", actual = "@crates_vendor__hashbrown-0.15.3//:hashbrown", @@ -43,6 +55,18 @@ alias( tags = ["manual"], ) +alias( + name = "http-1.3.1", + actual = "@crates_vendor__http-1.3.1//:http", + tags = ["manual"], +) + +alias( + name = "http", + actual = "@crates_vendor__http-1.3.1//:http", + tags = ["manual"], +) + alias( name = "log-0.4.27", actual = "@crates_vendor__log-0.4.27//:log", diff --git a/bazel/cargo/remote/BUILD.bytes-1.10.1.bazel b/bazel/cargo/remote/BUILD.bytes-1.10.1.bazel new file mode 100644 index 00000000..1f0ddc76 --- /dev/null +++ b/bazel/cargo/remote/BUILD.bytes-1.10.1.bazel @@ -0,0 +1,96 @@ +############################################################################### +# @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 = "bytes", + 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 = [ + "default", + "std", + ], + crate_root = "src/lib.rs", + edition = "2018", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=bytes", + "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-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-unknown": [], + "@rules_rust//rust/platform:wasm32-wasip1": [], + "@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.10.1", +) diff --git a/bazel/cargo/remote/BUILD.fnv-1.0.7.bazel b/bazel/cargo/remote/BUILD.fnv-1.0.7.bazel new file mode 100644 index 00000000..66e000d8 --- /dev/null +++ b/bazel/cargo/remote/BUILD.fnv-1.0.7.bazel @@ -0,0 +1,96 @@ +############################################################################### +# @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 = "fnv", + 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 = [ + "default", + "std", + ], + crate_root = "lib.rs", + edition = "2015", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=fnv", + "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-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-unknown": [], + "@rules_rust//rust/platform:wasm32-wasip1": [], + "@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.7", +) diff --git a/bazel/cargo/remote/BUILD.http-1.3.1.bazel b/bazel/cargo/remote/BUILD.http-1.3.1.bazel new file mode 100644 index 00000000..502f6e03 --- /dev/null +++ b/bazel/cargo/remote/BUILD.http-1.3.1.bazel @@ -0,0 +1,101 @@ +############################################################################### +# @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 = "http", + 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 = [ + "default", + "std", + ], + crate_root = "src/lib.rs", + edition = "2018", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=http", + "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-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-unknown": [], + "@rules_rust//rust/platform:wasm32-wasip1": [], + "@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.3.1", + deps = [ + "@crates_vendor__bytes-1.10.1//:bytes", + "@crates_vendor__fnv-1.0.7//:fnv", + "@crates_vendor__itoa-1.0.15//:itoa", + ], +) diff --git a/bazel/cargo/remote/BUILD.itoa-1.0.15.bazel b/bazel/cargo/remote/BUILD.itoa-1.0.15.bazel new file mode 100644 index 00000000..f781307a --- /dev/null +++ b/bazel/cargo/remote/BUILD.itoa-1.0.15.bazel @@ -0,0 +1,92 @@ +############################################################################### +# @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 = "itoa", + 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 = "2018", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=itoa", + "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-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-unknown": [], + "@rules_rust//rust/platform:wasm32-wasip1": [], + "@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.15", +) diff --git a/bazel/cargo/remote/defs.bzl b/bazel/cargo/remote/defs.bzl index 80b74d6b..ab786d99 100644 --- a/bazel/cargo/remote/defs.bzl +++ b/bazel/cargo/remote/defs.bzl @@ -295,7 +295,9 @@ def aliases( _NORMAL_DEPENDENCIES = { "": { _COMMON_CONDITION: { + "bytes": Label("@crates_vendor//:bytes-1.10.1"), "hashbrown": Label("@crates_vendor//:hashbrown-0.15.3"), + "http": Label("@crates_vendor//:http-1.3.1"), "log": Label("@crates_vendor//:log-0.4.27"), }, }, @@ -415,6 +417,16 @@ def crate_repositories(): build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.allocator-api2-0.2.21.bazel"), ) + maybe( + http_archive, + name = "crates_vendor__bytes-1.10.1", + sha256 = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a", + type = "tar.gz", + urls = ["https://static.crates.io/crates/bytes/1.10.1/download"], + strip_prefix = "bytes-1.10.1", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.bytes-1.10.1.bazel"), + ) + maybe( http_archive, name = "crates_vendor__equivalent-1.0.2", @@ -425,6 +437,16 @@ def crate_repositories(): build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.equivalent-1.0.2.bazel"), ) + maybe( + http_archive, + name = "crates_vendor__fnv-1.0.7", + sha256 = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1", + type = "tar.gz", + urls = ["https://static.crates.io/crates/fnv/1.0.7/download"], + strip_prefix = "fnv-1.0.7", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.fnv-1.0.7.bazel"), + ) + maybe( http_archive, name = "crates_vendor__foldhash-0.1.5", @@ -445,6 +467,26 @@ def crate_repositories(): build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.hashbrown-0.15.3.bazel"), ) + maybe( + http_archive, + name = "crates_vendor__http-1.3.1", + sha256 = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565", + type = "tar.gz", + urls = ["https://static.crates.io/crates/http/1.3.1/download"], + strip_prefix = "http-1.3.1", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.http-1.3.1.bazel"), + ) + + maybe( + http_archive, + name = "crates_vendor__itoa-1.0.15", + sha256 = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c", + type = "tar.gz", + urls = ["https://static.crates.io/crates/itoa/1.0.15/download"], + strip_prefix = "itoa-1.0.15", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.itoa-1.0.15.bazel"), + ) + maybe( http_archive, name = "crates_vendor__log-0.4.27", @@ -456,6 +498,8 @@ def crate_repositories(): ) return [ + struct(repo = "crates_vendor__bytes-1.10.1", is_dev_dep = False), struct(repo = "crates_vendor__hashbrown-0.15.3", is_dev_dep = False), + struct(repo = "crates_vendor__http-1.3.1", is_dev_dep = False), struct(repo = "crates_vendor__log-0.4.27", is_dev_dep = False), ] diff --git a/examples/grpc_auth_random/Cargo.toml b/examples/grpc_auth_random/Cargo.toml index c3e6ec01..3e5b3be6 100644 --- a/examples/grpc_auth_random/Cargo.toml +++ b/examples/grpc_auth_random/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] [dependencies] log = "0.4" -proxy-wasm = { path = "../../" } +proxy-wasm = { path = "../../", features = ["header-value"] } [profile.release] lto = true diff --git a/examples/grpc_auth_random/src/lib.rs b/examples/grpc_auth_random/src/lib.rs index d1c589e9..11df530e 100644 --- a/examples/grpc_auth_random/src/lib.rs +++ b/examples/grpc_auth_random/src/lib.rs @@ -27,12 +27,12 @@ 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") => {} + Some(value) if value.as_bytes().starts_with(b"application/grpc") => {} _ => { // Reject non-gRPC clients. self.send_http_response( 503, - vec![("Powered-By", "proxy-wasm")], + vec![("Powered-By", &HeaderValue::from_static("proxy-wasm"))], Some(b"Service accessible only to gRPC clients.\n"), ); return Action::Pause; @@ -40,7 +40,7 @@ impl HttpContext for GrpcAuthRandom { } match self.get_http_request_header(":path") { - Some(value) if value.starts_with("/grpc.reflection") => { + Some(value) if value.as_bytes().starts_with(b"/grpc.reflection") => { // Always allow gRPC calls to the reflection API. Action::Continue } @@ -61,7 +61,7 @@ impl HttpContext for GrpcAuthRandom { } fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { - self.set_http_response_header("Powered-By", Some("proxy-wasm")); + self.set_http_response_header("Powered-By", Some(&HeaderValue::from_static("proxy-wasm"))); Action::Continue } } diff --git a/examples/http_headers/Cargo.toml b/examples/http_headers/Cargo.toml index 02afd242..2fb8fa6a 100644 --- a/examples/http_headers/Cargo.toml +++ b/examples/http_headers/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] [dependencies] log = "0.4" -proxy-wasm = { path = "../../" } +proxy-wasm = { path = "../../", features = ["header-value"] } [profile.release] lto = true diff --git a/examples/http_headers/src/lib.rs b/examples/http_headers/src/lib.rs index 315a7b88..a6a4942b 100644 --- a/examples/http_headers/src/lib.rs +++ b/examples/http_headers/src/lib.rs @@ -44,14 +44,22 @@ 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); + info!( + "#{} -> {}: {}", + self.context_id, + name, + value.to_str().unwrap_or("<non-printable>") + ); } match self.get_http_request_header(":path") { Some(path) if path == "/hello" => { self.send_http_response( 200, - vec![("Hello", "World"), ("Powered-By", "proxy-wasm")], + vec![ + ("Hello", &HeaderValue::from_static("World")), + ("Powered-By", &HeaderValue::from_static("proxy-wasm")), + ], Some(b"Hello, World!\n"), ); Action::Pause @@ -62,7 +70,12 @@ impl HttpContext for HttpHeaders { 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); + info!( + "#{} <- {}: {}", + self.context_id, + name, + value.to_str().unwrap_or("<non-printable>") + ); } Action::Continue } diff --git a/src/hostcalls.rs b/src/hostcalls.rs index 76c3513d..a259d946 100644 --- a/src/hostcalls.rs +++ b/src/hostcalls.rs @@ -145,6 +145,27 @@ extern "C" { ) -> Status; } +#[cfg(feature = "header-value")] +pub fn get_map(map_type: MapType) -> Result<Vec<(String, HeaderValue)>, 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 = + bytes::Bytes::from(std::slice::from_raw_parts(return_data, return_size)); + Ok(utils::deserialize_map(serialized_map)) + } else { + Ok(Vec::new()) + } + } + status => panic!("unexpected status: {}", status as u32), + } + } +} + +#[cfg(not(feature = "header-value"))] pub fn get_map(map_type: MapType) -> Result<Vec<(String, String)>, Status> { unsafe { let mut return_data: *mut u8 = null_mut(); @@ -189,7 +210,10 @@ extern "C" { ) -> Status; } -pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status> { +pub fn set_map<V>(map_type: MapType, map: Vec<(&str, V)>) -> Result<(), Status> +where + V: AsRef<[u8]>, +{ let serialized_map = utils::serialize_map(&map); unsafe { match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) { @@ -200,13 +224,7 @@ pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status> } 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), - } - } + set_map(map_type, map) } extern "C" { @@ -219,6 +237,38 @@ extern "C" { ) -> Status; } +#[cfg(feature = "header-value")] +pub fn get_map_value(map_type: MapType, key: &str) -> Result<Option<HeaderValue>, 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() { + match HeaderValue::from_bytes(std::slice::from_raw_parts( + return_data, + return_size, + )) { + Ok(value) => Ok(Some(value)), + Err(_) => panic!("illegal field value in: {}", key), + } + } else { + Ok(Some(HeaderValue::from_static(""))) + } + } + Status::NotFound => Ok(None), + status => panic!("unexpected status: {}", status as u32), + } + } +} + +#[cfg(not(feature = "header-value"))] pub fn get_map_value(map_type: MapType, key: &str) -> Result<Option<String>, Status> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; @@ -305,15 +355,18 @@ extern "C" { ) -> Status; } -pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Result<(), Status> { +pub fn set_map_value<V>(map_type: MapType, key: &str, value: Option<V>) -> Result<(), Status> +where + V: AsRef<[u8]>, +{ unsafe { if let Some(value) = value { match proxy_replace_header_map_value( map_type, key.as_ptr(), key.len(), - value.as_ptr(), - value.len(), + value.as_ref().as_ptr(), + value.as_ref().len(), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), @@ -332,25 +385,7 @@ pub fn set_map_value_bytes( 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), - } - } - } + set_map_value(map_type, key, value) } extern "C" { @@ -363,14 +398,17 @@ extern "C" { ) -> Status; } -pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<(), Status> { +pub fn add_map_value<V>(map_type: MapType, key: &str, value: V) -> Result<(), Status> +where + V: AsRef<[u8]>, +{ unsafe { match proxy_add_header_map_value( map_type, key.as_ptr(), key.len(), - value.as_ptr(), - value.len(), + value.as_ref().as_ptr(), + value.as_ref().len(), ) { Status::Ok => Ok(()), status => panic!("unexpected status: {}", status as u32), @@ -379,18 +417,7 @@ pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<(), St } 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), - } - } + add_map_value(map_type, key, value) } extern "C" { @@ -714,11 +741,14 @@ extern "C" { ) -> Status; } -pub fn send_http_response( +pub fn send_http_response<V>( status_code: u32, - headers: Vec<(&str, &str)>, + headers: Vec<(&str, V)>, body: Option<&[u8]>, -) -> Result<(), Status> { +) -> Result<(), Status> +where + V: AsRef<[u8]>, +{ let serialized_headers = utils::serialize_map(&headers); unsafe { match proxy_send_local_response( @@ -775,13 +805,16 @@ extern "C" { ) -> Status; } -pub fn dispatch_http_call( +pub fn dispatch_http_call<V>( upstream: &str, - headers: Vec<(&str, &str)>, + headers: Vec<(&str, V)>, body: Option<&[u8]>, - trailers: Vec<(&str, &str)>, + trailers: Vec<(&str, V)>, timeout: Duration, -) -> Result<u32, Status> { +) -> Result<u32, Status> +where + V: AsRef<[u8]>, +{ let serialized_headers = utils::serialize_map(&headers); let serialized_trailers = utils::serialize_map(&trailers); let mut return_token: u32 = 0; @@ -1149,6 +1182,10 @@ pub fn increment_metric(metric_id: u32, offset: i64) -> Result<(), Status> { mod utils { use crate::types::Bytes; + #[cfg(feature = "header-value")] + use crate::types::HeaderValue; + #[cfg(feature = "header-value")] + use bytes::Buf; use std::convert::TryFrom; pub(super) fn serialize_property_path(path: Vec<&str>) -> Bytes { @@ -1168,46 +1205,60 @@ mod utils { bytes } - pub(super) fn serialize_map(map: &[(&str, &str)]) -> Bytes { + pub(super) fn serialize_map<V>(map: &[(&str, V)]) -> Bytes + where + V: AsRef<[u8]>, + { let mut size: usize = 4; for (name, value) in map { - size += name.len() + value.len() + 10; + size += name.len() + value.as_ref().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()); + bytes.extend_from_slice(&(value.as_ref().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.extend_from_slice(value.as_ref()); 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()); + serialize_map(map) + } + + #[cfg(feature = "header-value")] + pub(super) fn deserialize_map(mut bytes: bytes::Bytes) -> Vec<(String, HeaderValue)> { + if bytes.is_empty() { + return Vec::new(); } - for (name, value) in map { - bytes.extend_from_slice(name.as_bytes()); - bytes.push(0); - bytes.extend_from_slice(value); - bytes.push(0); + let size = bytes.get_u32_le() as usize; + let mut sizes = bytes.split_to(size * 8); + let mut map = Vec::with_capacity(size); + for _ in 0..size { + let size = sizes.get_u32_le() as usize; + let key = bytes.split_to(size); + bytes.advance(1); + let size = sizes.get_u32_le() as usize; + let value = bytes.split_to(size); + bytes.advance(1); + map.push(( + String::from_utf8(key.to_vec()).unwrap(), + // We're intentionally using the unchecked variant in order to retain + // values accepted by the hosts and proxies that don't enforce strict + // RFC compliance on HTTP field values. + unsafe { HeaderValue::from_maybe_shared_unchecked(value) }, + )); } - bytes + map } + #[cfg(not(feature = "header-value"))] pub(super) fn deserialize_map(bytes: &[u8]) -> Vec<(String, String)> { if bytes.is_empty() { return Vec::new(); @@ -1297,23 +1348,32 @@ mod utils { 112, 114, 111, 120, 121, 45, 119, 97, 115, 109, 0, ]; + #[rustfmt::skip] + static SERIALIZED_EMPTY_MAP: &[u8] = &[ + // num entries + 0, 0, 0, 0, + ]; + #[test] fn test_serialize_map_empty() { - let serialized_map = serialize_map(&[]); - assert_eq!(serialized_map, [0, 0, 0, 0]); + let serialized_map = serialize_map::<&str>(&[]); + assert_eq!(serialized_map, SERIALIZED_EMPTY_MAP); } #[test] fn test_serialize_map_empty_bytes() { let serialized_map = serialize_map_bytes(&[]); - assert_eq!(serialized_map, [0, 0, 0, 0]); + assert_eq!(serialized_map, SERIALIZED_EMPTY_MAP); } #[test] fn test_deserialize_map_empty() { - let map = deserialize_map(&[]); + let empty_map: &[u8] = &[]; + #[allow(clippy::useless_conversion)] + let map = deserialize_map(empty_map.into()); assert_eq!(map, []); - let map = deserialize_map(&[0, 0, 0, 0]); + #[allow(clippy::useless_conversion)] + let map = deserialize_map(SERIALIZED_EMPTY_MAP.into()); assert_eq!(map, []); } @@ -1321,7 +1381,7 @@ mod utils { fn test_deserialize_map_empty_bytes() { let map = deserialize_map_bytes(&[]); assert_eq!(map, []); - let map = deserialize_map_bytes(&[0, 0, 0, 0]); + let map = deserialize_map_bytes(SERIALIZED_EMPTY_MAP); assert_eq!(map, []); } @@ -1340,7 +1400,8 @@ mod utils { #[test] fn test_deserialize_map() { - let map = deserialize_map(SERIALIZED_MAP); + #[allow(clippy::useless_conversion)] + let map = deserialize_map(SERIALIZED_MAP.into()); assert_eq!(map.len(), MAP.len()); for (got, expected) in map.into_iter().zip(MAP) { assert_eq!(got.0, expected.0); @@ -1360,9 +1421,10 @@ mod utils { #[test] fn test_deserialize_map_roundtrip() { - let map = deserialize_map(SERIALIZED_MAP); + #[allow(clippy::useless_conversion)] + let map = deserialize_map(SERIALIZED_MAP.into()); // TODO(v0.3): fix arguments, so that maps can be reused without conversion. - let map_refs: Vec<(&str, &str)> = + let map_refs: Vec<(&str, &[u8])> = 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); @@ -1378,12 +1440,29 @@ mod utils { assert_eq!(serialized_map, SERIALIZED_MAP); } + #[cfg(feature = "header-value")] + #[test] + fn test_deserialize_map_all_chars() { + // We're intentionally accepting all values to support hosts and proxies that + // don't enforce strict RFC compliance on HTTP field values. + for i in 0..0xff { + let serialized_src: &[u8] = &[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 99, 0, i, 0]; + let map = deserialize_map(serialized_src.to_vec().into()); + // 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(&map_refs); + assert_eq!(serialized_map, serialized_src); + } + } + + #[cfg(not(feature = "header-value"))] #[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); + let serialized_src: &[u8] = &[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(); @@ -1392,10 +1471,10 @@ mod utils { } // 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]; + let serialized_src: &[u8] = &[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); + deserialize_map(serialized_src); }); assert!(result.is_err()); } @@ -1405,8 +1484,8 @@ mod utils { 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); + let serialized_src: &[u8] = &[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(); @@ -1433,6 +1512,17 @@ mod utils { }); } + #[cfg(feature = "header-value")] + #[cfg(nightly)] + #[bench] + fn bench_deserialize_map(b: &mut Bencher) { + let serialized_map: bytes::Bytes = SERIALIZED_MAP.into(); + b.iter(|| { + deserialize_map(test::black_box(serialized_map.clone())); + }); + } + + #[cfg(not(feature = "header-value"))] #[cfg(nightly)] #[bench] fn bench_deserialize_map(b: &mut Bencher) { diff --git a/src/traits.rs b/src/traits.rs index f43663a1..2724297e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -62,6 +62,19 @@ pub trait Context { hostcalls::enqueue_shared_queue(queue_id, value) } + #[cfg(feature = "header-value")] + fn dispatch_http_call( + &self, + upstream: &str, + headers: Vec<(&str, &HeaderValue)>, + body: Option<&[u8]>, + trailers: Vec<(&str, &HeaderValue)>, + timeout: Duration, + ) -> Result<u32, Status> { + hostcalls::dispatch_http_call(upstream, headers, body, trailers, timeout) + } + + #[cfg(not(feature = "header-value"))] fn dispatch_http_call( &self, upstream: &str, @@ -82,6 +95,12 @@ pub trait Context { ) { } + #[cfg(feature = "header-value")] + fn get_http_call_response_headers(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_call_response_headers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap() } @@ -90,6 +109,12 @@ pub trait Context { hostcalls::get_map_bytes(MapType::HttpCallResponseHeaders).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_call_response_header(&self, name: &str) -> Option<HeaderValue> { + hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_call_response_header(&self, name: &str) -> Option<String> { hostcalls::get_map_value(MapType::HttpCallResponseHeaders, name).unwrap() } @@ -102,6 +127,12 @@ pub trait Context { hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_call_response_trailers(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_call_response_trailers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap() } @@ -110,6 +141,12 @@ pub trait Context { hostcalls::get_map_bytes(MapType::HttpCallResponseTrailers).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_call_response_trailer(&self, name: &str) -> Option<HeaderValue> { + hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_call_response_trailer(&self, name: &str) -> Option<String> { hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap() } @@ -317,6 +354,12 @@ pub trait HttpContext: Context { Action::Continue } + #[cfg(feature = "header-value")] + fn get_http_request_headers(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map(MapType::HttpRequestHeaders).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_request_headers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpRequestHeaders).unwrap() } @@ -325,6 +368,12 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpRequestHeaders).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_request_headers(&self, headers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_request_headers(&self, headers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap() } @@ -333,6 +382,12 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpRequestHeaders, headers).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_request_header(&self, name: &str) -> Option<HeaderValue> { + hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_request_header(&self, name: &str) -> Option<String> { hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap() } @@ -341,6 +396,12 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpRequestHeaders, name).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_request_header(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_request_header(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap() } @@ -349,6 +410,12 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap() } + #[cfg(feature = "header-value")] + fn add_http_request_header(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn add_http_request_header(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap() } @@ -377,6 +444,12 @@ pub trait HttpContext: Context { Action::Continue } + #[cfg(feature = "header-value")] + fn get_http_request_trailers(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map(MapType::HttpRequestTrailers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_request_trailers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpRequestTrailers).unwrap() } @@ -385,6 +458,12 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpRequestTrailers).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_request_trailers(&self, trailers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_request_trailers(&self, trailers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap() } @@ -393,6 +472,12 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpRequestTrailers, trailers).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_request_trailer(&self, name: &str) -> Option<HeaderValue> { + hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_request_trailer(&self, name: &str) -> Option<String> { hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap() } @@ -401,6 +486,12 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpRequestTrailers, name).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_request_trailer(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_request_trailer(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap() } @@ -409,6 +500,12 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap() } + #[cfg(feature = "header-value")] + fn add_http_request_trailer(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn add_http_request_trailer(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap() } @@ -433,6 +530,12 @@ pub trait HttpContext: Context { Action::Continue } + #[cfg(feature = "header-value")] + fn get_http_response_headers(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map(MapType::HttpResponseHeaders).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_response_headers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpResponseHeaders).unwrap() } @@ -441,6 +544,12 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpResponseHeaders).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_response_headers(&self, headers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_response_headers(&self, headers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap() } @@ -449,6 +558,12 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpResponseHeaders, headers).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_response_header(&self, name: &str) -> Option<HeaderValue> { + hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_response_header(&self, name: &str) -> Option<String> { hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap() } @@ -457,6 +572,12 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpResponseHeaders, name).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_response_header(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_response_header(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap() } @@ -465,6 +586,12 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap() } + #[cfg(feature = "header-value")] + fn add_http_response_header(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn add_http_response_header(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap() } @@ -493,6 +620,12 @@ pub trait HttpContext: Context { Action::Continue } + #[cfg(feature = "header-value")] + fn get_http_response_trailers(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map(MapType::HttpResponseTrailers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_response_trailers(&self) -> Vec<(String, String)> { hostcalls::get_map(MapType::HttpResponseTrailers).unwrap() } @@ -501,6 +634,12 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpResponseTrailers).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_response_trailers(&self, trailers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_response_trailers(&self, trailers: Vec<(&str, &str)>) { hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap() } @@ -509,6 +648,12 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpResponseTrailers, trailers).unwrap() } + #[cfg(feature = "header-value")] + fn get_http_response_trailer(&self, name: &str) -> Option<HeaderValue> { + hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn get_http_response_trailer(&self, name: &str) -> Option<String> { hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap() } @@ -517,6 +662,12 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpResponseTrailers, name).unwrap() } + #[cfg(feature = "header-value")] + fn set_http_response_trailer(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn set_http_response_trailer(&self, name: &str, value: Option<&str>) { hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap() } @@ -525,6 +676,12 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap() } + #[cfg(feature = "header-value")] + fn add_http_response_trailer(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn add_http_response_trailer(&self, name: &str, value: &str) { hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap() } @@ -545,6 +702,17 @@ pub trait HttpContext: Context { hostcalls::reset_http_response().unwrap() } + #[cfg(feature = "header-value")] + fn send_http_response( + &self, + status_code: u32, + headers: Vec<(&str, &HeaderValue)>, + body: Option<&[u8]>, + ) { + hostcalls::send_http_response(status_code, headers, body).unwrap() + } + + #[cfg(not(feature = "header-value"))] fn send_http_response( &self, status_code: u32, diff --git a/src/types.rs b/src/types.rs index 4272ba90..a8e78f61 100644 --- a/src/types.rs +++ b/src/types.rs @@ -140,3 +140,6 @@ pub enum GrpcStatusCode { } pub type Bytes = Vec<u8>; + +#[cfg(feature = "header-value")] +pub use http::HeaderValue;