Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions examples/cdn/api_key/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/cdn/api_key/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ crate-type = ["cdylib"]

[dependencies]
proxy-wasm = "0.2"
fastedge = { version = "0.3", features = ["proxywasm"] }
fastedge = { path = "../../../", features = ["proxywasm"] }
36 changes: 2 additions & 34 deletions examples/cdn/headers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,22 +161,14 @@ impl HttpContext for HttpHeaders {
}

// check if the response header is not returned
let Some(value) = self.get_http_response_header("host") else {
if self.get_http_response_header("host").is_some() {
self.send_http_response(553, vec![], None);
return Action::Pause;
};
if !value.is_empty() {
self.send_http_response(554, vec![], None);
return Action::Pause;
}
let Some(value) = self.get_http_response_header_bytes("host") else {
if self.get_http_response_header_bytes("host").is_some() {
self.send_http_response(553, vec![], None);
return Action::Pause;
};
if !value.is_empty() {
self.send_http_response(554, vec![], None);
return Action::Pause;
}

let response_headers = self.get_http_response_headers();
if response_headers.len() != 1 {
Expand Down Expand Up @@ -321,30 +313,6 @@ impl HttpContext for HttpHeaders {
return Action::Pause;
}

// check if the reponse header is not returnd
let Some(value) = self.get_http_response_header("host") else {
self.send_http_response(553, vec![], None);
return Action::Pause;
};
if !value.is_empty() {
self.send_http_response(554, vec![], None);
return Action::Pause;
}
let Some(value) = self.get_http_response_header_bytes("host") else {
self.send_http_response(553, vec![], None);
return Action::Pause;
};
if !value.is_empty() {
self.send_http_response(554, vec![], None);
return Action::Pause;
}

let request_headers = self.get_http_response_headers();
if request_headers.is_empty() {
self.send_http_response(555, vec![], None);
return Action::Pause;
}

Action::Continue
}

Expand Down
2 changes: 1 addition & 1 deletion examples/cdn/jwt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ crate-type = ["cdylib"]

[dependencies]
proxy-wasm = "0.2"
fastedge = { version = "0.3", features = ["proxywasm"] }
fastedge = { path = "../../../", features = ["proxywasm"] }
jsonwebtoken = "9"
serde = { version = "1", features = ["derive"] }
headers = "0.4"
2 changes: 1 addition & 1 deletion examples/cdn/key_value/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ crate-type = ["cdylib"]

[dependencies]
proxy-wasm = "0.2"
fastedge = { version = "0.3", features = ["proxywasm"] }
fastedge = { path = "../../../", features = ["proxywasm"] }
querystring = "1.1"
serde_json = "1"
2 changes: 1 addition & 1 deletion examples/cdn/large_env_variable/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ crate-type = ["cdylib"]

[dependencies]
proxy-wasm = "0.2"
fastedge = { version = "0.3", features = ["proxywasm"] }
fastedge = { path = "../../../", features = ["proxywasm"] }
12 changes: 12 additions & 0 deletions examples/cdn/local_response/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[workspace]

[package]
name = "local_response"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]

[dependencies]
proxy-wasm = "0.2"
63 changes: 63 additions & 0 deletions examples/cdn/local_response/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use proxy_wasm::traits::*;
use proxy_wasm::types::*;

proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) });
}}

struct HttpHeadersRoot;

impl Context for HttpHeadersRoot {}

impl RootContext for HttpHeadersRoot {
fn create_http_context(&self, _context_id: u32) -> Option<Box<dyn HttpContext>> {
Some(Box::new(HttpHeaders))
}

fn get_type(&self) -> Option<ContextType> {
Some(ContextType::HttpContext)
}
}

struct HttpHeaders;

impl Context for HttpHeaders {}

impl HttpContext for HttpHeaders {
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
self.send_http_response(
251,
vec![("Powered-By", "proxy-wasm"), ("Key", "Value")], // Headers
Some(b"on_http_request_headers response from proxywasm local response example"),
);
Action::Pause
}

fn on_http_request_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action {
self.send_http_response(
253,
vec![("Powered-By", "proxy-wasm"), ("Key", "Value")], // Headers
Some(b"on_http_request_body response from proxywasm local response example"),
);
Action::Pause
}

fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
self.send_http_response(
252,
vec![("Powered-By", "proxy-wasm"), ("Key", "Value")], // Headers
Some(b"on_http_response_headers response from proxywasm local response example"),
);
Action::Pause
}

