diff --git a/BUILD b/BUILD index 7f611e4f..f8b02ffb 100644 --- a/BUILD +++ b/BUILD @@ -34,7 +34,9 @@ rust_library( visibility = ["//visibility:public"], deps = [ ":proxy_wasm_build_script", + "//bazel/cargo/remote:bytes", "//bazel/cargo/remote:hashbrown", + "//bazel/cargo/remote:http", "//bazel/cargo/remote:log", ], ) diff --git a/Cargo.toml b/Cargo.toml index 9cebcfe5..814170cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,9 @@ edition = "2018" build = "build.rs" [dependencies] +bytes = "1" hashbrown = "0.16" +http = "1" log = "0.4" mockalloc = { version = "0.1", optional = true } diff --git a/MODULE.bazel b/MODULE.bazel index 050f8441..d907b0a6 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -52,6 +52,8 @@ crates_deps = use_extension("//bazel:extensions.bzl", "crates_deps") use_repo( crates_deps, "crates_vendor", + "crates_vendor__bytes-1.11.1", "crates_vendor__hashbrown-0.16.0", + "crates_vendor__http-1.4.0", "crates_vendor__log-0.4.27", ) diff --git a/bazel/cargo/Cargo.Bazel.lock b/bazel/cargo/Cargo.Bazel.lock index 21d41bc9..2498d622 100644 --- a/bazel/cargo/Cargo.Bazel.lock +++ b/bazel/cargo/Cargo.Bazel.lock @@ -8,6 +8,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + [[package]] name = "equivalent" version = "1.0.2" @@ -31,6 +37,22 @@ dependencies = [ "foldhash", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + [[package]] name = "log" version = "0.4.27" @@ -70,7 +92,9 @@ dependencies = [ name = "proxy-wasm" version = "0.2.5-dev" dependencies = [ + "bytes", "hashbrown", + "http", "log", "mockalloc", ] diff --git a/bazel/cargo/remote/BUILD.bazel b/bazel/cargo/remote/BUILD.bazel index ee93cf85..f1d1e02f 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.11.1", + actual = "@crates_vendor__bytes-1.11.1//:bytes", + tags = ["manual"], +) + +alias( + name = "bytes", + actual = "@crates_vendor__bytes-1.11.1//:bytes", + tags = ["manual"], +) + alias( name = "hashbrown-0.16.0", actual = "@crates_vendor__hashbrown-0.16.0//:hashbrown", @@ -43,6 +55,18 @@ alias( tags = ["manual"], ) +alias( + name = "http-1.4.0", + actual = "@crates_vendor__http-1.4.0//:http", + tags = ["manual"], +) + +alias( + name = "http", + actual = "@crates_vendor__http-1.4.0//: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.11.1.bazel b/bazel/cargo/remote/BUILD.bytes-1.11.1.bazel new file mode 100644 index 00000000..05c72588 --- /dev/null +++ b/bazel/cargo/remote/BUILD.bytes-1.11.1.bazel @@ -0,0 +1,100 @@ +############################################################################### +# @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 = "2021", + 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-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.11.1", +) diff --git a/bazel/cargo/remote/BUILD.http-1.4.0.bazel b/bazel/cargo/remote/BUILD.http-1.4.0.bazel new file mode 100644 index 00000000..4e880f0b --- /dev/null +++ b/bazel/cargo/remote/BUILD.http-1.4.0.bazel @@ -0,0 +1,104 @@ +############################################################################### +# @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 = "2021", + 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-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.4.0", + deps = [ + "@crates_vendor__bytes-1.11.1//:bytes", + "@crates_vendor__itoa-1.0.17//:itoa", + ], +) diff --git a/bazel/cargo/remote/BUILD.itoa-1.0.17.bazel b/bazel/cargo/remote/BUILD.itoa-1.0.17.bazel new file mode 100644 index 00000000..4748cb99 --- /dev/null +++ b/bazel/cargo/remote/BUILD.itoa-1.0.17.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 = "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 = "2021", + 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-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.17", +) diff --git a/bazel/cargo/remote/defs.bzl b/bazel/cargo/remote/defs.bzl index abd5ff52..199ebfe7 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.11.1"), "hashbrown": Label("@crates_vendor//:hashbrown-0.16.0"), + "http": Label("@crates_vendor//:http-1.4.0"), "log": Label("@crates_vendor//:log-0.4.27"), }, }, @@ -419,6 +421,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.11.1", + sha256 = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33", + type = "tar.gz", + urls = ["https://static.crates.io/crates/bytes/1.11.1/download"], + strip_prefix = "bytes-1.11.1", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.bytes-1.11.1.bazel"), + ) + maybe( http_archive, name = "crates_vendor__equivalent-1.0.2", @@ -449,6 +461,26 @@ def crate_repositories(): build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.hashbrown-0.16.0.bazel"), ) + maybe( + http_archive, + name = "crates_vendor__http-1.4.0", + sha256 = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a", + type = "tar.gz", + urls = ["https://static.crates.io/crates/http/1.4.0/download"], + strip_prefix = "http-1.4.0", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.http-1.4.0.bazel"), + ) + + maybe( + http_archive, + name = "crates_vendor__itoa-1.0.17", + sha256 = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2", + type = "tar.gz", + urls = ["https://static.crates.io/crates/itoa/1.0.17/download"], + strip_prefix = "itoa-1.0.17", + build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.itoa-1.0.17.bazel"), + ) + maybe( http_archive, name = "crates_vendor__log-0.4.27", @@ -460,6 +492,8 @@ def crate_repositories(): ) return [ + struct(repo = "crates_vendor__bytes-1.11.1", is_dev_dep = False), struct(repo = "crates_vendor__hashbrown-0.16.0", is_dev_dep = False), + struct(repo = "crates_vendor__http-1.4.0", is_dev_dep = False), struct(repo = "crates_vendor__log-0.4.27", is_dev_dep = False), ] diff --git a/examples/envoy_filter_metadata/src/lib.rs b/examples/envoy_filter_metadata/src/lib.rs index 2a2356df..fd8c5d42 100644 --- a/examples/envoy_filter_metadata/src/lib.rs +++ b/examples/envoy_filter_metadata/src/lib.rs @@ -33,12 +33,17 @@ impl HttpContext for MetadataHttp { "envoy.filters.http.lua", "uppercased-custom-metadata", ]) { - Some(metadata) => match String::from_utf8(metadata) { - Ok(data) => { - self.send_http_response( + Some(metadata) => match HeaderValue::from_maybe_shared(metadata) { + Ok(value) => { + self.send_http_response_typed( 200, - vec![("Powered-By", "proxy-wasm"), ("uppercased-metadata", &data)], - Some(format!("Custom response with Envoy metadata: {data:?}\n").as_bytes()), + vec![ + ("Powered-By", &HeaderValue::from_static("proxy-wasm")), + ("uppercased-metadata", &value), + ], + Some( + format!("Custom response with Envoy metadata: {value:?}\n").as_bytes(), + ), ); Action::Pause } diff --git a/examples/grpc_auth_random/src/lib.rs b/examples/grpc_auth_random/src/lib.rs index 54d227b2..aa5f5f04 100644 --- a/examples/grpc_auth_random/src/lib.rs +++ b/examples/grpc_auth_random/src/lib.rs @@ -26,21 +26,21 @@ 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") => {} + match self.get_http_request_header_typed("content-type") { + Some(value) if value.as_bytes().starts_with(b"application/grpc") => {} _ => { // Reject non-gRPC clients. - self.send_http_response( + self.send_http_response_typed( 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; } } - match self.get_http_request_header(":path") { - Some(value) if value.starts_with("/grpc.reflection") => { + match self.get_http_request_header_typed(":path") { + Some(value) if value.as_bytes().starts_with(b"/grpc.reflection") => { // Always allow gRPC calls to the reflection API. Action::Continue } @@ -61,7 +61,10 @@ 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_typed( + "Powered-By", + Some(&HeaderValue::from_static("proxy-wasm")), + ); Action::Continue } } diff --git a/examples/http_auth_random/src/lib.rs b/examples/http_auth_random/src/lib.rs index 4e539713..dd0ee8fe 100644 --- a/examples/http_auth_random/src/lib.rs +++ b/examples/http_auth_random/src/lib.rs @@ -26,12 +26,12 @@ struct HttpAuthRandom; impl HttpContext for HttpAuthRandom { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { - self.dispatch_http_call( + self.dispatch_http_call_typed( "httpbin", vec![ - (":method", "GET"), - (":path", "/bytes/1"), - (":authority", "httpbin.org"), + (":method", &HeaderValue::from_static("GET")), + (":path", &HeaderValue::from_static("/bytes/1")), + (":authority", &HeaderValue::from_static("httpbin.org")), ], None, vec![], @@ -42,7 +42,10 @@ impl HttpContext for HttpAuthRandom { } 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_typed( + "Powered-By", + Some(&HeaderValue::from_static("proxy-wasm")), + ); Action::Continue } } @@ -58,9 +61,9 @@ impl Context for HttpAuthRandom { } } info!("Access forbidden."); - self.send_http_response( + self.send_http_response_typed( 403, - vec![("Powered-By", "proxy-wasm")], + vec![("Powered-By", &HeaderValue::from_static("proxy-wasm"))], Some(b"Access forbidden.\n"), ); } diff --git a/examples/http_body/src/lib.rs b/examples/http_body/src/lib.rs index 397886a8..1d37d445 100644 --- a/examples/http_body/src/lib.rs +++ b/examples/http_body/src/lib.rs @@ -44,7 +44,7 @@ impl HttpContext for HttpBody { // 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); + self.set_http_response_header_typed("content-length", None); Action::Continue } diff --git a/examples/http_config/src/lib.rs b/examples/http_config/src/lib.rs index 7493bf5f..0328aaf8 100644 --- a/examples/http_config/src/lib.rs +++ b/examples/http_config/src/lib.rs @@ -19,26 +19,26 @@ proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box { Box::new(HttpConfigHeaderRoot { - header_content: String::new(), + header_content: HeaderValue::from_static(""), }) }); }} struct HttpConfigHeader { - header_content: String, + header_content: HeaderValue, } 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()); + self.add_http_response_header_typed("custom-header", &self.header_content); Action::Continue } } struct HttpConfigHeaderRoot { - header_content: String, + header_content: HeaderValue, } impl Context for HttpConfigHeaderRoot {} @@ -46,7 +46,7 @@ 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() + self.header_content = HeaderValue::from_maybe_shared(config_bytes).unwrap() } true } diff --git a/examples/http_headers/src/lib.rs b/examples/http_headers/src/lib.rs index 315a7b88..e7aca745 100644 --- a/examples/http_headers/src/lib.rs +++ b/examples/http_headers/src/lib.rs @@ -43,15 +43,23 @@ 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); + for (name, value) in &self.get_http_request_headers_typed() { + info!( + "#{} -> {}: {}", + self.context_id, + name, + value.to_str().unwrap_or("") + ); } - match self.get_http_request_header(":path") { - Some(path) if path == "/hello" => { - self.send_http_response( + match self.get_http_request_header_typed(":path") { + Some(path) if path.as_bytes() == b"/hello" => { + self.send_http_response_typed( 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 @@ -61,8 +69,13 @@ 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); + for (name, value) in &self.get_http_response_headers_typed() { + info!( + "#{} <- {}: {}", + self.context_id, + name, + value.to_str().unwrap_or("") + ); } Action::Continue } diff --git a/src/hostcalls.rs b/src/hostcalls.rs index bfeff0c5..d1371f77 100644 --- a/src/hostcalls.rs +++ b/src/hostcalls.rs @@ -155,6 +155,7 @@ fn proxy_get_header_map_pairs( mocks::proxy_get_header_map_pairs(map_type, return_map_data, return_map_size) } +#[deprecated(since = "0.2.5", note = "use `get_map_typed` instead")] pub fn get_map(map_type: MapType) -> Result, Status> { unsafe { let mut return_data: *mut u8 = null_mut(); @@ -191,6 +192,28 @@ pub fn get_map_bytes(map_type: MapType) -> Result, Status> } } +pub fn get_map_typed(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 = bytes::Bytes::from(Vec::from_raw_parts( + return_data, + return_size, + return_size, + )); + Ok(utils::deserialize_map_typed(serialized_map)) + } else { + Ok(Vec::new()) + } + } + status => panic!("unexpected status: {}", status as u32), + } + } +} + extern "C" { fn proxy_set_header_map_pairs( map_type: MapType, @@ -199,17 +222,15 @@ extern "C" { ) -> Status; } +#[deprecated(since = "0.2.5", note = "use `set_map_typed` instead")] 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), - } - } + set_map_bytes(map_type, map) } -pub fn set_map_bytes(map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), Status> { +pub fn set_map_bytes(map_type: MapType, map: Vec<(&str, V)>) -> Result<(), Status> +where + V: AsRef<[u8]>, +{ let serialized_map = utils::serialize_map_bytes(&map); unsafe { match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) { @@ -219,6 +240,11 @@ pub fn set_map_bytes(map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), S } } +pub fn set_map_typed(map_type: MapType, map: Vec<(&str, &HeaderValue)>) -> Result<(), Status> { + set_map_bytes(map_type, map) +} + +#[cfg(not(all(test, feature = "mockalloc")))] extern "C" { fn proxy_get_header_map_value( map_type: MapType, @@ -229,6 +255,24 @@ extern "C" { ) -> Status; } +#[cfg(all(test, feature = "mockalloc"))] +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 { + mocks::proxy_get_header_map_value( + map_type, + key_data, + key_size, + return_value_data, + return_value_size, + ) +} + +#[deprecated(since = "0.2.5", note = "use `get_map_value_typed` instead")] 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; @@ -288,6 +332,35 @@ pub fn get_map_value_bytes(map_type: MapType, key: &str) -> Result } } +pub fn get_map_value_typed(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() { + // 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. + Ok(Some(HeaderValue::from_maybe_shared_unchecked( + Vec::from_raw_parts(return_data, return_size, return_size), + ))) + } else { + Ok(Some(HeaderValue::from_static(""))) + } + } + Status::NotFound => Ok(None), + status => panic!("unexpected status: {}", status as u32), + } + } +} + extern "C" { fn proxy_remove_header_map_value( map_type: MapType, @@ -315,15 +388,23 @@ extern "C" { ) -> Status; } +#[deprecated(since = "0.2.5", note = "use `set_map_value_typed` instead")] pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Result<(), Status> { + set_map_value_bytes(map_type, key, value) +} + +pub fn set_map_value_bytes(map_type: MapType, key: &str, value: Option) -> 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), @@ -337,30 +418,12 @@ pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Resul } } -pub fn set_map_value_bytes( +pub fn set_map_value_typed( map_type: MapType, key: &str, - value: Option<&[u8]>, + value: Option<&HeaderValue>, ) -> 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_bytes(map_type, key, value) } extern "C" { @@ -373,29 +436,22 @@ extern "C" { ) -> Status; } +#[deprecated(since = "0.2.5", note = "use `add_map_value_typed` instead")] 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), - } - } + add_map_value_bytes(map_type, key, value) } -pub fn add_map_value_bytes(map_type: MapType, key: &str, value: &[u8]) -> Result<(), Status> { +pub fn add_map_value_bytes(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), @@ -403,6 +459,14 @@ pub fn add_map_value_bytes(map_type: MapType, key: &str, value: &[u8]) -> Result } } +pub fn add_map_value_typed( + map_type: MapType, + key: &str, + value: &HeaderValue, +) -> Result<(), Status> { + add_map_value_bytes(map_type, key, value) +} + extern "C" { fn proxy_get_property( path_data: *const u8, @@ -724,12 +788,24 @@ extern "C" { ) -> Status; } +#[deprecated(since = "0.2.5", note = "use `send_http_response_typed` instead")] pub fn send_http_response( status_code: u32, headers: Vec<(&str, &str)>, body: Option<&[u8]>, ) -> Result<(), Status> { - let serialized_headers = utils::serialize_map(&headers); + send_http_response_bytes(status_code, headers, body) +} + +pub fn send_http_response_bytes( + status_code: u32, + headers: Vec<(&str, V)>, + body: Option<&[u8]>, +) -> Result<(), Status> +where + V: AsRef<[u8]>, +{ + let serialized_headers = utils::serialize_map_bytes(&headers); unsafe { match proxy_send_local_response( status_code, @@ -747,6 +823,14 @@ pub fn send_http_response( } } +pub fn send_http_response_typed( + status_code: u32, + headers: Vec<(&str, &HeaderValue)>, + body: Option<&[u8]>, +) -> Result<(), Status> { + send_http_response_bytes(status_code, headers, body) +} + pub fn send_grpc_response( grpc_status: GrpcStatusCode, grpc_status_message: Option<&str>, @@ -785,6 +869,7 @@ extern "C" { ) -> Status; } +#[deprecated(since = "0.2.5", note = "use `dispatch_http_call_typed` instead")] pub fn dispatch_http_call( upstream: &str, headers: Vec<(&str, &str)>, @@ -792,8 +877,21 @@ pub fn dispatch_http_call( trailers: Vec<(&str, &str)>, timeout: Duration, ) -> Result { - let serialized_headers = utils::serialize_map(&headers); - let serialized_trailers = utils::serialize_map(&trailers); + dispatch_http_call_bytes(upstream, headers, body, trailers, timeout) +} + +pub fn dispatch_http_call_bytes( + upstream: &str, + headers: Vec<(&str, V)>, + body: Option<&[u8]>, + trailers: Vec<(&str, V)>, + timeout: Duration, +) -> Result +where + V: AsRef<[u8]>, +{ + let serialized_headers = utils::serialize_map_bytes(&headers); + let serialized_trailers = utils::serialize_map_bytes(&trailers); let mut return_token: u32 = 0; unsafe { match proxy_http_call( @@ -819,6 +917,16 @@ pub fn dispatch_http_call( } } +pub fn dispatch_http_call_typed( + upstream: &str, + headers: Vec<(&str, &HeaderValue)>, + body: Option<&[u8]>, + trailers: Vec<(&str, &HeaderValue)>, + timeout: Duration, +) -> Result { + dispatch_http_call_bytes(upstream, headers, body, trailers, timeout) +} + extern "C" { fn proxy_grpc_call( upstream_data: *const u8, @@ -1180,6 +1288,24 @@ mod mocks { } Status::Ok } + + pub 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 { + let mut value = String::from("foo"); + value.push_str("-prevent-optimizations"); + let layout = Layout::array::(value.len()).unwrap(); + unsafe { + *return_value_data = alloc(layout); + *return_value_size = value.len(); + std::ptr::copy(value.as_ptr(), *return_value_data, value.len()); + } + Status::Ok + } } #[cfg(all(test, feature = "mockalloc"))] @@ -1193,6 +1319,7 @@ mod tests { #[mockalloc::test] fn test_get_map_no_leaks() { + #[allow(deprecated)] let result = super::get_map(MapType::HttpRequestHeaders); assert!(result.is_ok()); } @@ -1202,10 +1329,37 @@ mod tests { let result = super::get_map_bytes(MapType::HttpRequestHeaders); assert!(result.is_ok()); } + + #[mockalloc::test] + fn test_get_map_typed_no_leaks() { + let result = super::get_map_typed(MapType::HttpRequestHeaders); + assert!(result.is_ok()); + } + + #[mockalloc::test] + fn test_get_map_value_no_leaks() { + #[allow(deprecated)] + let result = super::get_map_value(MapType::HttpRequestHeaders, "foo"); + assert!(result.is_ok()); + } + + #[mockalloc::test] + fn test_get_map_value_bytes_no_leaks() { + let result = super::get_map_value_bytes(MapType::HttpRequestHeaders, "foo"); + assert!(result.is_ok()); + } + + #[mockalloc::test] + fn test_get_map_value_typed_no_leaks() { + let result = super::get_map_value_typed(MapType::HttpRequestHeaders, "foo"); + assert!(result.is_ok()); + } } mod utils { use crate::types::Bytes; + use crate::types::HeaderValue; + use bytes::Buf; use std::convert::TryFrom; pub(super) fn serialize_property_path(path: Vec<&str>) -> Bytes { @@ -1225,46 +1379,39 @@ mod utils { bytes } + #[cfg(test)] 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 + serialize_map_bytes(map) } - pub(super) fn serialize_map_bytes(map: &[(&str, &[u8])]) -> Bytes { + pub(super) fn serialize_map_bytes(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); + bytes.extend_from_slice(value.as_ref()); bytes.push(0); } bytes } + #[cfg(test)] + pub(super) fn serialize_map_typed(map: &[(&str, &HeaderValue)]) -> Bytes { + serialize_map_bytes(map) + } + pub(super) fn deserialize_map(bytes: &[u8]) -> Vec<(String, String)> { if bytes.is_empty() { return Vec::new(); @@ -1310,6 +1457,31 @@ mod utils { map } + pub(super) fn deserialize_map_typed(mut bytes: bytes::Bytes) -> Vec<(String, HeaderValue)> { + if bytes.is_empty() { + return Vec::new(); + } + 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) }, + )); + } + map + } + #[cfg(test)] pub(super) mod tests { use super::*; @@ -1354,23 +1526,35 @@ mod utils { 112, 114, 111, 120, 121, 45, 119, 97, 115, 109, 0, ]; + #[rustfmt::skip] + static SERIALIZED_MAP_EMPTY: &[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]); + assert_eq!(serialized_map, SERIALIZED_MAP_EMPTY); } #[test] fn test_serialize_map_empty_bytes() { - let serialized_map = serialize_map_bytes(&[]); - assert_eq!(serialized_map, [0, 0, 0, 0]); + let serialized_map = serialize_map_bytes::<&[u8]>(&[]); + assert_eq!(serialized_map, SERIALIZED_MAP_EMPTY); + } + + #[test] + fn test_serialize_map_empty_typed() { + let serialized_map = serialize_map_typed(&[]); + assert_eq!(serialized_map, SERIALIZED_MAP_EMPTY); } #[test] fn test_deserialize_map_empty() { let map = deserialize_map(&[]); assert_eq!(map, []); - let map = deserialize_map(&[0, 0, 0, 0]); + let map = deserialize_map(SERIALIZED_MAP_EMPTY); assert_eq!(map, []); } @@ -1378,7 +1562,15 @@ 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_MAP_EMPTY); + assert_eq!(map, []); + } + + #[test] + fn test_deserialize_map_empty_typed() { + let map = deserialize_map_typed(bytes::Bytes::new()); + assert_eq!(map, []); + let map = deserialize_map_typed(SERIALIZED_MAP_EMPTY.into()); assert_eq!(map, []); } @@ -1395,6 +1587,17 @@ mod utils { assert_eq!(serialized_map, SERIALIZED_MAP); } + #[test] + fn test_serialize_map_typed() { + let map: Vec<(&str, HeaderValue)> = MAP + .iter() + .map(|x| (x.0, HeaderValue::from_static(x.1))) + .collect(); + let map_refs: Vec<(&str, &HeaderValue)> = map.iter().map(|x| (x.0, &x.1)).collect(); + let serialized_map = serialize_map_typed(&map_refs); + assert_eq!(serialized_map, SERIALIZED_MAP); + } + #[test] fn test_deserialize_map() { let map = deserialize_map(SERIALIZED_MAP); @@ -1415,6 +1618,16 @@ mod utils { } } + #[test] + fn test_deserialize_map_typed() { + let map = deserialize_map_typed(SERIALIZED_MAP.into()); + 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_roundtrip() { let map = deserialize_map(SERIALIZED_MAP); @@ -1435,6 +1648,16 @@ mod utils { assert_eq!(serialized_map, SERIALIZED_MAP); } + #[test] + fn test_deserialize_map_roundtrip_typed() { + let map = deserialize_map_typed(SERIALIZED_MAP.into()); + // TODO(v0.3): fix arguments, so that maps can be reused without conversion. + let map_refs: Vec<(&str, &HeaderValue)> = + map.iter().map(|x| (x.0.as_ref(), &x.1)).collect(); + let serialized_map = serialize_map_typed(&map_refs); + assert_eq!(serialized_map, SERIALIZED_MAP); + } + #[test] fn test_deserialize_map_all_chars() { // 0x00-0x7f are valid single-byte UTF-8 characters. @@ -1450,6 +1673,7 @@ 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]; + // Override panic to silence panic logs in the test output. std::panic::set_hook(Box::new(|_| {})); let result = std::panic::catch_unwind(|| { deserialize_map(&serialized_src); @@ -1472,6 +1696,21 @@ mod utils { } } + #[test] + fn test_deserialize_map_all_chars_typed() { + // 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 = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 99, 0, i, 0]; + let map = deserialize_map_typed(serialized_src.to_vec().into()); + // TODO(v0.3): fix arguments, so that maps can be reused without conversion. + let map_refs: Vec<(&str, &HeaderValue)> = + map.iter().map(|x| (x.0.as_ref(), &x.1)).collect(); + let serialized_map = serialize_map_typed(&map_refs); + assert_eq!(serialized_map, serialized_src); + } + } + #[cfg(nightly)] #[bench] fn bench_serialize_map(b: &mut Bencher) { @@ -1490,6 +1729,19 @@ mod utils { }); } + #[cfg(nightly)] + #[bench] + fn bench_serialize_map_typed(b: &mut Bencher) { + let map: Vec<(&str, HeaderValue)> = MAP + .iter() + .map(|x| (x.0, HeaderValue::from_static(x.1))) + .collect(); + let map_refs: Vec<(&str, &HeaderValue)> = map.iter().map(|x| (x.0, &x.1)).collect(); + b.iter(|| { + serialize_map_typed(test::black_box(&map_refs)); + }); + } + #[cfg(nightly)] #[bench] fn bench_deserialize_map(b: &mut Bencher) { @@ -1507,5 +1759,14 @@ mod utils { deserialize_map_bytes(test::black_box(&serialized_map)); }); } + + #[cfg(nightly)] + #[bench] + fn bench_deserialize_map_typed(b: &mut Bencher) { + let serialized_map: bytes::Bytes = SERIALIZED_MAP.into(); + b.iter(|| { + deserialize_map_typed(test::black_box(serialized_map.clone())); + }); + } } } diff --git a/src/traits.rs b/src/traits.rs index f43663a1..ff0a70ed 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -62,6 +62,7 @@ pub trait Context { hostcalls::enqueue_shared_queue(queue_id, value) } + #[deprecated(since = "0.2.5", note = "use `dispatch_http_call_typed` instead")] fn dispatch_http_call( &self, upstream: &str, @@ -70,9 +71,32 @@ pub trait Context { trailers: Vec<(&str, &str)>, timeout: Duration, ) -> Result { + #[allow(deprecated)] hostcalls::dispatch_http_call(upstream, headers, body, trailers, timeout) } + fn dispatch_http_call_bytes( + &self, + upstream: &str, + headers: Vec<(&str, &[u8])>, + body: Option<&[u8]>, + trailers: Vec<(&str, &[u8])>, + timeout: Duration, + ) -> Result { + hostcalls::dispatch_http_call_bytes(upstream, headers, body, trailers, timeout) + } + + fn dispatch_http_call_typed( + &self, + upstream: &str, + headers: Vec<(&str, &HeaderValue)>, + body: Option<&[u8]>, + trailers: Vec<(&str, &HeaderValue)>, + timeout: Duration, + ) -> Result { + hostcalls::dispatch_http_call_typed(upstream, headers, body, trailers, timeout) + } + fn on_http_call_response( &mut self, _token_id: u32, @@ -82,7 +106,12 @@ pub trait Context { ) { } + #[deprecated( + since = "0.2.5", + note = "use `get_http_call_response_headers_typed` instead" + )] fn get_http_call_response_headers(&self) -> Vec<(String, String)> { + #[allow(deprecated)] hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap() } @@ -90,7 +119,16 @@ pub trait Context { hostcalls::get_map_bytes(MapType::HttpCallResponseHeaders).unwrap() } + fn get_http_call_response_headers_typed(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map_typed(MapType::HttpCallResponseHeaders).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `get_http_call_response_header_typed` instead" + )] fn get_http_call_response_header(&self, name: &str) -> Option { + #[allow(deprecated)] hostcalls::get_map_value(MapType::HttpCallResponseHeaders, name).unwrap() } @@ -98,11 +136,20 @@ pub trait Context { hostcalls::get_map_value_bytes(MapType::HttpCallResponseHeaders, name).unwrap() } + fn get_http_call_response_header_typed(&self, name: &str) -> Option { + hostcalls::get_map_value_typed(MapType::HttpCallResponseTrailers, name).unwrap() + } + fn get_http_call_response_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap() } + #[deprecated( + since = "0.2.5", + note = "use `get_http_call_response_trailers_typed` instead" + )] fn get_http_call_response_trailers(&self) -> Vec<(String, String)> { + #[allow(deprecated)] hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap() } @@ -110,7 +157,16 @@ pub trait Context { hostcalls::get_map_bytes(MapType::HttpCallResponseTrailers).unwrap() } + fn get_http_call_response_trailers_typed(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map_typed(MapType::HttpCallResponseTrailers).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `get_http_call_response_trailer_typed` instead" + )] fn get_http_call_response_trailer(&self, name: &str) -> Option { + #[allow(deprecated)] hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap() } @@ -118,6 +174,10 @@ pub trait Context { hostcalls::get_map_value_bytes(MapType::HttpCallResponseTrailers, name).unwrap() } + fn get_http_call_response_trailer_typed(&self, name: &str) -> Option { + hostcalls::get_map_value_typed(MapType::HttpCallResponseTrailers, name).unwrap() + } + fn dispatch_grpc_call( &self, upstream_name: &str, @@ -317,7 +377,9 @@ pub trait HttpContext: Context { Action::Continue } + #[deprecated(since = "0.2.5", note = "use `get_http_request_headers_typed` instead")] fn get_http_request_headers(&self) -> Vec<(String, String)> { + #[allow(deprecated)] hostcalls::get_map(MapType::HttpRequestHeaders).unwrap() } @@ -325,7 +387,13 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpRequestHeaders).unwrap() } + fn get_http_request_headers_typed(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map_typed(MapType::HttpRequestHeaders).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `set_http_request_headers_typed` instead")] fn set_http_request_headers(&self, headers: Vec<(&str, &str)>) { + #[allow(deprecated)] hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap() } @@ -333,7 +401,13 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpRequestHeaders, headers).unwrap() } + fn set_http_request_headers_typed(&self, headers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map_typed(MapType::HttpRequestHeaders, headers).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `get_http_request_header_typed` instead")] fn get_http_request_header(&self, name: &str) -> Option { + #[allow(deprecated)] hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap() } @@ -341,7 +415,13 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpRequestHeaders, name).unwrap() } + fn get_http_request_header_typed(&self, name: &str) -> Option { + hostcalls::get_map_value_typed(MapType::HttpRequestHeaders, name).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `set_http_request_header_typed` instead")] fn set_http_request_header(&self, name: &str, value: Option<&str>) { + #[allow(deprecated)] hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap() } @@ -349,7 +429,13 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap() } + fn set_http_request_header_typed(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value_typed(MapType::HttpRequestHeaders, name, value).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `add_http_request_header_typed` instead")] fn add_http_request_header(&self, name: &str, value: &str) { + #[allow(deprecated)] hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap() } @@ -357,6 +443,10 @@ pub trait HttpContext: Context { hostcalls::add_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap() } + fn add_http_request_header_typed(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value_typed(MapType::HttpRequestHeaders, name, value).unwrap() + } + fn remove_http_request_header(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpRequestHeaders, name).unwrap() } @@ -377,7 +467,12 @@ pub trait HttpContext: Context { Action::Continue } + #[deprecated( + since = "0.2.5", + note = "use `get_http_request_trailers_typed` instead" + )] fn get_http_request_trailers(&self) -> Vec<(String, String)> { + #[allow(deprecated)] hostcalls::get_map(MapType::HttpRequestTrailers).unwrap() } @@ -385,7 +480,16 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpRequestTrailers).unwrap() } + fn get_http_request_trailers_typed(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map_typed(MapType::HttpRequestTrailers).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `set_http_request_trailers_typed` instead" + )] fn set_http_request_trailers(&self, trailers: Vec<(&str, &str)>) { + #[allow(deprecated)] hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap() } @@ -393,7 +497,13 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpRequestTrailers, trailers).unwrap() } + fn set_http_request_trailers_typed(&self, trailers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map_typed(MapType::HttpRequestTrailers, trailers).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `get_http_request_trailer_typed` instead")] fn get_http_request_trailer(&self, name: &str) -> Option { + #[allow(deprecated)] hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap() } @@ -401,7 +511,13 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpRequestTrailers, name).unwrap() } + fn get_http_request_trailer_typed(&self, name: &str) -> Option { + hostcalls::get_map_value_typed(MapType::HttpRequestTrailers, name).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `set_http_request_trailer_typed` instead")] fn set_http_request_trailer(&self, name: &str, value: Option<&str>) { + #[allow(deprecated)] hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap() } @@ -409,7 +525,13 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap() } + fn set_http_request_trailer_typed(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value_typed(MapType::HttpRequestTrailers, name, value).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `add_http_request_trailer_typed` instead")] fn add_http_request_trailer(&self, name: &str, value: &str) { + #[allow(deprecated)] hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap() } @@ -417,6 +539,10 @@ pub trait HttpContext: Context { hostcalls::add_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap() } + fn add_http_request_trailer_typed(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value_typed(MapType::HttpRequestTrailers, name, value).unwrap() + } + fn remove_http_request_trailer(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpRequestTrailers, name).unwrap() } @@ -433,7 +559,12 @@ pub trait HttpContext: Context { Action::Continue } + #[deprecated( + since = "0.2.5", + note = "use `get_http_response_headers_typed` instead" + )] fn get_http_response_headers(&self) -> Vec<(String, String)> { + #[allow(deprecated)] hostcalls::get_map(MapType::HttpResponseHeaders).unwrap() } @@ -441,7 +572,16 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpResponseHeaders).unwrap() } + fn get_http_response_headers_typed(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map_typed(MapType::HttpResponseHeaders).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `set_http_response_headers_typed` instead" + )] fn set_http_response_headers(&self, headers: Vec<(&str, &str)>) { + #[allow(deprecated)] hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap() } @@ -449,7 +589,13 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpResponseHeaders, headers).unwrap() } + fn set_http_response_headers_typed(&self, headers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map_typed(MapType::HttpResponseHeaders, headers).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `get_http_response_header_typed` instead")] fn get_http_response_header(&self, name: &str) -> Option { + #[allow(deprecated)] hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap() } @@ -457,7 +603,13 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpResponseHeaders, name).unwrap() } + fn get_http_response_header_typed(&self, name: &str) -> Option { + hostcalls::get_map_value_typed(MapType::HttpResponseHeaders, name).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `set_http_response_header_typed` instead")] fn set_http_response_header(&self, name: &str, value: Option<&str>) { + #[allow(deprecated)] hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap() } @@ -465,7 +617,13 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap() } + fn set_http_response_header_typed(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value_typed(MapType::HttpResponseHeaders, name, value).unwrap() + } + + #[deprecated(since = "0.2.5", note = "use `add_http_response_header_typed` instead")] fn add_http_response_header(&self, name: &str, value: &str) { + #[allow(deprecated)] hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap() } @@ -473,6 +631,10 @@ pub trait HttpContext: Context { hostcalls::add_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap() } + fn add_http_response_header_typed(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value_typed(MapType::HttpResponseHeaders, name, value).unwrap() + } + fn remove_http_response_header(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpResponseHeaders, name).unwrap() } @@ -493,7 +655,12 @@ pub trait HttpContext: Context { Action::Continue } + #[deprecated( + since = "0.2.5", + note = "use `get_http_response_trailers_typed` instead" + )] fn get_http_response_trailers(&self) -> Vec<(String, String)> { + #[allow(deprecated)] hostcalls::get_map(MapType::HttpResponseTrailers).unwrap() } @@ -501,7 +668,16 @@ pub trait HttpContext: Context { hostcalls::get_map_bytes(MapType::HttpResponseTrailers).unwrap() } + fn get_http_response_trailers_typed(&self) -> Vec<(String, HeaderValue)> { + hostcalls::get_map_typed(MapType::HttpResponseTrailers).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `set_http_response_trailers_typed` instead" + )] fn set_http_response_trailers(&self, trailers: Vec<(&str, &str)>) { + #[allow(deprecated)] hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap() } @@ -509,7 +685,16 @@ pub trait HttpContext: Context { hostcalls::set_map_bytes(MapType::HttpResponseTrailers, trailers).unwrap() } + fn set_http_response_trailers_typed(&self, trailers: Vec<(&str, &HeaderValue)>) { + hostcalls::set_map_typed(MapType::HttpResponseTrailers, trailers).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `get_http_response_trailer_typed` instead" + )] fn get_http_response_trailer(&self, name: &str) -> Option { + #[allow(deprecated)] hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap() } @@ -517,7 +702,16 @@ pub trait HttpContext: Context { hostcalls::get_map_value_bytes(MapType::HttpResponseTrailers, name).unwrap() } + fn get_http_response_trailer_typed(&self, name: &str) -> Option { + hostcalls::get_map_value_typed(MapType::HttpResponseTrailers, name).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `set_http_response_trailer_typed` instead" + )] fn set_http_response_trailer(&self, name: &str, value: Option<&str>) { + #[allow(deprecated)] hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap() } @@ -525,7 +719,16 @@ pub trait HttpContext: Context { hostcalls::set_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap() } + fn set_http_response_trailer_typed(&self, name: &str, value: Option<&HeaderValue>) { + hostcalls::set_map_value_typed(MapType::HttpResponseTrailers, name, value).unwrap() + } + + #[deprecated( + since = "0.2.5", + note = "use `add_http_response_trailer_typed` instead" + )] fn add_http_response_trailer(&self, name: &str, value: &str) { + #[allow(deprecated)] hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap() } @@ -533,6 +736,10 @@ pub trait HttpContext: Context { hostcalls::add_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap() } + fn add_http_response_trailer_typed(&self, name: &str, value: &HeaderValue) { + hostcalls::add_map_value_typed(MapType::HttpResponseTrailers, name, value).unwrap() + } + fn remove_http_response_trailer(&self, name: &str) { hostcalls::remove_map_value(MapType::HttpResponseTrailers, name).unwrap() } @@ -545,15 +752,35 @@ pub trait HttpContext: Context { hostcalls::reset_http_response().unwrap() } + #[deprecated(since = "0.2.5", note = "use `send_http_response_typed` instead")] fn send_http_response( &self, status_code: u32, headers: Vec<(&str, &str)>, body: Option<&[u8]>, ) { + #[allow(deprecated)] hostcalls::send_http_response(status_code, headers, body).unwrap() } + fn send_http_response_bytes( + &self, + status_code: u32, + headers: Vec<(&str, &[u8])>, + body: Option<&[u8]>, + ) { + hostcalls::send_http_response_bytes(status_code, headers, body).unwrap() + } + + fn send_http_response_typed( + &self, + status_code: u32, + headers: Vec<(&str, &HeaderValue)>, + body: Option<&[u8]>, + ) { + hostcalls::send_http_response_typed(status_code, headers, body).unwrap() + } + fn send_grpc_response( &self, grpc_status: GrpcStatusCode, diff --git a/src/types.rs b/src/types.rs index 4272ba90..91b160b5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -140,3 +140,5 @@ pub enum GrpcStatusCode { } pub type Bytes = Vec; + +pub use http::HeaderValue;