├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE.md ├── Makefile.in ├── README.md ├── benches ├── build_router_rule_benchmark.rs ├── filter_body_benchmark.rs ├── match_rule_benchmark.rs ├── perf.rs └── test_examples_benchmark.rs ├── cbindgen.toml ├── configure.ac ├── libredirectionio.pc.in ├── pkg ├── package.json ├── redirectionio.d.ts ├── redirectionio.js └── tsconfig.json ├── rustfmt.toml ├── src ├── action │ ├── ffi.rs │ ├── log_override.rs │ ├── mod.rs │ ├── run.rs │ ├── status_code_update.rs │ ├── trace.rs │ └── unit_trace.rs ├── api │ ├── body_filter.rs │ ├── date_time.rs │ ├── examples.rs │ ├── explain_request.rs │ ├── ffi.rs │ ├── header.rs │ ├── header_filter.rs │ ├── impact.rs │ ├── ip.rs │ ├── log.rs │ ├── marker.rs │ ├── mod.rs │ ├── redirection_loop.rs │ ├── rule.rs │ ├── rules_message.rs │ ├── source.rs │ ├── test_examples.rs │ ├── transformer.rs │ ├── unit_ids.rs │ └── variable.rs ├── build.rs ├── callback_log.rs ├── dot.rs ├── ffi_helpers.rs ├── filter │ ├── buffer.rs │ ├── encoding │ │ ├── decode.rs │ │ ├── encode.rs │ │ └── mod.rs │ ├── error.rs │ ├── filter_body.rs │ ├── filter_header.rs │ ├── header_action │ │ ├── header_add.rs │ │ ├── header_default.rs │ │ ├── header_override.rs │ │ ├── header_remove.rs │ │ ├── header_replace.rs │ │ └── mod.rs │ ├── html_body_action │ │ ├── body_append.rs │ │ ├── body_prepend.rs │ │ ├── body_replace.rs │ │ └── mod.rs │ ├── html_filter_body.rs │ ├── mod.rs │ └── text_filter_body.rs ├── html │ ├── error.rs │ └── mod.rs ├── http │ ├── addr.rs │ ├── ffi.rs │ ├── header.rs │ ├── mod.rs │ ├── query.rs │ └── request.rs ├── lib.rs ├── marker │ ├── mod.rs │ └── transformer │ │ ├── camelize.rs │ │ ├── dasherize.rs │ │ ├── lowercase.rs │ │ ├── mod.rs │ │ ├── replace.rs │ │ ├── slice.rs │ │ ├── underscorize.rs │ │ └── uppercase.rs ├── regex.rs ├── regex_radix_tree │ ├── item.rs │ ├── iter.rs │ ├── leaf.rs │ ├── mod.rs │ ├── node.rs │ ├── prefix.rs │ ├── trace.rs │ └── tree.rs ├── router │ ├── mod.rs │ ├── request_matcher │ │ ├── datetime.rs │ │ ├── header.rs │ │ ├── host.rs │ │ ├── ip.rs │ │ ├── method.rs │ │ ├── mod.rs │ │ ├── path_and_query.rs │ │ └── scheme.rs │ ├── route.rs │ ├── route_datetime.rs │ ├── route_header.rs │ ├── route_ip.rs │ ├── route_time.rs │ ├── route_weekday.rs │ └── trace.rs ├── router_config.rs ├── utils.rs └── wasm_api.rs └── tests ├── redirectionio_router_test.rs ├── redirectionio_test_examples.rs ├── templates ├── redirectionio_router_test.rs.j2 └── redirectionio_test_examples.rs.j2 └── test_examples ├── configuration_log_off.in.json ├── configuration_log_off.out.json ├── configuration_reset_on.in.json ├── configuration_reset_on.out.json ├── header_add.in.json ├── header_add.out.json ├── header_override.in.json ├── header_override.out.json ├── header_remove.in.json ├── header_remove.out.json ├── header_replace.in.json ├── header_replace.out.json ├── must_match_false_broken.in.json ├── must_match_false_broken.out.json ├── must_match_true_broken.in.json ├── must_match_true_broken.out.json ├── no_examples.in.json ├── no_examples.out.json ├── no_rules.in.json ├── no_rules.out.json ├── one_rule_one_example.in.json └── one_rule_one_example.out.json /.gitignore: -------------------------------------------------------------------------------- 1 | autom4te.cache 2 | target/ 3 | target-musl/ 4 | rules.json 5 | config.log 6 | config.status 7 | configure 8 | libredirectionio.pc 9 | Makefile 10 | wrangler.toml 11 | worker/generated 12 | Cargo.lock 13 | redirectionio.h 14 | /tests/test_examples/*.out.current.json 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.11.2 - 17-05-2024 2 | 3 | * Add wasmbindgen feature to not expose the library as a javascript module when used as a dependency by default 4 | 5 | ## 2.11.1 - 17-05-2024 6 | 7 | * Fix request time in proxy log to work with millisecond precision 8 | * Export this library as a javascript module with webassembly 9 | 10 | ## 2.11.0 - 27-02-2024 11 | 12 | * Fix wasm integration by removing call to `SystemTime::now` which is not available in wasm 13 | * Fix default local network for trusted proxies 14 | * Add default header action 15 | 16 | ## 2.10.1 - 06-02-2024 17 | 18 | * Fix building without specific features 19 | 20 | ## 2.10.0 - 06-02-2024 21 | 22 | * Updated dependencies 23 | * Fix request time in log 24 | * Add backend duration to log 25 | 26 | ## 2.9.0 - 02-10-2023 27 | 28 | * Updated dependencies 29 | * Improve memory usage in router 30 | * Improve performance when creating a router 31 | * Improve performance when using html filters 32 | 33 | ## 2.8.0 - 26-05-2023 34 | 35 | * Fix parsing bug in ip address for logs and request matching 36 | * Fix incorrect behavior when matching a request against a response status code 37 | * Add support for excluding methods in request matching 38 | * Add support for excluding response status code in request matching 39 | 40 | ## 2.7.1 - 23-03-2023 41 | 42 | * Add optional features to libredirectionio, to allow better compilation on wasm target 43 | 44 | ## 2.7.0 - 20-03-2023 45 | 46 | * Add new trigger support for date, time and date time 47 | 48 | ## 2.6.0 - 08-03-2023 49 | 50 | * Add support for deflate and brotli compression 51 | * Avoid creating a body filter if there is only compression and no real filters 52 | * Fix supporting host with port 53 | * Add support to trusted proxies in log to avoid collecting their ips 54 | * Support streaming body when there is compression 55 | 56 | ## 2.5.2 - 24-11-2022 57 | 58 | * Fix a bug in wasm target compilation that make it impossible to compile 59 | 60 | ## 2.5.1 - 23-11-2022 61 | 62 | * Ensure order for rules, no rules will always be applied in the same order even if they have the same priority, as they 63 | are sorted by their ID 64 | * Body filters will now only be applied on a correct content-type header 65 | 66 | ## 2.5.0 - 28-10-2022 67 | 68 | * Add support for gzip compression in body filter 69 | * Hardened error handling in body filter 70 | 71 | ## 2.4.0 - 07-07-2022 72 | 73 | * Update crate to publish on crates.io 74 | 75 | ## 2.3.0 - 13-04-2022 76 | 77 | * Add support to trigger on specific ip address 78 | * Add variable feature which allow using request properties in action values 79 | * Add append/prepend/replace text content 80 | * Fix cases when an url could not be parsed 81 | * Fix wrong rule ids reported in log or header 82 | 83 | ## 2.2.0 - 21-04-2021 84 | 85 | * Add cache support for cloudflare worker 86 | 87 | ## 2.1.1 - 16-03-2021 88 | 89 | * Dependencies update 90 | 91 | ## 2.1.0 - 02-02-2021 92 | 93 | * Allow to globally ignore and copy specific query parameter 94 | * Add client ip to log 95 | * Extract ip from forwraded header in log 96 | 97 | ## 2.0.0 - 11-01-2021 98 | 99 | * Initial stable version of libredirectionio 100 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "redirectionio" 3 | description = "Redirection IO Library to handle matching rule, redirect and filtering headers and body." 4 | repository = "https://github.com/redirectionio/libredirectionio" 5 | license = "MIT" 6 | version = "2.11.2" 7 | authors = ["Joel Wurtz "] 8 | edition = "2024" 9 | build = "src/build.rs" 10 | 11 | [lib] 12 | name = "redirectionio" 13 | crate-type = ["staticlib", "cdylib", "rlib"] 14 | 15 | [build-dependencies] 16 | cbindgen = "0.28.0" 17 | glob = "^0.3.1" 18 | libtool = "0.1.1" 19 | linked_hash_set = { version = "0.1.4", features = ["serde"] } 20 | serde = { version = "1.0.215", features = ["derive"] } 21 | serde_yaml = "0.9.34" 22 | tera = "1.20.0" 23 | 24 | [features] 25 | default = ["compress", "router"] 26 | compress = ["dep:brotli", "dep:flate2"] 27 | router = [] 28 | dot = ["dep:dot_graph"] 29 | wasmbind = [] 30 | 31 | [target.'cfg(target_arch = "wasm32")'.dependencies] 32 | chrono = { version = "0.4.38", features = ["serde", "wasmbind"] } 33 | futures-util = { version = "0.3.30", default-features = false } 34 | getrandom = { version = "0.2.15", features = ["js"] } 35 | log = "0.4.22" 36 | serde = { version = "1.0.215", features = ["derive"] } 37 | serde_json = "1.0.133" 38 | wasm-bindgen = "0.2.95" 39 | wasm-logger = "0.2.0" 40 | 41 | [dependencies] 42 | brotli = { version = "8.0.0", optional = true } 43 | chrono = { version = "0.4.38", features = ["serde"] } 44 | cidr = { version = "0.3.0", features = ["serde"] } 45 | dot_graph = { version = "0.2.3", optional = true } 46 | flate2 = { version = "1.0.35", optional = true } 47 | heck = "0.5.0" 48 | http = "1.2.0" 49 | lazy_static = "1.5.0" 50 | linked_hash_set = { version = "0.1.4", features = ["serde"] } 51 | log = "0.4.22" 52 | percent-encoding = "2.3.1" 53 | rand = "0.8.5" 54 | regex = "1.11.1" 55 | scraper = "0.21.0" 56 | serde = { version = "1.0.215", features = ["derive", "rc"] } 57 | serde_json = "1.0.133" 58 | stderrlog = "0.6.0" 59 | tracing = "0.1.40" 60 | trusted-proxies = "0.3.0" 61 | url = "2.5.3" 62 | 63 | [dev-dependencies] 64 | pprof = { version = "0.14.0", features = ["flamegraph"] } 65 | criterion = { version = "0.5.1", default-features = false } 66 | 67 | [[bench]] 68 | name = "match_rule_benchmark" 69 | harness = false 70 | 71 | [[bench]] 72 | name = "build_router_rule_benchmark" 73 | harness = false 74 | 75 | [[bench]] 76 | name = "filter_body_benchmark" 77 | harness = false 78 | 79 | [[bench]] 80 | name = "test_examples_benchmark" 81 | harness = false 82 | 83 | [dependencies.web-sys] 84 | version = "0.3.72" 85 | features = [ 86 | "console", 87 | ] 88 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 redirection.io 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | RUST_SOURCES = $(shell find src/ -type f -name '*.rs') 2 | 3 | RUST_EXTRA = \ 4 | Cargo.toml 5 | 6 | EXTRA_DIST += $(RUST_SOURCES) $(RUST_EXTRA) 7 | 8 | DESTDIR ?= 9 | 10 | MAKE_PID := $(shell echo $$PPID) 11 | JOB_FLAG := $(filter -j%, $(subst -j ,-j,$(shell ps T | grep "^\s*$(MAKE_PID).*$(MAKE)"))) 12 | JOBS := $(subst -j,,$(JOB_FLAG)) 13 | 14 | ifdef JOBS 15 | CARGO_FLAGS=-j $(JOBS) 16 | else 17 | CARGO_FLAGS= 18 | endif 19 | 20 | prefix := @prefix@ 21 | exec_prefix := @exec_prefix@ 22 | 23 | all: @RUST_TARGET_DIR@/@RUST_TARGET_SUBDIR@/libredirectionio.a redirectionio.h 24 | 25 | redirectionio.h: $(EXTRA_DIST) 26 | @CARGO_BIN@ build $(CARGO_FLAGS) --lib @RUST_RELEASE@ --target-dir @RUST_TARGET_DIR@ 27 | 28 | @RUST_TARGET_DIR@/@RUST_TARGET_SUBDIR@/libredirectionio.a: $(EXTRA_DIST) 29 | @CARGO_BIN@ build $(CARGO_FLAGS) --lib @RUST_RELEASE@ --target-dir @RUST_TARGET_DIR@ 30 | 31 | install: @RUST_TARGET_DIR@/@RUST_TARGET_SUBDIR@/libredirectionio.a redirectionio.h 32 | mkdir -p $(DESTDIR)@prefix@/include/ 33 | install -c -m 644 redirectionio.h $(DESTDIR)@prefix@/include/ 34 | mkdir -p $(DESTDIR)@libdir@/ 35 | install -c -m 644 @RUST_TARGET_DIR@/@RUST_TARGET_SUBDIR@/libredirectionio.a $(DESTDIR)@libdir@/ 36 | mkdir -p $(DESTDIR)@libdir@/pkgconfig/ 37 | install -c -m 644 libredirectionio.pc $(DESTDIR)@libdir@/pkgconfig/ 38 | 39 | build-wasm: 40 | wasm-pack build --scope redirection.io --no-default-features --out-dir pkg/wasm --no-pack --release --features wasmbind 41 | 42 | .PHONY: clean 43 | clean: 44 | rm -f redirectionio.h 45 | rm -f @RUST_TARGET_DIR@/@RUST_TARGET_SUBDIR@/libredirectionio.a 46 | @CARGO_BIN@ clean --package redirectionio @RUST_RELEASE@ 47 | 48 | .PHONY: fullclean 49 | fullclean: 50 | rm -rf target 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | rust library that process redirect and filtering from rules coming of https://redirection.io/ 4 | 5 | This library is mainly used in proxies and agent of redirectionio. 6 | 7 | ## Installation 8 | 9 | You only to install this library if you wish to compile apache or nginx proxies or build the web assembly module. 10 | 11 | ### Library 12 | 13 | You need rust and cargo to compile this library, to install on your system run the following commands: 14 | 15 | ``` 16 | autoreconf -i 17 | ./configure 18 | make 19 | ``` 20 | 21 | You can run `make install` (with root permissions) to install this library into your system, this is required if you need to 22 | compile some of our modules against this library. 23 | 24 | ## Tests 25 | 26 | Some tests are generated. Templates are located in `tests/templates` folder. If 27 | you update them, you need to run `cargo build` to (re)generate the tests. 28 | -------------------------------------------------------------------------------- /benches/build_router_rule_benchmark.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use std::fs::File; 5 | 6 | use criterion::{BatchSize, BenchmarkId, Criterion}; 7 | use flate2::read::GzDecoder; 8 | use redirectionio::{ 9 | RouterConfig, 10 | api::{Rule, RulesMessage}, 11 | router::Router, 12 | }; 13 | 14 | fn create_rules(filename: String) -> RulesMessage { 15 | let content_gzip = File::open(filename.clone()).expect("Cannot open file"); 16 | let rules: RulesMessage = serde_json::from_reader(GzDecoder::new(content_gzip)).expect("Cannot deserialize"); 17 | 18 | rules 19 | } 20 | 21 | fn build_router_bench(c: &mut Criterion) { 22 | let files = vec![ 23 | "../bench-files/heavy-memory-rules.json.gz".to_string(), 24 | "../bench-files/large-rules-1k.json.gz".to_string(), 25 | "../bench-files/large-rules-10k.json.gz".to_string(), 26 | "../bench-files/large-rules-50k.json.gz".to_string(), 27 | "../bench-files/large-rules-150k.json.gz".to_string(), 28 | "../bench-files/large-rules-200k.json.gz".to_string(), 29 | "../bench-files/large-rules-210k.json.gz".to_string(), 30 | ]; 31 | 32 | let mut group = c.benchmark_group("router_builder"); 33 | 34 | for filename in files { 35 | group.sample_size(10); 36 | group.bench_with_input(BenchmarkId::from_parameter(filename.clone()), &filename, |b, f| { 37 | b.iter_batched( 38 | || create_rules(f.to_string()), 39 | |rules| { 40 | let config = RouterConfig::default(); 41 | let mut router = Router::::from_config(config.clone()); 42 | let rules_len = rules.rules.len(); 43 | 44 | for rule in rules.rules { 45 | router.insert(rule); 46 | } 47 | 48 | router.cache(None); 49 | 50 | assert_eq!(router.len(), rules_len); 51 | }, 52 | BatchSize::NumIterations(1), 53 | ); 54 | }); 55 | } 56 | 57 | group.finish(); 58 | } 59 | 60 | criterion_group!(benches, build_router_bench); 61 | criterion_main!(benches); 62 | -------------------------------------------------------------------------------- /benches/perf.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::File, os::raw::c_int, path::Path}; 2 | 3 | use criterion::profiler::Profiler; 4 | use pprof::{ProfilerGuard, ProfilerGuardBuilder}; 5 | 6 | /// Small custom profiler that can be used with Criterion to create a flamegraph for benchmarks. 7 | /// Also see [the Criterion documentation on this][custom-profiler]. 8 | /// 9 | /// ## Example on how to enable the custom profiler: 10 | /// 11 | /// ``` 12 | /// mod perf; 13 | /// use perf::FlamegraphProfiler; 14 | /// 15 | /// fn fibonacci_profiled(criterion: &mut Criterion) { 16 | /// // Use the criterion struct as normal here. 17 | /// } 18 | /// 19 | /// fn custom() -> Criterion { 20 | /// Criterion::default().with_profiler(FlamegraphProfiler::new()) 21 | /// } 22 | /// 23 | /// criterion_group! { 24 | /// name = benches; 25 | /// config = custom(); 26 | /// targets = fibonacci_profiled 27 | /// } 28 | /// ``` 29 | /// 30 | /// The neat thing about this is that it will sample _only_ the benchmark, and not other stuff like 31 | /// the setup process. 32 | /// 33 | /// Further, it will only kick in if `--profile-time