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}