fn on_http_response_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action {
self.send_http_response(
254,
vec![("Powered-By", "proxy-wasm"), ("Key", "Value")], // Headers
Some(b"on_http_response_body response from proxywasm local response example"),
);
Action::Pause
}
}
23 changes: 9 additions & 14 deletions examples/cdn/properties/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,14 @@ impl HttpContext for HttpHeaders {
println!(" scheme = {} ", String::from_utf8_lossy(&scheme));
self.add_http_response_header_bytes("request-scheme", &scheme);

let Some(extension) = self.get_property(vec![REQUEST_EXTENSION]) else {
self.send_http_response(555, vec![], None);
return Action::Pause;
};
println!(" extension = {} ", String::from_utf8_lossy(&extension));
self.add_http_response_header_bytes("request-extension", &extension);

let Some(query) = self.get_property(vec![REQUEST_QUERY]) else {
self.send_http_response(556, vec![], None);
return Action::Pause;
let query = match self.get_property(vec![REQUEST_QUERY]) {
None => Bytes::new(),
Some(query) => {
println!(" query = {} ", String::from_utf8_lossy(&query));
self.add_http_response_header_bytes("request-query", &query);
query
}
};
println!(" query = {} ", String::from_utf8_lossy(&query));
self.add_http_response_header_bytes("request-query", &query);

let Some(client_ip) = self.get_property(vec![REQUEST_X_REAL_IP]) else {
self.send_http_response(557, vec![], None);
Expand Down Expand Up @@ -151,9 +146,9 @@ impl HttpContext for HttpHeaders {
println!(" continent = {} ", String::from_utf8_lossy(&value));
self.add_http_response_header_bytes("request-continent", &value);

let query = String::from_utf8_lossy(&query);
let query = std::str::from_utf8(&query).unwrap();
println!("query={}", query);
let params = querystring::querify(&query);
let params = querystring::querify(query);

if let Some(url) = params.iter().find_map(|(k, v)| {
if "url".eq_ignore_ascii_case(k) {
Expand Down
14 changes: 14 additions & 0 deletions examples/cdn/request_url/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[workspace]

[package]
name = "request_url"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]

[dependencies]
log = "0.4"
proxy-wasm = "0.2"
querystring = "1.1"
43 changes: 43 additions & 0 deletions examples/cdn/request_url/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[← Back to examples](../../README.md)

# Request URL (CDN)

A CDN proxy-wasm filter that rewrites outbound request properties — URL, host, path, and query string — based on incoming request headers.

## What it does

On each incoming HTTP request the filter reads the following special headers and, if present, overwrites the corresponding request property before the request is forwarded to the origin:

| Header | Property overwritten |
|--------|----------------------|
| `set-url` | `request.url` — full request URL |
| `set-host` | `request.host` — host name |
| `set-path` | `request.path` — URL path |
| `set-query` | `request.query` — query string |

It also writes a custom Nginx log field `nginx.log_field1` on every request.

## Use cases

- Rewrite the upstream URL at the edge without changing client-visible headers.
- Override the request host for multi-tenant routing.
- Strip or replace the path / query string before hitting the origin.

## Build

```bash
cargo build --target wasm32-wasip1 --release
```

## Example

Forward a request but override the path and query string:

```http
GET /original-path HTTP/1.1
Host: example.com
set-path: /new-path
set-query: page=2&limit=10
```

The filter rewrites `request.path` to `/new-path` and `request.query` to `page=2&limit=10` before the request reaches the origin.
103 changes: 103 additions & 0 deletions examples/cdn/request_url/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use log::info;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;

proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) });
}}

struct HttpHeadersRoot;

impl Context for HttpHeadersRoot {}

impl RootContext for HttpHeadersRoot {
fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> {
Some(Box::new(HttpHeaders { context_id }))
}

fn get_type(&self) -> Option<ContextType> {
Some(ContextType::HttpContext)
}
}

struct HttpHeaders {
context_id: u32,
}

impl Context for HttpHeaders {}

pub const REQUEST_URI: &str = "request.url";
pub const REQUEST_HOST: &str = "request.host";
pub const REQUEST_PATH: &str = "request.path";
pub const REQUEST_QUERY: &str = "request.query";

impl HttpContext for HttpHeaders {
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {

let Some(uri) = self.get_property(vec![REQUEST_URI]) else {
self.send_http_response(551, vec![], None);
return Action::Pause;
};
self.add_http_request_header_bytes("request-uri", &uri);

let Some(host) = self.get_property(vec![REQUEST_HOST]) else {
self.send_http_response(552, vec![], None);
return Action::Pause;
};
self.add_http_request_header_bytes("request-host", &host);

let Some(path) = self.get_property(vec![REQUEST_PATH]) else {
self.send_http_response(553, vec![], None);
return Action::Pause;
};
self.add_http_request_header_bytes("request-path", &path);

let Some(query) = self.get_property(vec![REQUEST_QUERY]) else {
self.send_http_response(554, vec![], None);
return Action::Pause;
};
self.add_http_request_header_bytes("request-query", &query);

if let Some(new_url) = self.get_http_request_header("set-url") {
self.set_property(
vec!["request.url"],
Some(new_url.as_bytes()),
);
}

if let Some(new_host) = self.get_http_request_header("set-host") {
self.set_property(
vec!["request.host"],
Some(new_host.as_bytes()),
);
}

if let Some(new_path) = self.get_http_request_header("set-path") {
self.set_property(
vec!["request.path"],
Some(new_path.as_bytes()),
);
}

if let Some(new_query) = self.get_http_request_header("set-query") {
self.set_property(
vec!["request.query"],
Some(new_query.as_bytes()),
);
}

self.set_property(
vec!["nginx.log_field1"],
Some(b"from_wasm nginx.log_field1"),
);

Action::Continue
}

fn on_log(&mut self) {
info!("#{} completed.", self.context_id);
}
}


Loading
Loading