diff --git a/Cargo.lock b/Cargo.lock index 86fef956d4..03b2b36a75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -682,6 +682,18 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -2337,6 +2349,7 @@ dependencies = [ "divan", "either", "enum-map", + "enum_dispatch", "eyre", "fixedstr", "form_urlencoded", diff --git a/Cargo.toml b/Cargo.toml index 8f2fd94cf0..9a9e2eb4d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,6 +149,7 @@ strum_macros = "0.26" cfg-if = "1.0.0" libflate = "2.0.0" form_urlencoded = "1.2.1" +enum_dispatch = "0.3.13" [dependencies.hyper-util] version = "0.1" @@ -236,4 +237,4 @@ schemars = { version = "0.8.15", features = ["bytes", "url"] } url = { version = "2.4.1", features = ["serde"] } [workspace.lints.clippy] -undocumented_unsafe_blocks = "deny" \ No newline at end of file +undocumented_unsafe_blocks = "deny" diff --git a/build/Makefile b/build/Makefile index cfa805c860..c342e99642 100644 --- a/build/Makefile +++ b/build/Makefile @@ -77,7 +77,7 @@ version: @echo $(package_version) # Run all tests -test: ensure-build-image test-quilkin test-examples test-docs +test: ensure-build-image test-quilkin test-docs # In CI with split jobs that both fetch they will fail if run in parallel since # cargo will be fighting with itself for some the same host directory that is @@ -103,13 +103,6 @@ test-quilkin: ensure-build-image --network=host \ -e RUST_BACKTRACE=1 --entrypoint=cargo $(BUILD_IMAGE_TAG) test -p quilkin -p qt -# Run tests against the examples -test-examples: ensure-build-image - docker run --rm $(common_rust_args) -w /workspace/examples/quilkin-filter-example \ - --entrypoint=cargo $(BUILD_IMAGE_TAG) clippy --tests -- -D warnings - docker run --rm $(common_rust_args) -w /workspace/examples/quilkin-filter-example \ - --entrypoint=cargo $(BUILD_IMAGE_TAG) fmt -- --check - # Run tests against documentation test-docs: ensure-build-image test-docs: GITHUB_REF_NAME ?= main diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index d44bc222ed..700af1e247 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -26,7 +26,6 @@ - [Pass](./services/proxy/filters/pass.md) - [Timestamp](./services/proxy/filters/timestamp.md) - [Token Router](./services/proxy/filters/token_router.md) - - [Writing Custom Filters](./services/proxy/filters/writing_custom_filters.md) - [Control Message Protocol](./services/proxy/qcmp.md) - [Metrics](./services/proxy/metrics.md) @@ -55,4 +54,4 @@ # Third Party -- [Videos and Presentations](./third-party/presentations.md) \ No newline at end of file +- [Videos and Presentations](./third-party/presentations.md) diff --git a/docs/src/services/proxy/filters/writing_custom_filters.md b/docs/src/services/proxy/filters/writing_custom_filters.md deleted file mode 100644 index 5f6369476a..0000000000 --- a/docs/src/services/proxy/filters/writing_custom_filters.md +++ /dev/null @@ -1,277 +0,0 @@ -# Writing Custom Filters - -> The full source code used in this example can be found - in [`examples/`][example]. - -Quilkin provides an extensible implementation of [Filters] that allows us to -plug in custom implementations to fit our needs. This document provides an -overview of the API and how we can go about writing our own [Filters]. First -we need to create a type and implement two traits for it. - -It's not terribly important what the filter in this example does so let's write -a `Greet` filter that appends `Hello` to every packet in one direction and -`Goodbye` to packets in the opposite direction. - -```rust,no_run,noplayground -struct Greet; -``` - -> As a convention within Quilkin: Filter names are singular, they also tend to -> be a verb, rather than an adjective. -> -> **Examples** -> -> - **Greet** not "Greets" -> - **Compress** not "Compressor". - -## `Filter` - -Represents the actual [Filter][built-in-filters] instance in the pipeline. An -implementation provides a `read` and a `write` method (both are passthrough -by default) that accepts a context object and returns a response. - -Both methods are invoked by the proxy when it consults the [filter chain] -`read` is invoked when a packet is received on the local downstream port and -is to be sent to an upstream endpoint while `write` is invoked in the opposite -direction when a packet is received from an upstream endpoint and is to be -sent to a downstream client. - -```rust,no_run,noplayground -# struct Greet; -use quilkin::filters::prelude::*; - -/// Appends data to each packet -impl Filter for Greet { - fn read(&self, ctx: &mut ReadContext) -> Result<(), FilterError> { - ctx.contents.extend_from_slice(b"Hello"); - Ok(()) - } - fn write(&self, ctx: &mut WriteContext) -> Result<(), FilterError> { - ctx.contents.extend_from_slice(b"Goodbye"); - Ok(()) - } -} -``` - -## `StaticFilter` - -Represents metadata needed for your [`Filter`], most of it has to with defining -configuration, for now we can use `()` as we have no configuration currently. - -```rust,no_run,noplayground -# use quilkin::filters::prelude::*; -# struct Greet; -# impl Filter for Greet {} -impl StaticFilter for Greet { - const NAME: &'static str = "greet.v1"; - type Configuration = (); - type BinaryConfiguration = (); - - fn try_from_config(config: Option) -> Result { - Ok(Self) - } -} -``` - -## Running - -We can run the proxy using `Proxy::run` function. Let's -add a main function that does that. Quilkin relies on the [Tokio] async -runtime, so we need to import that crate and wrap our main function with it. - -We can also register custom filters in quilkin using [`FilterRegistry::register`][FilterRegistry::register] - -Add Tokio as a dependency in `Cargo.toml`. - -```toml -[dependencies] -quilkin = "0.2.0" -tokio = { version = "1", features = ["full"]} -``` - -Add a main function that starts the proxy. - -```rust,no_run,noplayground,ignore -// src/main.rs -{{#include ../../../../../examples/quilkin-filter-example/src/main.rs:run}} -``` - -Now, let's try out the proxy. The following configuration starts our extended -version of the proxy at port 7777 and forwards all packets to an upstream server -at port 4321. - -```yaml -# quilkin.yaml -version: v1alpha1 -filters: - - name: greet.v1 -clusters: - - endpoints: - - address: 127.0.0.1:4321 -``` - -Next we to setup our network of services, for this example we're going to use -the `netcat` tool to spawn a UDP echo server and interactive client for us to -send packets over the wire. - -```bash -# Start the proxy -cargo run -- & -# Start a UDP listening server on the configured port -nc -lu 127.0.0.1 4321 & -# Start an interactive UDP client that sends packet to the proxy -nc -u 127.0.0.1 7777 -``` - -Whatever we pass to the client should now show up with our modification on the -listening server's standard output. For example typing `Quilkin` in the client -prints `Hello Quilkin` on the server. - -## Configuration - -Let's extend the `Greet` filter to have a configuration that contains what -greeting to use. - -The [Serde] crate is used to describe static YAML configuration in code while -[Tonic]/[Prost] is used to describe dynamic configuration as [Protobuf] messages -when talking to a [management server]. - -### YAML Configuration - -First let's create the type for our configuration: - -1. Add the yaml parsing crates to `Cargo.toml`: - -```toml -# [dependencies] -serde = "1.0" -serde_yaml = "0.8" -``` - -2. Define a struct representing the config: - -```rust,no_run,noplayground,ignore -// src/main.rs -{{#include ../../../../../examples/quilkin-filter-example/src/main.rs:serde_config}} -``` - -3. Update the `Greet` Filter to take in `greeting` as a parameter: - -```rust,no_run,noplayground,ignore -// src/main.rs -{{#include ../../../../../examples/quilkin-filter-example/src/main.rs:filter}} -``` - -### Protobuf Configuration - -Quilkin comes with out-of-the-box support for xDS management, and as such needs -to communicate filter configuration over [Protobuf] with management servers and -clients to synchronise state across the network. So let's add the binary version -of our `Greet` configuration. - -1. Add the proto parsing crates to `Cargo.toml`: - -```toml -[dependencies] -# ... -tonic = "0.5.0" -prost = "0.7" -prost-types = "0.7" -``` - -2. Create a [Protobuf] equivalent of our YAML configuration. - -```plaintext,no_run,noplayground,ignore -// src/greet.proto -{{#include ../../../../../examples/quilkin-filter-example/src/greet.proto:proto}} -``` - -3. Generate Rust code from the proto file: - -### Generated - Recommended - -Use something like [proto-gen](https://github.com/EmbarkStudios/proto-gen) to generate Rust code for the protobuf. - -At that point it is just normal rust code and can be included from where you placed the generated code, eg. `generated`. - -```rust,no_run,noplayground,ignore -mod generated; -use generated::greet as proto; -``` - -### At build time - -There are a few ways to generate [Prost] code from proto, we will use the [prost_build] crate in this example. - -Add the following required crates to `Cargo.toml`, and then add a -[build script][build-script] to generate the following Rust code -during compilation: - -```toml -# [dependencies] -bytes = "1.0" - -# [build-dependencies] -prost-build = "0.7" -``` - -```rust,no_run,noplayground,ignore -// src/build.rs -fn main() { - // Remove if you already have `protoc` installed in your system. - std::env::set_var("PROTOC", protobuf_src::protoc()); - - prost_build::compile_protos(&["src/greet.proto"], &["src/"]).unwrap(); -} -``` - -To include the generated code, we'll use [`tonic::include_proto`]. - -```rust,no_run,noplayground,ignore -// src/main.rs -#[allow(warnings, clippy::all)] -// ANCHOR: include_proto -mod proto { - tonic::include_proto!("greet"); -} -``` - -4. Then we just need to implement [std::convert::TryFrom] for converting the protobuf message to -equivalent configuration. - -```rust,no_run,noplayground,ignore -// src/main.rs -{{#include ../../../../../examples/quilkin-filter-example/src/main.rs:TryFrom}} -``` - -Now, let's update `Greet`'s `StaticFilter` implementation to use the two -configurations. - -```rust,no_run,noplayground,ignore -// src/main.rs -{{#include ../../../../../examples/quilkin-filter-example/src/main.rs:factory}} -``` - -That's it! With these changes we have wired up static configuration for our -filter. Try it out with the following configuration: - -```yaml -# quilkin.yaml -{{#include ../../../../../examples/quilkin-filter-example/config.yaml:yaml}} -``` - -[FilterRegistry::register]: ../../../../api/quilkin/filters/struct.FilterRegistry.html#method.register -[std::convert::TryFrom]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html - -[Filters]: ../filters.md -[filter chain]: ../filters.md#filters-and-filter-chain -[built-in-filters]: ../filters.md#built-in-filters -[management server]: ../../xds.md -[tokio]: https://docs.rs/tokio -[tonic]: https://docs.rs/tonic -[prost]: https://docs.rs/prost -[Protobuf]: https://protobuf.dev -[Serde]: https://docs.serde.rs/serde_yaml/index.html -[prost_build]: https://docs.rs/prost-build/0.7.0/prost_build/ -[build-script]: https://doc.rust-lang.org/cargo/reference/build-scripts.html -[example]: https://github.com/googleforgames/quilkin/tree/{{GITHUB_REF_NAME}}/examples/quilkin-filter-example diff --git a/examples/quilkin-filter-example/.gitignore b/examples/quilkin-filter-example/.gitignore deleted file mode 100644 index da48e4ceb1..0000000000 --- a/examples/quilkin-filter-example/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright 2021 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. -# - -.* -!.gitignore -!.gcloudignore -!.dockerignore - -*.iml - -### Rust template -# Generated by Cargo -# will have compiled files and executables -/target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk diff --git a/examples/quilkin-filter-example/Cargo.toml b/examples/quilkin-filter-example/Cargo.toml deleted file mode 100644 index a8b66cafd0..0000000000 --- a/examples/quilkin-filter-example/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -# -# Copyright 2021 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. -# - -[workspace] - -[package] -name = "quilkin-filter-example" -version = "0.1.0" -homepage = "https://github.com/googleforgames/quilkin" -repository = "https://github.com/googleforgames/quilkin" -edition = "2021" - -[dependencies] -# If lifting this example, you will want to be explicit about the Quilkin version, e.g. -# quilkin = "0.2.0" -quilkin = { path = "../../" } -tokio = { version = "1.32", features = ["full"] } -tonic = "0.10" -prost = "0.12" -prost-types = "0.12" -serde = "1" -serde_yaml = "0.9" -bytes = "1" -schemars = "0.8" -async-trait = "0.1" diff --git a/examples/quilkin-filter-example/README.md b/examples/quilkin-filter-example/README.md deleted file mode 100644 index b4a15cb622..0000000000 --- a/examples/quilkin-filter-example/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Quilkin filter example - -This is the code example for the "Writing Custom Filters" Guide, linked via the homepage. diff --git a/examples/quilkin-filter-example/config.yaml b/examples/quilkin-filter-example/config.yaml deleted file mode 100644 index a4df9ed39b..0000000000 --- a/examples/quilkin-filter-example/config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright 2021 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. -# - -# ANCHOR: yaml -version: v1alpha1 -port: 7001 -filters: -- name: greet.v1 - config: - greeting: Hey -endpoints: -- address: 127.0.0.1:4321 -# ANCHOR_END: yaml diff --git a/examples/quilkin-filter-example/src/generated.rs b/examples/quilkin-filter-example/src/generated.rs deleted file mode 100644 index 5208bb720a..0000000000 --- a/examples/quilkin-filter-example/src/generated.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#![allow(clippy::doc_markdown, clippy::use_self)] -pub mod greet; diff --git a/examples/quilkin-filter-example/src/generated/greet.rs b/examples/quilkin-filter-example/src/generated/greet.rs deleted file mode 100644 index 98baf274ce..0000000000 --- a/examples/quilkin-filter-example/src/generated/greet.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Greet { - #[prost(string, tag = "1")] - pub greeting: ::prost::alloc::string::String, -} diff --git a/examples/quilkin-filter-example/src/greet.proto b/examples/quilkin-filter-example/src/greet.proto deleted file mode 100644 index e97bd6cae1..0000000000 --- a/examples/quilkin-filter-example/src/greet.proto +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2021 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. - */ - - -// ANCHOR: proto -syntax = "proto3"; - -package greet; - -message Greet { - string greeting = 1; -} -// ANCHOR_END: proto diff --git a/examples/quilkin-filter-example/src/main.rs b/examples/quilkin-filter-example/src/main.rs deleted file mode 100644 index 35a0f3de19..0000000000 --- a/examples/quilkin-filter-example/src/main.rs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2021 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. - */ - -#[allow(warnings, clippy::all)] -mod generated; -use generated::greet as proto; - -use quilkin::filters::prelude::*; - -use serde::{Deserialize, Serialize}; - -// ANCHOR: serde_config -#[derive(Serialize, Deserialize, Debug, schemars::JsonSchema)] -struct Config { - greeting: String, -} -// ANCHOR_END: serde_config - -// ANCHOR: TryFrom -impl TryFrom for Config { - type Error = ConvertProtoConfigError; - - fn try_from(p: proto::Greet) -> Result { - Ok(Self { - greeting: p.greeting, - }) - } -} - -impl From for proto::Greet { - fn from(config: Config) -> Self { - Self { - greeting: config.greeting, - } - } -} -// ANCHOR_END: TryFrom - -// ANCHOR: filter -struct Greet { - config: Config, -} - -impl Filter for Greet { - fn read(&self, ctx: &mut ReadContext) -> Result<(), FilterError> { - ctx.contents - .prepend_from_slice(format!("{} ", self.config.greeting).as_bytes()); - Ok(()) - } - fn write(&self, ctx: &mut WriteContext) -> Result<(), FilterError> { - ctx.contents - .prepend_from_slice(format!("{} ", self.config.greeting).as_bytes()); - Ok(()) - } -} -// ANCHOR_END: filter - -// ANCHOR: factory -use quilkin::filters::StaticFilter; - -impl StaticFilter for Greet { - const NAME: &'static str = "greet.v1"; - type Configuration = Config; - type BinaryConfiguration = proto::Greet; - - fn try_from_config(config: Option) -> Result { - Ok(Self { - config: Self::ensure_config_exists(config)?, - }) - } -} -// ANCHOR_END: factory - -// ANCHOR: run -#[tokio::main] -async fn main() -> quilkin::Result<()> { - quilkin::filters::FilterRegistry::register(vec![Greet::factory()].into_iter()); - - let (_shutdown_tx, shutdown_rx) = quilkin::make_shutdown_channel(quilkin::ShutdownKind::Normal); - let proxy = quilkin::Proxy::default(); - let config = quilkin::Config::default_non_agent(); - config.filters.store(std::sync::Arc::new( - quilkin::filters::FilterChain::try_create([quilkin::config::Filter { - name: Greet::NAME.into(), - label: None, - config: None, - }])?, - )); - config.clusters.modify(|map| { - map.insert_default( - [quilkin::net::endpoint::Endpoint::new( - (std::net::Ipv4Addr::LOCALHOST, 4321).into(), - )] - .into(), - ) - }); - - proxy - .run(config.into(), Default::default(), None, shutdown_rx) - .await -} -// ANCHOR_END: run diff --git a/src/filters.rs b/src/filters.rs index c3971a5777..586010e546 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -70,41 +70,34 @@ pub use self::{ write::WriteContext, }; +use crate::test::TestFilter; + pub use self::chain::FilterChain; +#[enum_dispatch::enum_dispatch(Filter)] +pub enum FilterKind { + Capture, + Compress, + Concatenate, + Debug, + Drop, + Firewall, + LoadBalancer, + LocalRateLimit, + Pass, + Match, + Timestamp, + TokenRouter, + HashedTokenRouter, + TestFilter, +} + /// Statically safe version of [`Filter`], if you're writing a Rust filter, you /// should implement [`StaticFilter`] in addition to [`Filter`], as /// [`StaticFilter`] guarantees all of the required properties through the type /// system, allowing Quilkin take care of the virtual table boilerplate /// automatically at compile-time. -/// ``` -/// use quilkin::filters::prelude::*; -/// -/// struct Greet; -/// -/// /// Prepends data on each packet -/// impl Filter for Greet { -/// fn read(&self, ctx: &mut ReadContext) -> Result<(), FilterError> { -/// ctx.contents.prepend_from_slice(b"Hello "); -/// Ok(()) -/// } -/// fn write(&self, ctx: &mut WriteContext) -> Result<(), FilterError> { -/// ctx.contents.prepend_from_slice(b"Goodbye "); -/// Ok(()) -/// } -/// } -/// -/// impl StaticFilter for Greet { -/// const NAME: &'static str = "greet.v1"; -/// type Configuration = (); -/// type BinaryConfiguration = (); -/// -/// fn try_from_config(_: Option) -> Result { -/// Ok(Self) -/// } -/// } -/// ``` -pub trait StaticFilter: Filter + Sized +pub trait StaticFilter: Filter + Sized + Into // This where clause simply states that `Configuration`'s and // `BinaryConfiguration`'s `Error` types are compatible with `filters::Error`. where @@ -207,6 +200,7 @@ where /// `write` implementation to execute. /// * Labels /// * `filter` The name of the filter being executed. +#[enum_dispatch::enum_dispatch] pub trait Filter: Send + Sync { /// [`Filter::read`] is invoked when the proxy receives data from a /// downstream connection on the listening port. diff --git a/src/filters/chain.rs b/src/filters/chain.rs index fc642e5e39..8d9ad21b5b 100644 --- a/src/filters/chain.rs +++ b/src/filters/chain.rs @@ -425,11 +425,11 @@ mod tests { let chain = FilterChain::new(vec![ ( TestFilter::NAME.into(), - FilterInstance::new(serde_json::json!(null), Box::new(TestFilter)), + FilterInstance::new(serde_json::json!(null), TestFilter.into()), ), ( TestFilter::NAME.into(), - FilterInstance::new(serde_json::json!(null), Box::new(TestFilter)), + FilterInstance::new(serde_json::json!(null), TestFilter.into()), ), ]) .unwrap(); @@ -477,41 +477,19 @@ mod tests { #[test] fn get_configs() { - struct TestFilter2; - impl Filter for TestFilter2 {} - - let filter_chain = FilterChain::new(vec![ - ( - "TestFilter".into(), - FilterInstance::new(serde_json::json!(null), Box::new(TestFilter)), - ), - ( - "TestFilter2".into(), - FilterInstance::new( - serde_json::json!({ "k1": "v1", "k2": 2 }), - Box::new(TestFilter2), - ), - ), - ]) + let filter_chain = FilterChain::new(vec![( + "TestFilter".into(), + FilterInstance::new(serde_json::json!(null), TestFilter.into()), + )]) .unwrap(); let configs = filter_chain.iter().collect::>(); assert_eq!( - vec![ - crate::config::Filter { - name: "TestFilter".into(), - label: None, - config: None, - }, - crate::config::Filter { - name: "TestFilter2".into(), - label: None, - config: Some(serde_json::json!({ - "k1": "v1", - "k2": 2 - })) - }, - ], + vec![crate::config::Filter { + name: "TestFilter".into(), + label: None, + config: None, + },], configs ) } diff --git a/src/filters/factory.rs b/src/filters/factory.rs index 0f5a3fbca0..c63495af7e 100644 --- a/src/filters/factory.rs +++ b/src/filters/factory.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use crate::{ config::ConfigType, - filters::{CreationError, Filter, StaticFilter}, + filters::{CreationError, FilterKind, StaticFilter}, }; /// An owned pointer to a dynamic [`FilterFactory`] instance. @@ -35,12 +35,12 @@ struct FilterInstanceData { /// The configuration used to create the filter. pub label: Option, /// The created filter. - pub filter: Box, + pub filter: FilterKind, } impl FilterInstance { /// Constructs a [`FilterInstance`]. - pub fn new(config: serde_json::Value, filter: Box) -> Self { + pub fn new(config: serde_json::Value, filter: FilterKind) -> Self { Self(Arc::new(FilterInstanceData { config, label: None, @@ -56,8 +56,8 @@ impl FilterInstance { self.0.label.as_deref() } - pub fn filter(&self) -> &dyn Filter { - &*self.0.filter + pub fn filter(&self) -> &FilterKind { + &self.0.filter } } @@ -128,7 +128,7 @@ where Ok(FilterInstance::new( config_json, - Box::from(F::try_from_config(config)?), + F::try_from_config(config)?.into(), )) } diff --git a/src/lib.rs b/src/lib.rs index 1e54b8dd85..6050c0bcf4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,5 @@ mod external_doc_tests { #![doc = include_str!("../docs/src/services/proxy/filters/match.md")] #![doc = include_str!("../docs/src/services/proxy/filters/timestamp.md")] #![doc = include_str!("../docs/src/services/proxy/filters/token_router.md")] - #![doc = include_str!("../docs/src/services/proxy/filters/writing_custom_filters.md")] #![doc = include_str!("../docs/src/services/xds/providers/filesystem.md")] }