ic_http_certification/http/
http_response.rs

1use crate::HeaderField;
2use candid::{
3    types::{Serializer, Type, TypeInner},
4    CandidType, Deserialize,
5};
6pub use http::StatusCode;
7use serde::Deserializer;
8use std::{borrow::Cow, fmt::Debug};
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11struct StatusCodeWrapper(StatusCode);
12
13impl CandidType for StatusCodeWrapper {
14    fn _ty() -> Type {
15        TypeInner::Nat16.into()
16    }
17
18    fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
19    where
20        S: Serializer,
21    {
22        self.0.as_u16().idl_serialize(serializer)
23    }
24}
25
26impl<'de> Deserialize<'de> for StatusCodeWrapper {
27    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
28    where
29        D: Deserializer<'de>,
30    {
31        u16::deserialize(deserializer).and_then(|status_code| {
32            StatusCode::from_u16(status_code)
33                .map(Into::into)
34                .map_err(|_| serde::de::Error::custom("Invalid HTTP Status Code."))
35        })
36    }
37}
38
39impl From<StatusCode> for StatusCodeWrapper {
40    fn from(status_code: StatusCode) -> Self {
41        Self(status_code)
42    }
43}
44
45/// A Candid-encodable representation of an HTTP response. This struct is used
46/// by the `http_request` method of the HTTP Gateway Protocol's Candid interface.
47///
48/// # Examples
49///
50/// ```
51/// use ic_http_certification::{HttpResponse, StatusCode};
52///
53/// let response = HttpResponse::builder()
54///     .with_status_code(StatusCode::OK)
55///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
56///     .with_body(b"Hello, World!")
57///     .with_upgrade(false)
58///     .build();
59///
60/// assert_eq!(response.status_code(), StatusCode::OK);
61/// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
62/// assert_eq!(response.body(), b"Hello, World!");
63/// assert_eq!(response.upgrade(), Some(false));
64/// ```
65///
66/// # Helpers
67///
68/// There are also a number of convenience methods for quickly creating an [HttpResponse] with
69/// commonly used HTTP status codes:
70///
71/// - [OK](HttpResponse::ok)
72/// - [CREATED](HttpResponse::created)
73/// - [NO_CONTENT](HttpResponse::no_content)
74/// - [MOVED_PERMANENTLY](HttpResponse::moved_permanently)
75/// - [TEMPORARY_REDIRECT](HttpResponse::temporary_redirect)
76/// - [BAD_REQUEST](HttpResponse::bad_request)
77/// - [UNAUTHORIZED](HttpResponse::unauthorized)
78/// - [FORBIDDEN](HttpResponse::forbidden)
79/// - [NOT_FOUND](HttpResponse::not_found)
80/// - [METHOD_NOT_ALLOWED](HttpResponse::method_not_allowed)
81/// - [TOO_MANY_REQUESTS](HttpResponse::too_many_requests)
82/// - [INTERNAL_SERVER_ERROR](HttpResponse::internal_server_error)
83///
84/// ```
85/// use ic_http_certification::{HttpResponse, StatusCode};
86///
87/// let response = HttpResponse::ok(b"Hello, World!", vec![("Content-Type".into(), "text/plain".into())]).build();
88///
89/// assert_eq!(response.status_code(), StatusCode::OK);
90/// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
91/// assert_eq!(response.body(), b"Hello, World!");
92/// ```
93#[derive(Clone, CandidType, Deserialize)]
94pub struct HttpResponse<'a> {
95    /// HTTP response status code.
96    status_code: StatusCodeWrapper,
97
98    /// HTTP response headers.
99    headers: Vec<HeaderField>,
100
101    /// HTTP response body as an array of bytes.
102    body: Cow<'a, [u8]>,
103
104    /// Whether the corresponding HTTP request should be upgraded to an update
105    /// call.
106    upgrade: Option<bool>,
107}
108
109impl<'a> HttpResponse<'a> {
110    /// Creates a new [HttpResponseBuilder] initialized with an OK status code and
111    /// the given body and headers.
112    ///
113    /// This method returns an instance of [HttpResponseBuilder] that can be used to
114    /// to create an [HttpResponse].
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// use ic_http_certification::{HttpResponse, StatusCode};
120    ///
121    /// let response = HttpResponse::ok(b"Hello, World!", vec![("Content-Type".into(), "text/plain".into())]).build();
122    ///
123    /// assert_eq!(response.status_code(), StatusCode::OK);
124    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
125    /// assert_eq!(response.body(), b"Hello, World!");
126    /// ```
127    pub fn ok(
128        body: impl Into<Cow<'a, [u8]>>,
129        headers: Vec<(String, String)>,
130    ) -> HttpResponseBuilder<'a> {
131        Self::builder()
132            .with_status_code(StatusCode::OK)
133            .with_body(body)
134            .with_headers(headers)
135    }
136
137    /// Creates a new [HttpResponseBuilder] initialized with a CREATED status code and
138    /// the given body and headers.
139    ///
140    /// This method returns an instance of [HttpResponseBuilder] that can be used to
141    /// to create an [HttpResponse].
142    ///
143    /// # Examples
144    ///
145    /// ```
146    /// use ic_http_certification::{HttpResponse, StatusCode};
147    ///
148    /// let response = HttpResponse::created(b"Hello, World!", vec![("Content-Type".into(), "text/plain".into())]).build();
149    ///
150    /// assert_eq!(response.status_code(), StatusCode::CREATED);
151    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
152    /// assert_eq!(response.body(), b"Hello, World!");
153    /// ```
154    pub fn created(
155        body: impl Into<Cow<'a, [u8]>>,
156        headers: Vec<(String, String)>,
157    ) -> HttpResponseBuilder<'a> {
158        Self::builder()
159            .with_status_code(StatusCode::CREATED)
160            .with_body(body)
161            .with_headers(headers)
162    }
163
164    /// Creates a new [HttpResponseBuilder] initialized with a NO_CONTENT status code and
165    /// the given headers.
166    ///
167    /// This method returns an instance of [HttpResponseBuilder] that can be used to
168    /// to create an [HttpResponse].
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use ic_http_certification::{HttpResponse, StatusCode};
174    ///
175    /// let response = HttpResponse::no_content(vec![("Content-Type".into(), "text/plain".into())]).build();
176    ///
177    /// assert_eq!(response.status_code(), StatusCode::NO_CONTENT);
178    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
179    /// ```
180    pub fn no_content(headers: Vec<(String, String)>) -> HttpResponseBuilder<'a> {
181        Self::builder()
182            .with_status_code(StatusCode::NO_CONTENT)
183            .with_headers(headers)
184    }
185
186    /// Creates a new [HttpResponseBuilder] initialized with a MOVED_PERMANENTLY status code and
187    /// the given location and headers.
188    ///
189    /// This method returns an instance of [HttpResponseBuilder] that can be used to
190    /// to create an [HttpResponse].
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use ic_http_certification::{HttpResponse, StatusCode};
196    ///
197    /// let response = HttpResponse::moved_permanently("https://www.example.com", vec![("Content-Type".into(), "text/plain".into())]).build();
198    ///
199    /// assert_eq!(response.status_code(), StatusCode::MOVED_PERMANENTLY);
200    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into()), ("Location".into(), "https://www.example.com".into())]);
201    /// ```
202    pub fn moved_permanently(
203        location: impl Into<String>,
204        headers: Vec<(String, String)>,
205    ) -> HttpResponseBuilder<'a> {
206        let headers = headers
207            .into_iter()
208            .chain(std::iter::once(("Location".into(), location.into())))
209            .collect();
210
211        Self::builder()
212            .with_status_code(StatusCode::MOVED_PERMANENTLY)
213            .with_headers(headers)
214    }
215
216    /// Creates a new [HttpResponseBuilder] initialized with a NOT_MODIFIED status code and
217    /// the given headers.
218    ///
219    /// This method returns an instance of [HttpResponseBuilder] that can be used to
220    /// to create an [HttpResponse].
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use ic_http_certification::{HttpResponse, StatusCode};
226    ///
227    /// let response = HttpResponse::not_modified(vec![("Content-Type".into(), "text/plain".into())]).build();
228    ///
229    /// assert_eq!(response.status_code(), StatusCode::NOT_MODIFIED);
230    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
231    /// ```
232    pub fn not_modified(headers: Vec<(String, String)>) -> HttpResponseBuilder<'a> {
233        Self::builder()
234            .with_status_code(StatusCode::NOT_MODIFIED)
235            .with_headers(headers)
236    }
237
238    /// Creates a new [HttpResponseBuilder] initialized with a TEMPORARY_REDIRECT status code and
239    /// the given location and headers.
240    ///
241    /// This method returns an instance of [HttpResponseBuilder] that can be used to
242    /// to create an [HttpResponse].
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use ic_http_certification::{HttpResponse, StatusCode};
248    ///
249    /// let response = HttpResponse::temporary_redirect("https://www.example.com", vec![("Content-Type".into(), "text/plain".into())]).build();
250    ///
251    /// assert_eq!(response.status_code(), StatusCode::TEMPORARY_REDIRECT);
252    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into()), ("Location".into(), "https://www.example.com".into())]);
253    /// ```
254    pub fn temporary_redirect(
255        location: impl Into<String>,
256        headers: Vec<(String, String)>,
257    ) -> HttpResponseBuilder<'a> {
258        let headers = headers
259            .into_iter()
260            .chain(std::iter::once(("Location".into(), location.into())))
261            .collect();
262
263        Self::builder()
264            .with_status_code(StatusCode::TEMPORARY_REDIRECT)
265            .with_headers(headers)
266    }
267
268    /// Creates a new [HttpResponseBuilder] initialized with a BAD_REQUEST status code and
269    /// the given body and headers.
270    ///
271    /// This method returns an instance of [HttpResponseBuilder] that can be used to
272    /// to create an [HttpResponse].
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// use ic_http_certification::{HttpResponse, StatusCode};
278    ///
279    /// let response = HttpResponse::bad_request(b"Bad Request", vec![("Content-Type".into(), "text/plain".into())]).build();
280    ///
281    /// assert_eq!(response.status_code(), StatusCode::BAD_REQUEST);
282    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
283    /// assert_eq!(response.body(), b"Bad Request");
284    /// ```
285    pub fn bad_request(
286        body: impl Into<Cow<'a, [u8]>>,
287        headers: Vec<(String, String)>,
288    ) -> HttpResponseBuilder<'a> {
289        Self::builder()
290            .with_status_code(StatusCode::BAD_REQUEST)
291            .with_body(body)
292            .with_headers(headers)
293    }
294
295    /// Creates a new [HttpResponseBuilder] initialized with an UNAUTHORIZED status code and
296    /// the given body and headers.
297    ///
298    /// This method returns an instance of [HttpResponseBuilder] that can be used to
299    /// to create an [HttpResponse].
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// use ic_http_certification::{HttpResponse, StatusCode};
305    ///
306    /// let response = HttpResponse::unauthorized(b"Unauthorized", vec![("Content-Type".into(), "text/plain".into())]).build();
307    ///
308    /// assert_eq!(response.status_code(), StatusCode::UNAUTHORIZED);
309    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
310    /// assert_eq!(response.body(), b"Unauthorized");
311    /// ```
312    pub fn unauthorized(
313        body: impl Into<Cow<'a, [u8]>>,
314        headers: Vec<(String, String)>,
315    ) -> HttpResponseBuilder<'a> {
316        Self::builder()
317            .with_status_code(StatusCode::UNAUTHORIZED)
318            .with_body(body)
319            .with_headers(headers)
320    }
321
322    /// Creates a new [HttpResponseBuilder] initialized with a FORBIDDEN status code and
323    /// the given body and headers.
324    ///
325    /// This method returns an instance of [HttpResponseBuilder] that can be used to
326    /// to create an [HttpResponse].
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// use ic_http_certification::{HttpResponse, StatusCode};
332    ///
333    /// let response = HttpResponse::forbidden(b"Forbidden", vec![("Content-Type".into(), "text/plain".into())]).build();
334    ///
335    /// assert_eq!(response.status_code(), StatusCode::FORBIDDEN);
336    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
337    /// assert_eq!(response.body(), b"Forbidden");
338    /// ```
339    pub fn forbidden(
340        body: impl Into<Cow<'a, [u8]>>,
341        headers: Vec<(String, String)>,
342    ) -> HttpResponseBuilder<'a> {
343        Self::builder()
344            .with_status_code(StatusCode::FORBIDDEN)
345            .with_body(body)
346            .with_headers(headers)
347    }
348
349    /// Creates a new [HttpResponseBuilder] initialized with a NOT_FOUND status code and
350    /// the given body and headers.
351    ///
352    /// This method returns an instance of [HttpResponseBuilder] that can be used to
353    /// to create an [HttpResponse].
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// use ic_http_certification::{HttpResponse, StatusCode};  
359    ///
360    /// let response = HttpResponse::not_found(b"Not Found", vec![("Content-Type".into(), "text/plain".into())]).build();
361    ///
362    /// assert_eq!(response.status_code(), StatusCode::NOT_FOUND);
363    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
364    /// assert_eq!(response.body(), b"Not Found");
365    /// ```
366    pub fn not_found(
367        body: impl Into<Cow<'a, [u8]>>,
368        headers: Vec<(String, String)>,
369    ) -> HttpResponseBuilder<'a> {
370        Self::builder()
371            .with_status_code(StatusCode::NOT_FOUND)
372            .with_body(body)
373            .with_headers(headers)
374    }
375
376    /// Creates a new [HttpResponseBuilder] initialized with a METHOD_NOT_ALLOWED status code and
377    /// the given body and headers.
378    ///
379    /// This method returns an instance of [HttpResponseBuilder] that can be used to
380    /// to create an [HttpResponse].
381    ///
382    /// # Examples
383    ///
384    /// ```
385    /// use ic_http_certification::{HttpResponse, StatusCode};
386    ///
387    /// let response = HttpResponse::method_not_allowed(b"Method Not Allowed", vec![("Content-Type".into(), "text/plain".into())]).build();
388    ///
389    /// assert_eq!(response.status_code(), StatusCode::METHOD_NOT_ALLOWED);
390    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
391    /// assert_eq!(response.body(), b"Method Not Allowed");
392    /// ```
393    pub fn method_not_allowed(
394        body: impl Into<Cow<'a, [u8]>>,
395        headers: Vec<(String, String)>,
396    ) -> HttpResponseBuilder<'a> {
397        Self::builder()
398            .with_status_code(StatusCode::METHOD_NOT_ALLOWED)
399            .with_body(body)
400            .with_headers(headers)
401    }
402
403    /// Creates a new [HttpResponseBuilder] initialized with a CONFLICT status code and
404    /// the given body and headers.
405    ///
406    /// This method returns an instance of [HttpResponseBuilder] that can be used to
407    /// to create an [HttpResponse].
408    ///
409    /// # Examples
410    ///
411    /// ```
412    /// use ic_http_certification::{HttpResponse, StatusCode};
413    ///
414    /// let response = HttpResponse::too_many_requests(b"Too many requests", vec![("Content-Type".into(), "text/plain".into())]).build();
415    ///
416    /// assert_eq!(response.status_code(), StatusCode::TOO_MANY_REQUESTS);
417    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
418    /// assert_eq!(response.body(), b"Too many requests");
419    /// ```
420    pub fn too_many_requests(
421        body: impl Into<Cow<'a, [u8]>>,
422        headers: Vec<(String, String)>,
423    ) -> HttpResponseBuilder<'a> {
424        Self::builder()
425            .with_status_code(StatusCode::TOO_MANY_REQUESTS)
426            .with_body(body)
427            .with_headers(headers)
428    }
429
430    /// Creates a new [HttpResponseBuilder] initialized with a INTERNAL_SERVER_ERROR status code and
431    /// the given body and headers.
432    ///
433    /// This method returns an instance of [HttpResponseBuilder] that can be used to
434    /// to create an [HttpResponse].
435    ///
436    /// # Examples
437    ///
438    /// ```
439    /// use ic_http_certification::{HttpResponse, StatusCode};
440    ///
441    /// let response = HttpResponse::internal_server_error(b"Internal Server Error", vec![("Content-Type".into(), "text/plain".into())]).build();
442    ///
443    /// assert_eq!(response.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
444    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
445    /// assert_eq!(response.body(), b"Internal Server Error");
446    /// ```
447    pub fn internal_server_error(
448        body: impl Into<Cow<'a, [u8]>>,
449        headers: Vec<(String, String)>,
450    ) -> HttpResponseBuilder<'a> {
451        Self::builder()
452            .with_status_code(StatusCode::INTERNAL_SERVER_ERROR)
453            .with_body(body)
454            .with_headers(headers)
455    }
456
457    /// Creates and returns an instance of [HttpResponseBuilder], a builder-style
458    /// object that can be used to construct an [HttpResponse].
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use ic_http_certification::{HttpResponse, StatusCode};
464    ///
465    /// let response = HttpResponse::builder()
466    ///     .with_status_code(StatusCode::OK)
467    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
468    ///     .with_body(b"Hello, World!")
469    ///     .with_upgrade(false)
470    ///     .build();
471    ///
472    /// assert_eq!(response.status_code(), StatusCode::OK);
473    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
474    /// assert_eq!(response.body(), b"Hello, World!");
475    /// assert_eq!(response.upgrade(), Some(false));
476    /// ```
477    #[inline]
478    pub fn builder() -> HttpResponseBuilder<'a> {
479        HttpResponseBuilder::new()
480    }
481
482    /// Returns the HTTP status code of the response.
483    ///
484    /// # Examples
485    ///
486    /// ```
487    /// use ic_http_certification::{HttpResponse, StatusCode};
488    ///
489    /// let response = HttpResponse::builder()
490    ///     .with_status_code(StatusCode::OK)
491    ///     .build();
492    ///
493    /// assert_eq!(response.status_code(), StatusCode::OK);
494    /// ```
495    #[inline]
496    pub fn status_code(&self) -> StatusCode {
497        self.status_code.0
498    }
499
500    /// Returns the HTTP headers of the response.
501    ///
502    /// # Examples
503    ///
504    /// ```
505    /// use ic_http_certification::HttpResponse;
506    ///
507    /// let response = HttpResponse::builder()
508    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
509    ///     .build();
510    ///
511    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
512    /// ```
513    #[inline]
514    pub fn headers(&self) -> &[HeaderField] {
515        &self.headers
516    }
517
518    /// Returns a mutable reference to the HTTP headers of the response.
519    ///
520    /// # Examples
521    ///
522    /// ```
523    /// use ic_http_certification::HttpResponse;
524    ///
525    /// let mut response = HttpResponse::builder()
526    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
527    ///     .build();
528    ///
529    /// response.headers_mut().push(("Content-Length".into(), "13".into()));
530    ///
531    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into()), ("Content-Length".into(), "13".into())]);
532    /// ```
533    #[inline]
534    pub fn headers_mut(&mut self) -> &mut Vec<HeaderField> {
535        &mut self.headers
536    }
537
538    /// Adds an additional header to the HTTP response.
539    ///
540    /// # Examples
541    ///
542    /// ```
543    /// use ic_http_certification::HttpResponse;
544    ///
545    /// let mut response = HttpResponse::builder()
546    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
547    ///     .build();
548    ///
549    /// response.add_header(("Content-Length".into(), "13".into()));
550    ///
551    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into()), ("Content-Length".into(), "13".into())]);
552    /// ```
553    #[inline]
554    pub fn add_header(&mut self, header: HeaderField) {
555        self.headers.push(header);
556    }
557
558    /// Returns the HTTP body of the response.
559    ///
560    /// # Examples
561    ///
562    /// ```
563    /// use ic_http_certification::HttpResponse;
564    ///
565    /// let response = HttpResponse::builder()
566    ///     .with_body(b"Hello, World!")
567    ///     .build();
568    ///
569    /// assert_eq!(response.body(), b"Hello, World!");
570    /// ```
571    #[inline]
572    pub fn body(&self) -> &[u8] {
573        &self.body
574    }
575
576    /// Returns the upgrade flag of the response. This will determine if the HTTP Gateway will
577    /// upgrade the request to an update call.
578    ///
579    /// # Examples
580    ///
581    /// ```
582    /// use ic_http_certification::HttpResponse;
583    ///
584    /// let response = HttpResponse::builder()
585    ///     .with_upgrade(true)
586    ///     .build();
587    ///
588    /// assert_eq!(response.upgrade(), Some(true));
589    /// ```
590    #[inline]
591    pub fn upgrade(&self) -> Option<bool> {
592        self.upgrade
593    }
594}
595
596/// An HTTP response builder.
597///
598/// This type can be used to construct an instance of an [HttpResponse] using a builder-like
599/// pattern.
600///
601/// # Examples
602///
603/// ```
604/// use ic_http_certification::{HttpResponse, StatusCode};
605///
606/// let response = HttpResponse::builder()
607///     .with_status_code(StatusCode::OK)
608///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
609///     .with_body(b"Hello, World!")
610///     .with_upgrade(false)
611///     .build();
612///
613/// assert_eq!(response.status_code(), StatusCode::OK);
614/// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
615/// assert_eq!(response.body(), b"Hello, World!");
616/// assert_eq!(response.upgrade(), Some(false));
617/// ```
618#[derive(Debug, Clone, Default)]
619pub struct HttpResponseBuilder<'a> {
620    status_code: Option<StatusCodeWrapper>,
621    headers: Vec<HeaderField>,
622    body: Cow<'a, [u8]>,
623    upgrade: Option<bool>,
624}
625
626impl<'a> HttpResponseBuilder<'a> {
627    /// Creates a new instance of the [HttpResponseBuilder] that can be used to
628    /// constract an [HttpResponse].
629    ///
630    /// # Examples
631    ///
632    /// ```
633    /// use ic_http_certification::{HttpResponse, StatusCode};
634    ///
635    /// let response = HttpResponse::builder()
636    ///     .with_status_code(StatusCode::OK)
637    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
638    ///     .with_body(b"Hello, World!")
639    ///     .with_upgrade(false)
640    ///     .build();
641    ///
642    /// assert_eq!(response.status_code(), StatusCode::OK);
643    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
644    /// assert_eq!(response.body(), b"Hello, World!");
645    /// assert_eq!(response.upgrade(), Some(false));
646    /// ```
647    pub fn new() -> Self {
648        Self::default()
649    }
650
651    /// Sets the status code of the HTTP response.
652    ///
653    /// By default, the status code will be set to `200`.
654    ///
655    /// # Examples
656    ///
657    /// ```
658    /// use ic_http_certification::{HttpResponse, StatusCode};
659    ///
660    /// let response = HttpResponse::builder()
661    ///     .with_status_code(StatusCode::OK)
662    ///     .build();
663    ///
664    /// assert_eq!(response.status_code(), StatusCode::OK);
665    /// ```
666    pub fn with_status_code(mut self, status_code: StatusCode) -> Self {
667        self.status_code = Some(status_code.into());
668
669        self
670    }
671
672    /// Sets the headers of the HTTP response.
673    ///
674    /// By default, the headers will be set to an empty array.
675    ///
676    /// # Examples
677    ///
678    /// ```
679    /// use ic_http_certification::HttpResponse;
680    ///
681    /// let response = HttpResponse::builder()
682    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
683    ///     .build();
684    ///
685    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
686    /// ```
687    pub fn with_headers(mut self, headers: Vec<HeaderField>) -> Self {
688        self.headers = headers;
689
690        self
691    }
692
693    /// Sets the body of the HTTP response.
694    ///
695    /// This function will accept both owned and borrowed values. By default,
696    /// the body will be set to an empty array.
697    ///
698    /// # Examples
699    ///
700    /// ```
701    /// use ic_http_certification::HttpResponse;
702    ///
703    /// let response = HttpResponse::builder()
704    ///     .with_body(b"Hello, World!")
705    ///     .build();
706    ///
707    /// assert_eq!(response.body(), b"Hello, World!");
708    /// ```
709    pub fn with_body(mut self, body: impl Into<Cow<'a, [u8]>>) -> Self {
710        self.body = body.into();
711
712        self
713    }
714
715    /// Sets the upgrade flag of the HTTP response. This will determine if the HTTP Gateway will
716    /// upgrade the request to an update call.
717    ///
718    /// By default, the upgrade flag will be set to `None`, which is the same as `Some(false)`.
719    ///
720    /// # Examples
721    ///
722    /// ```
723    /// use ic_http_certification::HttpResponse;
724    ///
725    /// let response = HttpResponse::builder()
726    ///     .with_upgrade(true)
727    ///     .build();
728    ///
729    /// assert_eq!(response.upgrade(), Some(true));
730    /// ```
731    pub fn with_upgrade(mut self, upgrade: bool) -> Self {
732        self.upgrade = Some(upgrade);
733
734        self
735    }
736
737    /// Build an [HttpResponse] from the builder.
738    ///
739    /// If the status code is not set, it will default to `200`.
740    /// If the upgrade flag is not set, it will default to `None`.
741    /// If the headers or body are not set, they will default to empty arrays.
742    ///
743    /// # Examples
744    ///
745    /// ```
746    /// use ic_http_certification::{HttpResponse, StatusCode};
747    ///
748    /// let response = HttpResponse::builder()
749    ///     .with_status_code(StatusCode::OK)
750    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
751    ///     .with_body(b"Hello, World!")
752    ///     .with_upgrade(false)
753    ///     .build();
754    ///
755    /// assert_eq!(response.status_code(), StatusCode::OK);
756    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
757    /// assert_eq!(response.body(), b"Hello, World!");
758    /// assert_eq!(response.upgrade(), Some(false));
759    /// ```
760    pub fn build(self) -> HttpResponse<'a> {
761        HttpResponse {
762            status_code: self.status_code.unwrap_or(StatusCode::OK.into()),
763            headers: self.headers,
764            body: self.body,
765            upgrade: self.upgrade,
766        }
767    }
768
769    /// Build an [HttpUpdateResponse] from the builder.
770    ///
771    /// If the status code is not set, it will default to `200`.
772    /// If the headers or body are not set, they will default to empty arrays.
773    ///
774    /// # Examples
775    ///
776    /// ```
777    /// use ic_http_certification::{HttpResponse, HttpUpdateResponse, StatusCode};
778    ///
779    /// let response = HttpResponse::builder()
780    ///     .with_status_code(StatusCode::OK)
781    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
782    ///     .with_body(b"Hello, World!")
783    ///     .build();
784    ///
785    /// let update_response = HttpUpdateResponse::from(response);
786    ///
787    /// assert_eq!(update_response.status_code(), StatusCode::OK);
788    /// assert_eq!(update_response.headers(), &[("Content-Type".into(), "text/plain".into())]);
789    /// assert_eq!(update_response.body(), b"Hello, World!");
790    /// ```
791    pub fn build_update(self) -> HttpUpdateResponse<'a> {
792        HttpUpdateResponse {
793            status_code: self.status_code.unwrap_or(StatusCode::OK.into()),
794            headers: self.headers,
795            body: self.body,
796        }
797    }
798}
799
800impl<'a> From<HttpResponse<'a>> for HttpResponseBuilder<'a> {
801    fn from(response: HttpResponse<'a>) -> Self {
802        Self {
803            status_code: Some(response.status_code),
804            headers: response.headers,
805            body: response.body,
806            upgrade: response.upgrade,
807        }
808    }
809}
810
811impl PartialEq for HttpResponse<'_> {
812    fn eq(&self, other: &Self) -> bool {
813        let mut a_headers = self.headers().to_vec();
814        a_headers.sort();
815
816        let mut b_headers = other.headers().to_vec();
817        b_headers.sort();
818
819        self.status_code == other.status_code
820            && a_headers == b_headers
821            && self.body == other.body
822            && self.upgrade == other.upgrade
823    }
824}
825
826impl Debug for HttpResponse<'_> {
827    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
828        // Truncate body to 100 characters for debug output
829        let max_body_len = 100;
830        let formatted_body = if self.body.len() > max_body_len {
831            format!("{:?}...", &self.body[..max_body_len])
832        } else {
833            format!("{:?}", &self.body)
834        };
835
836        f.debug_struct("HttpResponse")
837            .field("status_code", &self.status_code)
838            .field("headers", &self.headers)
839            .field("body", &formatted_body)
840            .field("upgrade", &self.upgrade)
841            .finish()
842    }
843}
844
845/// A Candid-encodable representation of an HTTP update response. This struct is used
846/// by the `http_update_request` method of the HTTP Gateway Protocol.
847///
848/// This is the same as [HttpResponse], excluding the
849/// [upgrade](HttpResponse::upgrade) field.
850///
851/// # Examples
852///
853/// ```
854/// use ic_http_certification::{HttpResponse, HttpUpdateResponse, StatusCode};
855///
856/// let response = HttpResponse::builder()
857///     .with_status_code(StatusCode::OK)
858///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
859///     .with_body(b"Hello, World!")
860///     .build_update();
861///
862/// let update_response = HttpUpdateResponse::from(response);
863///
864/// assert_eq!(update_response.status_code(), StatusCode::OK);
865/// assert_eq!(update_response.headers(), &[("Content-Type".into(), "text/plain".into())]);
866/// assert_eq!(update_response.body(), b"Hello, World!");
867/// ```
868#[derive(Clone, Debug, CandidType, Deserialize, PartialEq, Eq)]
869pub struct HttpUpdateResponse<'a> {
870    /// HTTP response status code.
871    status_code: StatusCodeWrapper,
872
873    /// HTTP response headers.
874    headers: Vec<HeaderField>,
875
876    /// HTTP response body as an array of bytes.
877    body: Cow<'a, [u8]>,
878}
879
880impl<'a> HttpUpdateResponse<'a> {
881    /// Returns the HTTP status code of the response.
882    ///
883    /// # Examples
884    ///
885    /// ```
886    /// use ic_http_certification::{HttpResponse, StatusCode};
887    ///
888    /// let response = HttpResponse::builder()
889    ///     .with_status_code(StatusCode::OK)
890    ///     .build_update();
891    ///
892    /// assert_eq!(response.status_code(), StatusCode::OK);
893    /// ```
894    #[inline]
895    pub fn status_code(&self) -> StatusCode {
896        self.status_code.0
897    }
898
899    /// Returns the HTTP headers of the response.
900    ///
901    /// # Examples
902    ///
903    /// ```
904    /// use ic_http_certification::HttpResponse;
905    ///
906    /// let response = HttpResponse::builder()
907    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
908    ///     .build_update();
909    ///
910    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into())]);
911    /// ```
912    #[inline]
913    pub fn headers(&self) -> &[HeaderField] {
914        &self.headers
915    }
916
917    /// Returns a mutable reference to the HTTP headers of the response.
918    ///
919    /// # Examples
920    ///
921    /// ```
922    /// use ic_http_certification::HttpResponse;
923    ///
924    /// let mut response = HttpResponse::builder()
925    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
926    ///     .build_update();
927    ///
928    /// response.headers_mut().push(("Content-Length".into(), "13".into()));
929    ///
930    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into()), ("Content-Length".into(), "13".into())]);
931    /// ```
932    #[inline]
933    pub fn headers_mut(&mut self) -> &mut Vec<HeaderField> {
934        &mut self.headers
935    }
936
937    /// Adds an additional header to the HTTP response.
938    ///
939    /// # Examples
940    ///
941    /// ```
942    /// use ic_http_certification::HttpResponse;
943    ///
944    /// let mut response = HttpResponse::builder()
945    ///     .with_headers(vec![("Content-Type".into(), "text/plain".into())])
946    ///     .build_update();
947    ///
948    /// response.add_header(("Content-Length".into(), "13".into()));
949    ///
950    /// assert_eq!(response.headers(), &[("Content-Type".into(), "text/plain".into()), ("Content-Length".into(), "13".into())]);
951    /// ```
952    #[inline]
953    pub fn add_header(&mut self, header: HeaderField) {
954        self.headers.push(header);
955    }
956
957    /// Returns the HTTP body of the response.
958    ///
959    /// # Examples
960    ///
961    /// ```
962    /// use ic_http_certification::HttpResponse;
963    ///
964    /// let response = HttpResponse::builder()
965    ///     .with_body(b"Hello, World!")
966    ///     .build_update();
967    ///
968    /// assert_eq!(response.body(), b"Hello, World!");
969    /// ```
970    #[inline]
971    pub fn body(&self) -> &[u8] {
972        &self.body
973    }
974}
975
976impl<'a> From<HttpResponse<'a>> for HttpUpdateResponse<'a> {
977    fn from(response: HttpResponse<'a>) -> Self {
978        Self {
979            status_code: response.status_code,
980            headers: response.headers,
981            body: response.body,
982        }
983    }
984}