|
| 1 | +//! Run the example with: |
| 2 | +//! ```sh |
| 3 | +//! cargo build --example http_server_proxy --target=wasm32-wasip2 |
| 4 | +//! wasmtime serve -Scli -Shttp --env TARGET_URL=https://example.com/ target/wasm32-wasip2/debug/examples/http_server_proxy.wasm |
| 5 | +//! curl --no-buffer -v 127.0.0.1:8080/proxy/ |
| 6 | +//! ``` |
| 7 | +use wstd::http::body::Body; |
| 8 | +use wstd::http::{Client, Error, Request, Response, StatusCode, Uri}; |
| 9 | + |
| 10 | +const PROXY_PREFIX: &str = "/proxy/"; |
| 11 | + |
| 12 | +#[wstd::http_server] |
| 13 | +async fn main(server_req: Request<Body>) -> Result<Response<Body>, Error> { |
| 14 | + match server_req.uri().path_and_query().unwrap().as_str() { |
| 15 | + api_prefixed_path if api_prefixed_path.starts_with(PROXY_PREFIX) => { |
| 16 | + // Remove PROXY_PREFIX |
| 17 | + let target_url = |
| 18 | + std::env::var("TARGET_URL").expect("missing environment variable TARGET_URL"); |
| 19 | + let target_url: Uri = format!( |
| 20 | + "{target_url}{}", |
| 21 | + api_prefixed_path |
| 22 | + .strip_prefix(PROXY_PREFIX) |
| 23 | + .expect("checked above") |
| 24 | + ) |
| 25 | + .parse() |
| 26 | + .expect("final target url should be parseable"); |
| 27 | + println!("Proxying to {target_url}"); |
| 28 | + proxy(server_req, target_url).await |
| 29 | + } |
| 30 | + _ => Ok(http_not_found(server_req)), |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +async fn proxy(server_req: Request<Body>, target_url: Uri) -> Result<Response<Body>, Error> { |
| 35 | + let client = Client::new(); |
| 36 | + let mut client_req = Request::builder(); |
| 37 | + client_req = client_req.uri(target_url).method(server_req.method()); |
| 38 | + |
| 39 | + // Copy headers from `server_req` to the `client_req`. |
| 40 | + for (key, value) in server_req.headers() { |
| 41 | + client_req = client_req.header(key, value); |
| 42 | + } |
| 43 | + |
| 44 | + // Stream the request body. |
| 45 | + let client_req = client_req.body(server_req.into_body())?; |
| 46 | + // Send the request. |
| 47 | + let client_resp = client.send(client_req).await?; |
| 48 | + // Copy headers from `client_resp` to `server_resp`. |
| 49 | + let mut server_resp = Response::builder(); |
| 50 | + for (key, value) in client_resp.headers() { |
| 51 | + server_resp |
| 52 | + .headers_mut() |
| 53 | + .expect("no errors could be in ResponseBuilder") |
| 54 | + .append(key, value.clone()); |
| 55 | + } |
| 56 | + Ok(server_resp.body(client_resp.into_body())?) |
| 57 | +} |
| 58 | + |
| 59 | +fn http_not_found(_request: Request<Body>) -> Response<Body> { |
| 60 | + Response::builder() |
| 61 | + .status(StatusCode::NOT_FOUND) |
| 62 | + .body(Body::empty()) |
| 63 | + .unwrap() |
| 64 | +} |
0 commit comments