Skip to main content

multistore_cf_workers/
response.rs

1//! Response builder helpers for Cloudflare Workers.
2//!
3//! Provides conversion functions between multistore proxy types and
4//! `web_sys::Response`, including header conversion utilities.
5
6use crate::headers::WsHeaders;
7use http::HeaderMap;
8use multistore::backend::ForwardResponse;
9use multistore::proxy::GatewayResponse;
10use multistore::route_handler::{ProxyResponseBody, ProxyResult};
11
12/// Convert a `ProxyResult` (small buffered XML/JSON) to a `web_sys::Response`.
13pub(crate) fn response_from_proxy_result(result: ProxyResult) -> web_sys::Response {
14    let resp_init = web_sys::ResponseInit::new();
15    resp_init.set_status(result.status);
16    resp_init.set_headers(&WsHeaders::from(&result.headers).into_inner().into());
17
18    match result.body {
19        ProxyResponseBody::Empty => {
20            web_sys::Response::new_with_opt_str_and_init(None, &resp_init).unwrap()
21        }
22        ProxyResponseBody::Bytes(bytes) => {
23            let uint8 = js_sys::Uint8Array::from(bytes.as_ref());
24            web_sys::Response::new_with_opt_buffer_source_and_init(Some(&uint8), &resp_init)
25                .unwrap()
26        }
27    }
28}
29
30/// Convert a `ForwardResponse<web_sys::Response>` into a `web_sys::Response`
31/// for the client, preserving the backend's body stream (zero-copy).
32pub(crate) fn response_from_forward(resp: ForwardResponse<web_sys::Response>) -> web_sys::Response {
33    let resp_init = web_sys::ResponseInit::new();
34    resp_init.set_status(resp.status);
35    resp_init.set_headers(&WsHeaders::from(&resp.headers).into_inner().into());
36
37    web_sys::Response::new_with_opt_readable_stream_and_init(resp.body.body().as_ref(), &resp_init)
38        .unwrap_or_else(|_| error_response(502, "Bad Gateway"))
39}
40
41/// Build a plain-text error response.
42pub(crate) fn error_response(status: u16, message: &str) -> web_sys::Response {
43    let init = web_sys::ResponseInit::new();
44    init.set_status(status);
45    web_sys::Response::new_with_opt_str_and_init(Some(message), &init)
46        .unwrap_or_else(|_| web_sys::Response::new().unwrap())
47}
48
49/// Extension trait for converting a [`GatewayResponse`] into a `web_sys::Response`.
50pub trait GatewayResponseExt {
51    /// Convert this gateway response into a `web_sys::Response`.
52    fn into_web_sys(self) -> web_sys::Response;
53}
54
55impl GatewayResponseExt for GatewayResponse<web_sys::Response> {
56    fn into_web_sys(self) -> web_sys::Response {
57        match self {
58            GatewayResponse::Response(r) => response_from_proxy_result(r),
59            GatewayResponse::Forward(r) => response_from_forward(r),
60        }
61    }
62}
63
64// -- Header conversion helpers -----------------------------------------------
65
66/// Convert `web_sys::Headers` to `http::HeaderMap` by iterating all entries.
67pub fn headermap_from_js(ws_headers: &web_sys::Headers) -> HeaderMap {
68    let mut headers = HeaderMap::new();
69    for entry in ws_headers.entries() {
70        let Ok(pair) = entry else { continue };
71        let arr: js_sys::Array = pair.into();
72        let Some(key) = arr.get(0).as_string() else {
73            continue;
74        };
75        let Some(value) = arr.get(1).as_string() else {
76            continue;
77        };
78        let Ok(name) = http::header::HeaderName::from_bytes(key.as_bytes()) else {
79            continue;
80        };
81        let Ok(val) = http::header::HeaderValue::from_str(&value) else {
82            continue;
83        };
84        headers.append(name, val);
85    }
86    headers
87}