[go: up one dir, main page]

worker/
response.rs

1use crate::cors::Cors;
2use crate::error::Error;
3use crate::headers::Headers;
4use crate::ByteStream;
5use crate::Result;
6use crate::WebSocket;
7
8#[cfg(feature = "http")]
9use bytes::Bytes;
10use futures_util::{TryStream, TryStreamExt};
11use js_sys::Uint8Array;
12use serde::{de::DeserializeOwned, Serialize};
13#[cfg(feature = "http")]
14use std::convert::TryFrom;
15use wasm_bindgen::JsCast;
16use wasm_bindgen::JsValue;
17use web_sys::ReadableStream;
18use worker_sys::ext::{ResponseExt, ResponseInitExt};
19
20#[derive(Debug, Clone)]
21pub enum ResponseBody {
22    Empty,
23    Body(Vec<u8>),
24    Stream(ReadableStream),
25}
26
27const CONTENT_TYPE: &str = "content-type";
28
29/// A [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) representation for
30/// working with or returning a response to a `Request`.
31#[derive(Debug)]
32pub struct Response {
33    body: ResponseBody,
34    init: ResponseBuilder,
35}
36
37#[cfg(feature = "http")]
38impl<B: http_body::Body<Data = Bytes> + 'static> TryFrom<http::Response<B>> for Response {
39    type Error = crate::Error;
40    fn try_from(res: http::Response<B>) -> Result<Self> {
41        let resp = crate::http::response::to_wasm(res)?;
42        Ok(resp.into())
43    }
44}
45
46#[cfg(feature = "http")]
47impl TryFrom<Response> for crate::HttpResponse {
48    type Error = crate::Error;
49    fn try_from(res: Response) -> Result<crate::HttpResponse> {
50        let sys_resp: web_sys::Response = res.into();
51        crate::http::response::from_wasm(sys_resp)
52    }
53}
54
55impl Response {
56    /// Construct a builder for a new `Response`.
57    pub fn builder() -> ResponseBuilder {
58        ResponseBuilder::new()
59    }
60
61    /// Create a `Response` using `B` as the body encoded as JSON. Sets the associated
62    /// `Content-Type` header for the `Response` as `application/json`.
63    pub fn from_json<B: Serialize>(value: &B) -> Result<Self> {
64        ResponseBuilder::new().from_json(value)
65    }
66
67    /// Create a `Response` using the body encoded as HTML. Sets the associated `Content-Type`
68    /// header for the `Response` as `text/html; charset=utf-8`.
69    pub fn from_html(html: impl AsRef<str>) -> Result<Self> {
70        ResponseBuilder::new().from_html(html)
71    }
72
73    /// Create a `Response` using unprocessed bytes provided. Sets the associated `Content-Type`
74    /// header for the `Response` as `application/octet-stream`.
75    pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {
76        ResponseBuilder::new().from_bytes(bytes)
77    }
78
79    /// Create a `Response` using a `ResponseBody` variant. Sets a status code of 200 and an empty
80    /// set of Headers. Modify the Response with methods such as `with_status` and `with_headers`.
81    pub fn from_body(body: ResponseBody) -> Result<Self> {
82        Ok(ResponseBuilder::new().body(body))
83    }
84
85    /// Create a `Response` using a `WebSocket` client. Configures the browser to switch protocols
86    /// (using status code 101) and returns the websocket.
87    pub fn from_websocket(websocket: WebSocket) -> Result<Self> {
88        Ok(ResponseBuilder::new()
89            .with_websocket(websocket)
90            .with_status(101)
91            .empty())
92    }
93
94    /// Create a `Response` using a [`Stream`](futures::stream::Stream) for the body. Sets a status
95    /// code of 200 and an empty set of Headers. Modify the Response with methods such as
96    /// `with_status` and `with_headers`.
97    pub fn from_stream<S>(stream: S) -> Result<Self>
98    where
99        S: TryStream + 'static,
100        S::Ok: Into<Vec<u8>>,
101        S::Error: Into<Error>,
102    {
103        ResponseBuilder::new().from_stream(stream)
104    }
105
106    /// Create a `Response` using unprocessed text provided. Sets the associated `Content-Type`
107    /// header for the `Response` as `text/plain; charset=utf-8`.
108    pub fn ok(body: impl Into<String>) -> Result<Self> {
109        ResponseBuilder::new().ok(body)
110    }
111
112    /// Create an empty `Response` with a 200 status code.
113    pub fn empty() -> Result<Self> {
114        Ok(ResponseBuilder::new().empty())
115    }
116
117    /// A helper method to send an error message to a client. Will return `Err` if the status code
118    /// provided is outside the valid HTTP error range of 400-599.
119    pub fn error(msg: impl Into<String>, status: u16) -> Result<Self> {
120        if !(400..=599).contains(&status) {
121            return Err(Error::Internal(
122                "error status codes must be in the 400-599 range! see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status for more".into(),
123            ));
124        }
125
126        Ok(ResponseBuilder::new()
127            .with_status(status)
128            .fixed(msg.into().into_bytes()))
129    }
130
131    /// Create a `Response` which redirects to the specified URL with default status_code of 302
132    pub fn redirect(url: url::Url) -> Result<Self> {
133        match web_sys::Response::redirect(url.as_str()) {
134            Ok(edge_response) => Ok(Response::from(edge_response)),
135            Err(err) => Err(Error::from(err)),
136        }
137    }
138
139    /// Create a `Response` which redirects to the specified URL with a custom status_code
140    pub fn redirect_with_status(url: url::Url, status_code: u16) -> Result<Self> {
141        if !(300..=399).contains(&status_code) {
142            return Err(Error::Internal(
143                "redirect status codes must be in the 300-399 range! Please checkout https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages for more".into(),
144            ));
145        }
146        match web_sys::Response::redirect_with_status(url.as_str(), status_code) {
147            Ok(edge_response) => Ok(Response::from(edge_response)),
148            Err(err) => Err(Error::from(err)),
149        }
150    }
151
152    /// Get the HTTP Status code of this `Response`.
153    pub fn status_code(&self) -> u16 {
154        self.init.status_code
155    }
156
157    /// Access this response's body
158    pub fn body(&self) -> &ResponseBody {
159        &self.body
160    }
161
162    /// Access this response's body as plaintext.
163    pub async fn text(&mut self) -> Result<String> {
164        match &self.body {
165            ResponseBody::Body(bytes) => {
166                Ok(String::from_utf8(bytes.clone()).map_err(|e| Error::from(e.to_string()))?)
167            }
168            ResponseBody::Empty => Ok(String::new()),
169            ResponseBody::Stream(_) => {
170                let bytes = self.bytes().await?;
171                String::from_utf8(bytes).map_err(|e| Error::RustError(e.to_string()))
172            }
173        }
174    }
175
176    /// Access this response's body encoded as JSON.
177    pub async fn json<B: DeserializeOwned>(&mut self) -> Result<B> {
178        serde_json::from_str(&self.text().await?).map_err(Error::from)
179    }
180
181    /// Access this response's body encoded as raw bytes.
182    pub async fn bytes(&mut self) -> Result<Vec<u8>> {
183        match &self.body {
184            ResponseBody::Body(bytes) => Ok(bytes.clone()),
185            ResponseBody::Empty => Ok(Vec::new()),
186            ResponseBody::Stream(_) => {
187                self.stream()?
188                    .try_fold(Vec::new(), |mut bytes, mut chunk| async move {
189                        bytes.append(&mut chunk);
190                        Ok(bytes)
191                    })
192                    .await
193            }
194        }
195    }
196
197    /// Access this response's body as a [`Stream`](futures::stream::Stream) of bytes.
198    pub fn stream(&mut self) -> Result<ByteStream> {
199        let stream = match &self.body {
200            ResponseBody::Stream(edge_request) => edge_request.clone(),
201            _ => return Err(Error::RustError("body is not streamable".into())),
202        };
203
204        let stream = wasm_streams::ReadableStream::from_raw(stream.dyn_into().unwrap());
205
206        Ok(ByteStream {
207            inner: stream.into_stream(),
208        })
209    }
210
211    // Get the WebSocket returned by the the server.
212    pub fn websocket(self) -> Option<WebSocket> {
213        self.init.websocket
214    }
215
216    /// Set this response's `Headers`.
217    pub fn with_headers(mut self, headers: Headers) -> Self {
218        self.init = self.init.with_headers(headers);
219        self
220    }
221
222    /// Set this response's status code.
223    /// The Workers platform will reject HTTP status codes outside the range of 200..599 inclusive,
224    /// and will throw a JavaScript `RangeError`, returning a response with an HTTP 500 status code.
225    pub fn with_status(mut self, status_code: u16) -> Self {
226        self.init = self.init.with_status(status_code);
227        self
228    }
229
230    /// Sets this response's cors headers from the `Cors` struct.
231    /// Example usage:
232    /// ```
233    /// use worker::*;
234    /// fn fetch() -> worker::Result<Response> {
235    ///     let cors = Cors::default();
236    ///     Response::empty()?
237    ///         .with_cors(&cors)
238    /// }
239    /// ```
240    pub fn with_cors(mut self, cors: &Cors) -> Result<Self> {
241        self.init = self.init.with_cors(cors)?;
242        Ok(self)
243    }
244
245    /// Sets this response's `webSocket` option.
246    /// This will require a status code 101 to work.
247    pub fn with_websocket(mut self, websocket: Option<WebSocket>) -> Self {
248        self.init.websocket = websocket;
249        self
250    }
251
252    /// Read the `encode_body` configuration for this `Response`.
253    pub fn encode_body(&self) -> &EncodeBody {
254        &self.init.encode_body
255    }
256
257    /// Set this response's `encodeBody` option.
258    /// In most cases this is not needed, but it can be set to "manual" to
259    /// return already compressed data to the user without re-compression.
260    pub fn with_encode_body(mut self, encode_body: EncodeBody) -> Self {
261        self.init.encode_body = encode_body;
262        self
263    }
264
265    /// Read the `cf` information for this `Response`.
266    pub fn cf<T: serde::de::DeserializeOwned>(&self) -> Result<Option<T>> {
267        self.init
268            .cf
269            .clone()
270            .map(|cf| serde_wasm_bindgen::from_value(cf.unchecked_into()))
271            .transpose()
272            .map_err(Error::SerdeWasmBindgenError)
273    }
274
275    /// Set this response's `cf` options. This is used by consumers of the `Response` for
276    /// informational purposes and has no impact on Workers behavior.
277    pub fn with_cf<T: serde::Serialize>(mut self, cf: Option<T>) -> Result<Self> {
278        match cf {
279            Some(cf) => self.init = self.init.with_cf(cf)?,
280            None => self.init.cf = None,
281        }
282        Ok(self)
283    }
284
285    /// Read the `Headers` on this response.
286    pub fn headers(&self) -> &Headers {
287        &self.init.headers
288    }
289
290    /// Get a mutable reference to the `Headers` on this response.
291    pub fn headers_mut(&mut self) -> &mut Headers {
292        &mut self.init.headers
293    }
294
295    /// Split the response into `ResponseBuilder` and `ResponseBody` so that it
296    /// can be modified.
297    pub fn into_parts(self) -> (ResponseBuilder, ResponseBody) {
298        (self.init, self.body)
299    }
300
301    /// Clones the response so it can be used multiple times.
302    pub fn cloned(&mut self) -> Result<Self> {
303        if self.init.websocket.is_some() {
304            return Err(Error::RustError("WebSockets cannot be cloned".into()));
305        }
306
307        let edge = web_sys::Response::from(&*self);
308        let cloned = edge.clone()?;
309
310        // Cloning a response might modify it's body as it might need to tee the stream, so we'll
311        // need to update it.
312        self.body = match edge.body() {
313            Some(stream) => ResponseBody::Stream(stream),
314            None => ResponseBody::Empty,
315        };
316
317        let clone: Response = cloned.into();
318
319        Ok(clone.with_encode_body(*self.encode_body()))
320    }
321}
322
323#[test]
324fn no_using_invalid_error_status_code() {
325    assert!(Response::error("OK", 200).is_err());
326    assert!(Response::error("600", 600).is_err());
327    assert!(Response::error("399", 399).is_err());
328}
329
330#[non_exhaustive]
331#[derive(Default, Debug, Clone, Copy)]
332/// Control how the body of the response will be encoded by the runtime before
333/// it is returned to the user.
334pub enum EncodeBody {
335    /// Response body will be compressed according to the content-encoding header when transmitting.
336    /// This is the default.
337    #[default]
338    Automatic,
339    /// Response body will be returned as-is, allowing to return pre-compressed data.
340    /// The matching content-encoding header must be set manually.
341    Manual,
342}
343
344#[derive(Debug, Clone)]
345pub struct ResponseBuilder {
346    status_code: u16,
347    headers: Headers,
348    websocket: Option<WebSocket>,
349    encode_body: EncodeBody,
350    cf: Option<js_sys::Object>,
351}
352
353impl ResponseBuilder {
354    pub fn new() -> Self {
355        Self {
356            status_code: 200,
357            headers: Headers::new(),
358            websocket: None,
359            encode_body: EncodeBody::default(),
360            cf: None,
361        }
362    }
363
364    /// Set this response's status code.
365    /// The Workers platform will reject HTTP status codes outside the range of 200..599 inclusive,
366    /// and will throw a JavaScript `RangeError`, returning a response with an HTTP 500 status code.
367    pub fn with_status(mut self, status: u16) -> Self {
368        self.status_code = status;
369        self
370    }
371
372    /// Set this response's `Headers`.
373    pub fn with_headers(mut self, headers: Headers) -> Self {
374        self.headers = headers;
375        self
376    }
377
378    /// Set a single header on this response.
379    pub fn with_header(self, key: &str, value: &str) -> Result<Self> {
380        self.headers.set(key, value)?;
381        Ok(self)
382    }
383
384    /// Sets this response's cors headers from the `Cors` struct.
385    /// Example usage:
386    /// ```
387    /// let cors = Cors::default();
388    /// ResponseBuilder::new()
389    ///     .with_cors(&cors)
390    ///     .empty()
391    /// ```
392    pub fn with_cors(self, cors: &Cors) -> Result<Self> {
393        let mut headers = self.headers.clone();
394        cors.apply_headers(&mut headers)?;
395        Ok(self.with_headers(headers))
396    }
397
398    /// Sets this response's `webSocket` option.
399    /// This will require a status code 101 to work.
400    pub fn with_websocket(mut self, websocket: WebSocket) -> Self {
401        self.websocket = Some(websocket);
402        self
403    }
404
405    /// Set this response's `encodeBody` option.
406    /// In most cases this is not needed, but it can be set to "manual" to
407    /// return already compressed data to the user without re-compression.
408    pub fn with_encode_body(mut self, encode_body: EncodeBody) -> Self {
409        self.encode_body = encode_body;
410        self
411    }
412
413    /// Set this response's `cf` options. This is used by consumers of the `Response` for
414    /// informational purposes and has no impact on Workers behavior.
415    pub fn with_cf<T: serde::Serialize>(self, cf: T) -> Result<Self> {
416        let value = serde_wasm_bindgen::to_value(&cf)?;
417        if value.is_object() {
418            let obj = value.unchecked_into::<js_sys::Object>();
419            Ok(self.with_cf_raw(obj))
420        } else {
421            Err(Error::from("cf must be an object"))
422        }
423    }
424
425    pub(crate) fn with_cf_raw(mut self, obj: js_sys::Object) -> Self {
426        self.cf = Some(obj);
427        self
428    }
429
430    /// Build a response with a fixed-length body.
431    pub fn fixed(self, body: Vec<u8>) -> Response {
432        Response {
433            body: ResponseBody::Body(body),
434            init: self,
435        }
436    }
437
438    /// Build a response with a stream body.
439    pub fn stream(self, stream: ReadableStream) -> Response {
440        Response {
441            body: ResponseBody::Stream(stream),
442            init: self,
443        }
444    }
445
446    /// Build a response from a [`ResponseBody`].
447    pub fn body(self, body: ResponseBody) -> Response {
448        Response { body, init: self }
449    }
450
451    /// Build a response with an empty body.
452    pub fn empty(self) -> Response {
453        Response {
454            body: ResponseBody::Empty,
455            init: self,
456        }
457    }
458
459    /// Create a `Response` using `B` as the body encoded as JSON. Sets the associated
460    /// `Content-Type` header for the `Response` as `application/json`.
461    pub fn from_json<B: Serialize>(self, value: &B) -> Result<Response> {
462        if let Ok(data) = serde_json::to_string(value) {
463            self.headers.set(CONTENT_TYPE, "application/json")?;
464            Ok(self.fixed(data.into_bytes()))
465        } else {
466            Err(Error::Json(("Failed to encode data to json".into(), 500)))
467        }
468    }
469
470    /// Create a `Response` using the body encoded as HTML. Sets the associated `Content-Type`
471    /// header for the `Response` as `text/html; charset=utf-8`.
472    pub fn from_html(self, html: impl AsRef<str>) -> Result<Response> {
473        self.headers.set(CONTENT_TYPE, "text/html; charset=utf-8")?;
474        let data = html.as_ref().as_bytes().to_vec();
475        Ok(self.fixed(data))
476    }
477
478    /// Create a `Response` using unprocessed bytes provided. Sets the associated `Content-Type`
479    /// header for the `Response` as `application/octet-stream`.
480    pub fn from_bytes(self, bytes: Vec<u8>) -> Result<Response> {
481        self.headers.set(CONTENT_TYPE, "application/octet-stream")?;
482        Ok(self.fixed(bytes))
483    }
484
485    /// Create a `Response` using a [`Stream`](futures::stream::Stream) for the body. Sets a status
486    /// code of 200 and an empty set of Headers. Modify the Response with methods such as
487    /// `with_status` and `with_headers`.
488    pub fn from_stream<S>(self, stream: S) -> Result<Response>
489    where
490        S: TryStream + 'static,
491        S::Ok: Into<Vec<u8>>,
492        S::Error: Into<Error>,
493    {
494        let js_stream = stream
495            .map_ok(|item| -> Vec<u8> { item.into() })
496            .map_ok(|chunk| {
497                let array = Uint8Array::new_with_length(chunk.len() as _);
498                array.copy_from(&chunk);
499
500                array.into()
501            })
502            .map_err(|err| -> crate::Error { err.into() })
503            .map_err(|e| JsValue::from(e.to_string()));
504
505        let stream = wasm_streams::ReadableStream::from_stream(js_stream);
506        let stream: ReadableStream = stream.into_raw().dyn_into().unwrap();
507
508        Ok(self.stream(stream))
509    }
510
511    /// Create a `Response` using unprocessed text provided. Sets the associated `Content-Type`
512    /// header for the `Response` as `text/plain; charset=utf-8`.
513    pub fn ok(self, body: impl Into<String>) -> Result<Response> {
514        self.headers
515            .set(CONTENT_TYPE, "text/plain; charset=utf-8")?;
516
517        Ok(self.fixed(body.into().into_bytes()))
518    }
519
520    /// A helper method to send an error message to a client. Will return `Err` if the status code
521    /// provided is outside the valid HTTP error range of 400-599.
522    pub fn error(self, msg: impl Into<String>, status: u16) -> Result<Response> {
523        if !(400..=599).contains(&status) {
524            return Err(Error::Internal(
525                "error status codes must be in the 400-599 range! see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status for more".into(),
526            ));
527        }
528
529        Ok(self.with_status(status).fixed(msg.into().into_bytes()))
530    }
531}
532
533impl From<ResponseBuilder> for web_sys::ResponseInit {
534    fn from(init: ResponseBuilder) -> Self {
535        let mut edge_init = web_sys::ResponseInit::new();
536        edge_init.set_status(init.status_code);
537        edge_init.set_headers(&init.headers.0);
538        if let Some(websocket) = &init.websocket {
539            edge_init
540                .websocket(websocket.as_ref())
541                .expect("failed to set websocket");
542        }
543        if matches!(init.encode_body, EncodeBody::Manual) {
544            edge_init
545                .encode_body("manual")
546                .expect("failed to set encode_body");
547        }
548        if let Some(cf) = init.cf {
549            edge_init.cf(&cf).expect("failed to set cf");
550        }
551        edge_init
552    }
553}
554
555impl From<Response> for web_sys::Response {
556    fn from(res: Response) -> Self {
557        match res.body {
558            ResponseBody::Body(bytes) => {
559                let array = Uint8Array::new_with_length(bytes.len() as u32);
560                array.copy_from(&bytes);
561                web_sys::Response::new_with_opt_buffer_source_and_init(
562                    Some(&array),
563                    &res.init.into(),
564                )
565                .unwrap()
566            }
567            ResponseBody::Stream(stream) => {
568                web_sys::Response::new_with_opt_readable_stream_and_init(
569                    Some(&stream),
570                    &res.init.into(),
571                )
572                .unwrap()
573            }
574            ResponseBody::Empty => {
575                web_sys::Response::new_with_opt_str_and_init(None, &res.init.into()).unwrap()
576            }
577        }
578    }
579}
580
581impl From<&Response> for web_sys::Response {
582    fn from(res: &Response) -> Self {
583        let init = res.init.clone();
584        match &res.body {
585            ResponseBody::Body(bytes) => {
586                let array = Uint8Array::new_with_length(bytes.len() as u32);
587                array.copy_from(bytes);
588                web_sys::Response::new_with_opt_buffer_source_and_init(Some(&array), &init.into())
589                    .unwrap()
590            }
591            ResponseBody::Stream(stream) => {
592                web_sys::Response::new_with_opt_readable_stream_and_init(Some(stream), &init.into())
593                    .unwrap()
594            }
595            ResponseBody::Empty => {
596                web_sys::Response::new_with_opt_str_and_init(None, &init.into()).unwrap()
597            }
598        }
599    }
600}
601
602impl From<web_sys::Response> for Response {
603    fn from(res: web_sys::Response) -> Self {
604        let builder = ResponseBuilder {
605            headers: Headers(res.headers()),
606            status_code: res.status(),
607            websocket: res.websocket().map(|ws| ws.into()),
608            encode_body: EncodeBody::Automatic,
609            cf: res.cf(),
610        };
611        match res.body() {
612            Some(stream) => builder.stream(stream),
613            None => builder.empty(),
614        }
615    }
616}
617
618/// A trait used to represent any viable Response type that can be used in the Worker.
619/// The only requirement is that it be convertible to a web_sys::Response.
620pub trait IntoResponse {
621    fn into_raw(
622        self,
623    ) -> std::result::Result<web_sys::Response, impl Into<Box<dyn std::error::Error>>>;
624}
625
626impl IntoResponse for web_sys::Response {
627    fn into_raw(
628        self,
629    ) -> std::result::Result<web_sys::Response, impl Into<Box<dyn std::error::Error>>> {
630        Ok::<web_sys::Response, Error>(self)
631    }
632}
633
634impl IntoResponse for Response {
635    fn into_raw(
636        self,
637    ) -> std::result::Result<web_sys::Response, impl Into<Box<dyn std::error::Error>>> {
638        Ok::<web_sys::Response, Error>(self.into())
639    }
640}
641
642#[cfg(feature = "http")]
643impl<B> IntoResponse for http::Response<B>
644where
645    B: http_body::Body<Data = Bytes> + 'static,
646{
647    fn into_raw(
648        self,
649    ) -> std::result::Result<web_sys::Response, impl Into<Box<dyn std::error::Error>>> {
650        crate::http::response::to_wasm(self)
651    }
652}