diff --git a/Cargo.toml b/Cargo.toml index 90bbe533..6a626dc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" [dependencies] log = "0.4" +regex = "1.9" url = "2.4" # Mandatory for cargo-raze diff --git a/README.md b/README.md index 49e26092..0fdc10e2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ unit test that verifies both. Build all plugins and run all plugin tests: -`$ bazelisk test --test_output=all --define engine=v8 samples/...` +`$ bazelisk test --test_output=all //samples/...` # Samples & Recipes @@ -29,6 +29,9 @@ plugin. Extend them to fit your particular use case. * [Log the value of a query parameter](samples/query_log): Emit a custom variable to Cloud Logging. Demonstrate a standard way to parse query string values from the request. +* [Rewrite the path using regex](samples/regex_rewrite): Remove a piece of the + URI using regex replacement. Demonstrate a standard way to use regular + expressions, compiling them at plugin initialization. # Feature set / ABI diff --git a/WORKSPACE b/WORKSPACE index 6b646acd..7c463a2c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -46,13 +46,20 @@ http_archive( load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps") boost_deps() -# Upgrade googletest to v1.13.0 to support bazel 6.0.0 -# TODO merge upstream to proxy-wasm-cpp-host/bazel/repositories.bzl +# Include Google RE2. http_archive( - name = "com_google_googletest", - sha256 = "ad7fdba11ea011c1d925b3289cf4af2c66a352e18d4c7264392fead75e919363", - strip_prefix = "googletest-1.13.0", - url = "https://github.com/google/googletest/archive/v1.13.0.tar.gz", + name = "com_google_re2", + sha256 = "18cf85922e27fad3ed9c96a27733037da445f35eb1a2744c306a37c6d11e95c4", + strip_prefix = "re2-2023-07-01", + url = "https://github.com/google/re2/archive/2023-07-01.tar.gz", +) + +# Upgrade Abseil to work with latest Emscripten and STANDALONE_WASM +http_archive( + name = "com_google_absl", # 2023-04-06T14:42:25Z + sha256 = "a50452f02402262f9a61a8eedda60f76dda6b9538d36b34b55bce9f74a4d5ef8", + strip_prefix = "abseil-cpp-e73b9139ee9b853a4bd7812531442c138da09084", + urls = ["https://github.com/abseil/abseil-cpp/archive/e73b9139ee9b853a4bd7812531442c138da09084.zip"], ) # Duplicate ProxyWasm WORKSPACE files (dependencies) diff --git a/cargo/BUILD.bazel b/cargo/BUILD.bazel index f88c6301..b1d3148a 100644 --- a/cargo/BUILD.bazel +++ b/cargo/BUILD.bazel @@ -21,6 +21,15 @@ alias( ], ) +alias( + name = "regex", + actual = "@raze__regex__1_9_1//:regex", + tags = [ + "cargo-raze", + "manual", + ], +) + alias( name = "url", actual = "@raze__url__2_4_0//:url", diff --git a/cargo/Cargo.raze.lock b/cargo/Cargo.raze.lock index 2eb933c3..7c298078 100644 --- a/cargo/Cargo.raze.lock +++ b/cargo/Cargo.raze.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -25,17 +34,53 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + [[package]] name = "service-extensions-samples" version = "0.0.0" dependencies = [ "log", + "regex", "url", ] diff --git a/cargo/crates.bzl b/cargo/crates.bzl index cf3eee4d..54994bac 100644 --- a/cargo/crates.bzl +++ b/cargo/crates.bzl @@ -11,6 +11,16 @@ load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") # buildifier: di def raze_fetch_remote_crates(): """This function defines a collection of repos and should be called in a WORKSPACE file""" + maybe( + http_archive, + name = "raze__aho_corasick__1_0_2", + url = "https://crates.io/api/v1/crates/aho-corasick/1.0.2/download", + type = "tar.gz", + sha256 = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41", + strip_prefix = "aho-corasick-1.0.2", + build_file = Label("//cargo/remote:BUILD.aho-corasick-1.0.2.bazel"), + ) + maybe( http_archive, name = "raze__form_urlencoded__1_2_0", @@ -41,6 +51,16 @@ def raze_fetch_remote_crates(): build_file = Label("//cargo/remote:BUILD.log-0.4.19.bazel"), ) + maybe( + http_archive, + name = "raze__memchr__2_5_0", + url = "https://crates.io/api/v1/crates/memchr/2.5.0/download", + type = "tar.gz", + sha256 = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d", + strip_prefix = "memchr-2.5.0", + build_file = Label("//cargo/remote:BUILD.memchr-2.5.0.bazel"), + ) + maybe( http_archive, name = "raze__percent_encoding__2_3_0", @@ -51,6 +71,36 @@ def raze_fetch_remote_crates(): build_file = Label("//cargo/remote:BUILD.percent-encoding-2.3.0.bazel"), ) + maybe( + http_archive, + name = "raze__regex__1_9_1", + url = "https://crates.io/api/v1/crates/regex/1.9.1/download", + type = "tar.gz", + sha256 = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575", + strip_prefix = "regex-1.9.1", + build_file = Label("//cargo/remote:BUILD.regex-1.9.1.bazel"), + ) + + maybe( + http_archive, + name = "raze__regex_automata__0_3_3", + url = "https://crates.io/api/v1/crates/regex-automata/0.3.3/download", + type = "tar.gz", + sha256 = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310", + strip_prefix = "regex-automata-0.3.3", + build_file = Label("//cargo/remote:BUILD.regex-automata-0.3.3.bazel"), + ) + + maybe( + http_archive, + name = "raze__regex_syntax__0_7_4", + url = "https://crates.io/api/v1/crates/regex-syntax/0.7.4/download", + type = "tar.gz", + sha256 = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2", + strip_prefix = "regex-syntax-0.7.4", + build_file = Label("//cargo/remote:BUILD.regex-syntax-0.7.4.bazel"), + ) + maybe( http_archive, name = "raze__tinyvec__1_6_0", diff --git a/cargo/remote/BUILD.aho-corasick-1.0.2.bazel b/cargo/remote/BUILD.aho-corasick-1.0.2.bazel new file mode 100644 index 00000000..4f59633a --- /dev/null +++ b/cargo/remote/BUILD.aho-corasick-1.0.2.bazel @@ -0,0 +1,58 @@ +""" +@generated +cargo-raze crate build file. + +DO NOT EDIT! Replaced on runs of cargo-raze +""" + +# buildifier: disable=load +load("@bazel_skylib//lib:selects.bzl", "selects") + +# buildifier: disable=load +load( + "@rules_rust//rust:defs.bzl", + "rust_binary", + "rust_library", + "rust_proc_macro", + "rust_test", +) + +package(default_visibility = [ + # Public for visibility by "@raze__crate__version//" targets. + # + # Prefer access through "//cargo", which limits external + # visibility to explicit Cargo.toml dependencies. + "//visibility:public", +]) + +licenses([ + "unencumbered", # Unlicense from expression "Unlicense OR MIT" +]) + +# Generated Targets + +rust_library( + name = "aho_corasick", + srcs = glob(["**/*.rs"]), + crate_features = [ + "default", + "perf-literal", + "std", + ], + crate_root = "src/lib.rs", + data = [], + edition = "2021", + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-raze", + "crate-name=aho_corasick", + "manual", + ], + version = "1.0.2", + # buildifier: leave-alone + deps = [ + "@raze__memchr__2_5_0//:memchr", + ], +) diff --git a/cargo/remote/BUILD.memchr-2.5.0.bazel b/cargo/remote/BUILD.memchr-2.5.0.bazel new file mode 100644 index 00000000..5cab9e6f --- /dev/null +++ b/cargo/remote/BUILD.memchr-2.5.0.bazel @@ -0,0 +1,88 @@ +""" +@generated +cargo-raze crate build file. + +DO NOT EDIT! Replaced on runs of cargo-raze +""" + +# buildifier: disable=load +load("@bazel_skylib//lib:selects.bzl", "selects") + +# buildifier: disable=load +load( + "@rules_rust//rust:defs.bzl", + "rust_binary", + "rust_library", + "rust_proc_macro", + "rust_test", +) + +package(default_visibility = [ + # Public for visibility by "@raze__crate__version//" targets. + # + # Prefer access through "//cargo", which limits external + # visibility to explicit Cargo.toml dependencies. + "//visibility:public", +]) + +licenses([ + "unencumbered", # Unlicense from expression "Unlicense OR MIT" +]) + +# Generated Targets +# buildifier: disable=out-of-order-load +# buildifier: disable=load-on-top +load( + "@rules_rust//cargo:cargo_build_script.bzl", + "cargo_build_script", +) + +cargo_build_script( + name = "memchr_build_script", + srcs = glob(["**/*.rs"]), + build_script_env = { + }, + crate_features = [ + "default", + "std", + ], + crate_root = "build.rs", + data = glob(["**"]), + edition = "2018", + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-raze", + "manual", + ], + version = "2.5.0", + visibility = ["//visibility:private"], + deps = [ + ], +) + +rust_library( + name = "memchr", + srcs = glob(["**/*.rs"]), + crate_features = [ + "default", + "std", + ], + crate_root = "src/lib.rs", + data = [], + edition = "2018", + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-raze", + "crate-name=memchr", + "manual", + ], + version = "2.5.0", + # buildifier: leave-alone + deps = [ + ":memchr_build_script", + ], +) diff --git a/cargo/remote/BUILD.regex-1.9.1.bazel b/cargo/remote/BUILD.regex-1.9.1.bazel new file mode 100644 index 00000000..93ba46bb --- /dev/null +++ b/cargo/remote/BUILD.regex-1.9.1.bazel @@ -0,0 +1,77 @@ +""" +@generated +cargo-raze crate build file. + +DO NOT EDIT! Replaced on runs of cargo-raze +""" + +# buildifier: disable=load +load("@bazel_skylib//lib:selects.bzl", "selects") + +# buildifier: disable=load +load( + "@rules_rust//rust:defs.bzl", + "rust_binary", + "rust_library", + "rust_proc_macro", + "rust_test", +) + +package(default_visibility = [ + # Public for visibility by "@raze__crate__version//" targets. + # + # Prefer access through "//cargo", which limits external + # visibility to explicit Cargo.toml dependencies. + "//visibility:public", +]) + +licenses([ + "notice", # MIT from expression "MIT OR Apache-2.0" +]) + +# Generated Targets + +rust_library( + name = "regex", + srcs = glob(["**/*.rs"]), + crate_features = [ + "default", + "perf", + "perf-backtrack", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "perf-onepass", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + ], + crate_root = "src/lib.rs", + data = [], + edition = "2021", + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-raze", + "crate-name=regex", + "manual", + ], + version = "1.9.1", + # buildifier: leave-alone + deps = [ + "@raze__aho_corasick__1_0_2//:aho_corasick", + "@raze__memchr__2_5_0//:memchr", + "@raze__regex_automata__0_3_3//:regex_automata", + "@raze__regex_syntax__0_7_4//:regex_syntax", + ], +) + +# Unsupported target "integration" with type "test" omitted diff --git a/cargo/remote/BUILD.regex-automata-0.3.3.bazel b/cargo/remote/BUILD.regex-automata-0.3.3.bazel new file mode 100644 index 00000000..bf326bf2 --- /dev/null +++ b/cargo/remote/BUILD.regex-automata-0.3.3.bazel @@ -0,0 +1,81 @@ +""" +@generated +cargo-raze crate build file. + +DO NOT EDIT! Replaced on runs of cargo-raze +""" + +# buildifier: disable=load +load("@bazel_skylib//lib:selects.bzl", "selects") + +# buildifier: disable=load +load( + "@rules_rust//rust:defs.bzl", + "rust_binary", + "rust_library", + "rust_proc_macro", + "rust_test", +) + +package(default_visibility = [ + # Public for visibility by "@raze__crate__version//" targets. + # + # Prefer access through "//cargo", which limits external + # visibility to explicit Cargo.toml dependencies. + "//visibility:public", +]) + +licenses([ + "notice", # MIT from expression "MIT OR Apache-2.0" +]) + +# Generated Targets + +rust_library( + name = "regex_automata", + srcs = glob(["**/*.rs"]), + crate_features = [ + "alloc", + "dfa-onepass", + "hybrid", + "meta", + "nfa-backtrack", + "nfa-pikevm", + "nfa-thompson", + "perf-inline", + "perf-literal", + "perf-literal-multisubstring", + "perf-literal-substring", + "std", + "syntax", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "unicode-word-boundary", + ], + crate_root = "src/lib.rs", + data = [], + edition = "2021", + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-raze", + "crate-name=regex-automata", + "manual", + ], + version = "0.3.3", + # buildifier: leave-alone + deps = [ + "@raze__aho_corasick__1_0_2//:aho_corasick", + "@raze__memchr__2_5_0//:memchr", + "@raze__regex_syntax__0_7_4//:regex_syntax", + ], +) + +# Unsupported target "integration" with type "test" omitted diff --git a/cargo/remote/BUILD.regex-syntax-0.7.4.bazel b/cargo/remote/BUILD.regex-syntax-0.7.4.bazel new file mode 100644 index 00000000..f6db31c3 --- /dev/null +++ b/cargo/remote/BUILD.regex-syntax-0.7.4.bazel @@ -0,0 +1,66 @@ +""" +@generated +cargo-raze crate build file. + +DO NOT EDIT! Replaced on runs of cargo-raze +""" + +# buildifier: disable=load +load("@bazel_skylib//lib:selects.bzl", "selects") + +# buildifier: disable=load +load( + "@rules_rust//rust:defs.bzl", + "rust_binary", + "rust_library", + "rust_proc_macro", + "rust_test", +) + +package(default_visibility = [ + # Public for visibility by "@raze__crate__version//" targets. + # + # Prefer access through "//cargo", which limits external + # visibility to explicit Cargo.toml dependencies. + "//visibility:public", +]) + +licenses([ + "notice", # MIT from expression "MIT OR Apache-2.0" +]) + +# Generated Targets + +# Unsupported target "bench" with type "bench" omitted + +rust_library( + name = "regex_syntax", + srcs = glob(["**/*.rs"]), + crate_features = [ + "default", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + ], + crate_root = "src/lib.rs", + data = [], + edition = "2021", + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-raze", + "crate-name=regex-syntax", + "manual", + ], + version = "0.7.4", + # buildifier: leave-alone + deps = [ + ], +) diff --git a/samples/regex_rewrite/BUILD b/samples/regex_rewrite/BUILD new file mode 100644 index 00000000..222338aa --- /dev/null +++ b/samples/regex_rewrite/BUILD @@ -0,0 +1,43 @@ +load("//:plugins.bzl", "proxy_wasm_plugin_cpp", "proxy_wasm_plugin_rust") + +licenses(["notice"]) # Apache 2 + +proxy_wasm_plugin_rust( + name = "plugin_rust.wasm", + srcs = ["plugin.rs"], + deps = [ + "//cargo:regex", + "@proxy_wasm_rust_sdk//:proxy_wasm", + "@proxy_wasm_rust_sdk//bazel/cargo:log", + ], +) + +# Blocked on emscripten update to fix Abseil/RE2 compatibility: +# https://github.com/proxy-wasm/proxy-wasm-cpp-sdk/pull/157 +# NOTE: Plugins can be built using that PR -- it's blocked on Envoy. +#proxy_wasm_plugin_cpp( +# name = "plugin_cpp.wasm", +# srcs = ["plugin.cc"], +# deps = [ +# "@com_google_re2//:re2", +# "@proxy_wasm_cpp_sdk//contrib:contrib_lib", +# ], +#) + +cc_test( + name = "plugin_test", + srcs = ["test.cc"], + data = [ + #":plugin_cpp.wasm", + ":plugin_rust.wasm", + ], + deps = [ + "//test:framework", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_googletest//:gtest_main", + "@proxy_wasm_cpp_host//:base_lib", + "@proxy_wasm_cpp_host//:v8_lib", + "@proxy_wasm_cpp_host//test:utility_lib", + ], +) diff --git a/samples/regex_rewrite/plugin.cc b/samples/regex_rewrite/plugin.cc new file mode 100644 index 00000000..66c5514d --- /dev/null +++ b/samples/regex_rewrite/plugin.cc @@ -0,0 +1,56 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START serviceextensions_regex_rewrite] +#include "proxy_wasm_intrinsics.h" +#include "re2/re2.h" + +class MyRootContext : public RootContext { + public: + explicit MyRootContext(uint32_t id, std::string_view root_id) + : RootContext(id, root_id) {} + + bool onConfigure(size_t) override { + // Compile the regex expression at plugin setup time. + path_match.emplace("/foo-([^/]+)/"); + return path_match->ok(); + } + + std::optional path_match; +}; + +class MyHttpContext : public Context { + public: + explicit MyHttpContext(uint32_t id, RootContext* root) + : Context(id, root), root_(static_cast(root)) {} + + FilterHeadersStatus onRequestHeaders(uint32_t headers, + bool end_of_stream) override { + auto path = getRequestHeader(":path"); + if (path) { + std::string edit = path->toString(); // mutable copy + if (re2::RE2::Replace(&edit, *root_->path_match, "/\\1/")) { + replaceRequestHeader(":path", edit); + } + } + return FilterHeadersStatus::Continue; + } + + private: + const MyRootContext* root_; +}; + +static RegisterContextFactory register_StaticContext( + CONTEXT_FACTORY(MyHttpContext), ROOT_FACTORY(MyRootContext)); +// [END serviceextensions_regex_rewrite] diff --git a/samples/regex_rewrite/plugin.rs b/samples/regex_rewrite/plugin.rs new file mode 100644 index 00000000..1bd43db0 --- /dev/null +++ b/samples/regex_rewrite/plugin.rs @@ -0,0 +1,68 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START serviceextensions_regex_rewrite] +use proxy_wasm::traits::*; +use proxy_wasm::types::*; +use regex::Regex; +use std::rc::Rc; + +proxy_wasm::main! {{ + proxy_wasm::set_log_level(LogLevel::Trace); + proxy_wasm::set_root_context(|_| -> Box { + Box::new(MyRootContext { path_match: Rc::new(None) }) + }); +}} + +struct MyRootContext { + path_match: Rc>, +} + +impl Context for MyRootContext {} + +impl RootContext for MyRootContext { + fn on_configure(&mut self, _: usize) -> bool { + self.path_match = Rc::new(Some(Regex::new(r"/foo-([^/]+)/").unwrap())); + return true; + } + + fn create_http_context(&self, _: u32) -> Option> { + Some(Box::new(MyHttpContext { + path_match: self.path_match.clone(), // shallow copy, ref count only + })) + } + fn get_type(&self) -> Option { + Some(ContextType::HttpContext) + } +} + +struct MyHttpContext { + path_match: Rc>, +} + +impl Context for MyHttpContext {} + +impl HttpContext for MyHttpContext { + fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { + if let Some(path) = self.get_http_request_header(":path") { + let re: &Regex = self.path_match.as_ref().as_ref().unwrap(); + let edit = re.replace(&path, "/$1/"); + if path.len() != edit.len() { + self.set_http_request_header(":path", Some(&edit)); + } + } + return Action::Continue; + } +} +// [END serviceextensions_regex_rewrite] diff --git a/samples/regex_rewrite/test.cc b/samples/regex_rewrite/test.cc new file mode 100644 index 00000000..86e18c4d --- /dev/null +++ b/samples/regex_rewrite/test.cc @@ -0,0 +1,66 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "include/proxy-wasm/exports.h" +#include "include/proxy-wasm/wasm.h" +#include "test/framework.h" + +using ::testing::ElementsAre; +using ::testing::Pair; + +namespace service_extensions_samples { + +INSTANTIATE_TEST_SUITE_P( + EnginesAndPlugins, HttpTest, + ::testing::Combine(::testing::ValuesIn(proxy_wasm::getWasmEngines()), + ::testing::Values( + //"samples/regex_rewrite/plugin_cpp.wasm", + "samples/regex_rewrite/plugin_rust.wasm"))); + +TEST_P(HttpTest, NoMatch) { + // Create VM and load the plugin. + ASSERT_TRUE(CreatePlugin(engine(), path()).ok()); + + // Create stream context. + auto http_context = TestHttpContext(handle_); + + // Expect no matches, so no changes. + auto res = + http_context.SendRequestHeaders({{":path", "/one/two?three=four"}}); + EXPECT_EQ(res.http_code, 0); + EXPECT_THAT(res.headers, ElementsAre(Pair(":path", "/one/two?three=four"))); + + EXPECT_FALSE(handle_->wasm()->isFailed()); +} + +TEST_P(HttpTest, MatchAndReplace) { + // Create VM and load the plugin. + ASSERT_TRUE(CreatePlugin(engine(), path()).ok()); + + // Create stream context. + auto http_context = TestHttpContext(handle_); + + // Expect the plugin to replace the first "foo-" path fragment. + auto res = http_context.SendRequestHeaders( + {{":path", "/pre/foo-one/foo-two/post?a=b"}}); + EXPECT_EQ(res.http_code, 0); + EXPECT_THAT(res.headers, + ElementsAre(Pair(":path", "/pre/one/foo-two/post?a=b"))); + + EXPECT_FALSE(handle_->wasm()->isFailed()); +} + +} // namespace service_extensions_samples