rouille/response.rs
1// Copyright (c) 2016 The Rouille developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10use percent_encoding;
11use serde;
12use serde_json;
13use std::borrow::Cow;
14use std::fmt;
15use std::fs::File;
16use std::io;
17use std::io::Cursor;
18use std::io::Read;
19use Request;
20use Upgrade;
21
22/// Contains a prototype of a response.
23///
24/// The response is only sent to the client when you return the `Response` object from your
25/// request handler. This means that you are free to create as many `Response` objects as you want.
26pub struct Response {
27 /// The status code to return to the user.
28 pub status_code: u16,
29
30 /// List of headers to be returned in the response.
31 ///
32 /// The value of the following headers will be ignored from this list, even if present:
33 ///
34 /// - Accept-Ranges
35 /// - Connection
36 /// - Content-Length
37 /// - Content-Range
38 /// - Trailer
39 /// - Transfer-Encoding
40 ///
41 /// Additionally, the `Upgrade` header is ignored as well unless the `upgrade` field of the
42 /// `Response` is set to something.
43 ///
44 /// The reason for this is that these headers are too low-level and are directly handled by
45 /// the underlying HTTP response system.
46 ///
47 /// The value of `Content-Length` is automatically determined by the `ResponseBody` object of
48 /// the `data` member.
49 ///
50 /// If you want to send back `Connection: upgrade`, you should set the value of the `upgrade`
51 /// field to something.
52 pub headers: Vec<(Cow<'static, str>, Cow<'static, str>)>,
53
54 /// An opaque type that contains the body of the response.
55 pub data: ResponseBody,
56
57 /// If set, rouille will give ownership of the client socket to the `Upgrade` object.
58 ///
59 /// In all circumstances, the value of the `Connection` header is managed by the framework and
60 /// cannot be customized. If this value is set, the response will automatically contain
61 /// `Connection: Upgrade`.
62 pub upgrade: Option<Box<dyn Upgrade + Send>>,
63}
64
65impl fmt::Debug for Response {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 f.debug_struct("Response")
68 .field("status_code", &self.status_code)
69 .field("headers", &self.headers)
70 .finish()
71 }
72}
73
74impl Response {
75 /// Returns true if the status code of this `Response` indicates success.
76 ///
77 /// This is the range [200-399].
78 ///
79 /// # Example
80 ///
81 /// ```
82 /// use rouille::Response;
83 /// let response = Response::text("hello world");
84 /// assert!(response.is_success());
85 /// ```
86 #[inline]
87 pub fn is_success(&self) -> bool {
88 self.status_code >= 200 && self.status_code < 400
89 }
90
91 /// Shortcut for `!response.is_success()`.
92 ///
93 /// # Example
94 ///
95 /// ```
96 /// use rouille::Response;
97 /// let response = Response::empty_400();
98 /// assert!(response.is_error());
99 /// ```
100 #[inline]
101 pub fn is_error(&self) -> bool {
102 !self.is_success()
103 }
104
105 /// Builds a `Response` that redirects the user to another URL with a 301 status code. This
106 /// semantically means a permanent redirect.
107 ///
108 /// > **Note**: If you're uncertain about which status code to use for a redirection, 303 is
109 /// > the safest choice.
110 ///
111 /// # Example
112 ///
113 /// ```
114 /// use rouille::Response;
115 /// let response = Response::redirect_301("/foo");
116 /// ```
117 #[inline]
118 pub fn redirect_301<S>(target: S) -> Response
119 where
120 S: Into<Cow<'static, str>>,
121 {
122 Response {
123 status_code: 301,
124 headers: vec![("Location".into(), target.into())],
125 data: ResponseBody::empty(),
126 upgrade: None,
127 }
128 }
129
130 /// Builds a `Response` that redirects the user to another URL with a 302 status code. This
131 /// semantically means a temporary redirect.
132 ///
133 /// > **Note**: If you're uncertain about which status code to use for a redirection, 303 is
134 /// > the safest choice.
135 ///
136 /// # Example
137 ///
138 /// ```
139 /// use rouille::Response;
140 /// let response = Response::redirect_302("/bar");
141 /// ```
142 #[inline]
143 pub fn redirect_302<S>(target: S) -> Response
144 where
145 S: Into<Cow<'static, str>>,
146 {
147 Response {
148 status_code: 302,
149 headers: vec![("Location".into(), target.into())],
150 data: ResponseBody::empty(),
151 upgrade: None,
152 }
153 }
154
155 /// Builds a `Response` that redirects the user to another URL with a 303 status code. This
156 /// means "See Other" and is usually used to indicate where the response of a query is
157 /// located.
158 ///
159 /// For example when a user sends a POST request to URL `/foo` the server can return a 303
160 /// response with a target to `/bar`, in which case the browser will automatically change
161 /// the page to `/bar` (with a GET request to `/bar`).
162 ///
163 /// > **Note**: If you're uncertain about which status code to use for a redirection, 303 is
164 /// > the safest choice.
165 ///
166 /// # Example
167 ///
168 /// ```
169 /// use rouille::Response;
170 /// let user_id = 5;
171 /// let response = Response::redirect_303(format!("/users/{}", user_id));
172 /// ```
173 #[inline]
174 pub fn redirect_303<S>(target: S) -> Response
175 where
176 S: Into<Cow<'static, str>>,
177 {
178 Response {
179 status_code: 303,
180 headers: vec![("Location".into(), target.into())],
181 data: ResponseBody::empty(),
182 upgrade: None,
183 }
184 }
185
186 /// Builds a `Response` that redirects the user to another URL with a 307 status code. This
187 /// semantically means a permanent redirect.
188 ///
189 /// The difference between 307 and 301 is that the client must keep the same method after
190 /// the redirection. For example if the browser sends a POST request to `/foo` and that route
191 /// returns a 307 redirection to `/bar`, then the browser will make a POST request to `/bar`.
192 /// With a 301 redirection it would use a GET request instead.
193 ///
194 /// > **Note**: If you're uncertain about which status code to use for a redirection, 303 is
195 /// > the safest choice.
196 ///
197 /// # Example
198 ///
199 /// ```
200 /// use rouille::Response;
201 /// let response = Response::redirect_307("/foo");
202 /// ```
203 #[inline]
204 pub fn redirect_307<S>(target: S) -> Response
205 where
206 S: Into<Cow<'static, str>>,
207 {
208 Response {
209 status_code: 307,
210 headers: vec![("Location".into(), target.into())],
211 data: ResponseBody::empty(),
212 upgrade: None,
213 }
214 }
215
216 /// Builds a `Response` that redirects the user to another URL with a 302 status code. This
217 /// semantically means a temporary redirect.
218 ///
219 /// The difference between 308 and 302 is that the client must keep the same method after
220 /// the redirection. For example if the browser sends a POST request to `/foo` and that route
221 /// returns a 308 redirection to `/bar`, then the browser will make a POST request to `/bar`.
222 /// With a 302 redirection it would use a GET request instead.
223 ///
224 /// > **Note**: If you're uncertain about which status code to use for a redirection, 303 is
225 /// > the safest choice.
226 ///
227 /// # Example
228 ///
229 /// ```
230 /// use rouille::Response;
231 /// let response = Response::redirect_302("/bar");
232 /// ```
233 #[inline]
234 pub fn redirect_308<S>(target: S) -> Response
235 where
236 S: Into<Cow<'static, str>>,
237 {
238 Response {
239 status_code: 308,
240 headers: vec![("Location".into(), target.into())],
241 data: ResponseBody::empty(),
242 upgrade: None,
243 }
244 }
245
246 /// Builds a 200 `Response` with data.
247 ///
248 /// # Example
249 ///
250 /// ```
251 /// use rouille::Response;
252 /// let response = Response::from_data("application/octet-stream", vec![1, 2, 3, 4]);
253 /// ```
254 #[inline]
255 pub fn from_data<C, D>(content_type: C, data: D) -> Response
256 where
257 C: Into<Cow<'static, str>>,
258 D: Into<Vec<u8>>,
259 {
260 Response {
261 status_code: 200,
262 headers: vec![("Content-Type".into(), content_type.into())],
263 data: ResponseBody::from_data(data),
264 upgrade: None,
265 }
266 }
267
268 /// Builds a 200 `Response` with the content of a file.
269 ///
270 /// # Example
271 ///
272 /// ```no_run
273 /// use std::fs::File;
274 /// use rouille::Response;
275 ///
276 /// let file = File::open("image.png").unwrap();
277 /// let response = Response::from_file("image/png", file);
278 /// ```
279 #[inline]
280 pub fn from_file<C>(content_type: C, file: File) -> Response
281 where
282 C: Into<Cow<'static, str>>,
283 {
284 Response {
285 status_code: 200,
286 headers: vec![("Content-Type".into(), content_type.into())],
287 data: ResponseBody::from_file(file),
288 upgrade: None,
289 }
290 }
291
292 /// Builds a `Response` that outputs HTML.
293 ///
294 /// # Example
295 ///
296 /// ```
297 /// use rouille::Response;
298 /// let response = Response::html("<p>hello <strong>world</strong></p>");
299 /// ```
300 #[inline]
301 pub fn html<D>(content: D) -> Response
302 where
303 D: Into<String>,
304 {
305 Response {
306 status_code: 200,
307 headers: vec![("Content-Type".into(), "text/html; charset=utf-8".into())],
308 data: ResponseBody::from_string(content),
309 upgrade: None,
310 }
311 }
312
313 /// Builds a `Response` that outputs SVG.
314 ///
315 /// # Example
316 ///
317 /// ```
318 /// use rouille::Response;
319 /// let response = Response::svg("<svg xmlns='http://www.w3.org/2000/svg'/>");
320 /// ```
321 #[inline]
322 pub fn svg<D>(content: D) -> Response
323 where
324 D: Into<String>,
325 {
326 Response {
327 status_code: 200,
328 headers: vec![("Content-Type".into(), "image/svg+xml; charset=utf-8".into())],
329 data: ResponseBody::from_string(content),
330 upgrade: None,
331 }
332 }
333
334 /// Builds a `Response` that outputs plain text.
335 ///
336 /// # Example
337 ///
338 /// ```
339 /// use rouille::Response;
340 /// let response = Response::text("hello world");
341 /// ```
342 #[inline]
343 pub fn text<S>(text: S) -> Response
344 where
345 S: Into<String>,
346 {
347 Response {
348 status_code: 200,
349 headers: vec![("Content-Type".into(), "text/plain; charset=utf-8".into())],
350 data: ResponseBody::from_string(text),
351 upgrade: None,
352 }
353 }
354
355 /// Builds a `Response` that outputs JSON.
356 ///
357 /// # Example
358 ///
359 /// ```
360 /// extern crate serde;
361 /// #[macro_use] extern crate serde_derive;
362 /// #[macro_use] extern crate rouille;
363 /// use rouille::Response;
364 /// # fn main() {
365 ///
366 /// #[derive(Serialize)]
367 /// struct MyStruct {
368 /// field1: String,
369 /// field2: i32,
370 /// }
371 ///
372 /// let response = Response::json(&MyStruct { field1: "hello".to_owned(), field2: 5 });
373 /// // The Response will contain something like `{ field1: "hello", field2: 5 }`
374 /// # }
375 /// ```
376 #[inline]
377 pub fn json<T>(content: &T) -> Response
378 where
379 T: serde::Serialize,
380 {
381 let data = serde_json::to_string(content).unwrap();
382
383 Response {
384 status_code: 200,
385 headers: vec![(
386 "Content-Type".into(),
387 "application/json; charset=utf-8".into(),
388 )],
389 data: ResponseBody::from_data(data),
390 upgrade: None,
391 }
392 }
393
394 /// Builds a `Response` that returns a `401 Not Authorized` status
395 /// and a `WWW-Authenticate` header.
396 ///
397 /// # Example
398 ///
399 /// ```
400 /// use rouille::Response;
401 /// let response = Response::basic_http_auth_login_required("realm");
402 /// ```
403 #[inline]
404 pub fn basic_http_auth_login_required(realm: &str) -> Response {
405 // TODO: escape the realm
406 Response {
407 status_code: 401,
408 headers: vec![(
409 "WWW-Authenticate".into(),
410 format!("Basic realm=\"{}\"", realm).into(),
411 )],
412 data: ResponseBody::empty(),
413 upgrade: None,
414 }
415 }
416
417 /// Builds an empty `Response` with a 204 status code.
418 ///
419 /// # Example
420 ///
421 /// ```
422 /// use rouille::Response;
423 /// let response = Response::empty_204();
424 /// ```
425 #[inline]
426 pub fn empty_204() -> Response {
427 Response {
428 status_code: 204,
429 headers: vec![],
430 data: ResponseBody::empty(),
431 upgrade: None,
432 }
433 }
434
435 /// Builds an empty `Response` with a 400 status code.
436 ///
437 /// # Example
438 ///
439 /// ```
440 /// use rouille::Response;
441 /// let response = Response::empty_400();
442 /// ```
443 #[inline]
444 pub fn empty_400() -> Response {
445 Response {
446 status_code: 400,
447 headers: vec![],
448 data: ResponseBody::empty(),
449 upgrade: None,
450 }
451 }
452
453 /// Builds an empty `Response` with a 404 status code.
454 ///
455 /// # Example
456 ///
457 /// ```
458 /// use rouille::Response;
459 /// let response = Response::empty_404();
460 /// ```
461 #[inline]
462 pub fn empty_404() -> Response {
463 Response {
464 status_code: 404,
465 headers: vec![],
466 data: ResponseBody::empty(),
467 upgrade: None,
468 }
469 }
470
471 /// Builds an empty `Response` with a 406 status code.
472 ///
473 /// # Example
474 ///
475 /// ```
476 /// use rouille::Response;
477 /// let response = Response::empty_406();
478 /// ```
479 #[inline]
480 pub fn empty_406() -> Response {
481 Response {
482 status_code: 406,
483 headers: vec![],
484 data: ResponseBody::empty(),
485 upgrade: None,
486 }
487 }
488
489 /// Changes the status code of the response.
490 ///
491 /// # Example
492 ///
493 /// ```
494 /// use rouille::Response;
495 /// let response = Response::text("hello world").with_status_code(500);
496 /// ```
497 #[inline]
498 pub fn with_status_code(mut self, code: u16) -> Response {
499 self.status_code = code;
500 self
501 }
502
503 /// Removes all headers from the response that match `header`.
504 pub fn without_header(mut self, header: &str) -> Response {
505 self.headers
506 .retain(|&(ref h, _)| !h.eq_ignore_ascii_case(header));
507 self
508 }
509
510 /// Adds an additional header to the response.
511 #[inline]
512 pub fn with_additional_header<H, V>(mut self, header: H, value: V) -> Response
513 where
514 H: Into<Cow<'static, str>>,
515 V: Into<Cow<'static, str>>,
516 {
517 self.headers.push((header.into(), value.into()));
518 self
519 }
520
521 /// Removes all headers from the response whose names are `header`, and replaces them .
522 pub fn with_unique_header<H, V>(mut self, header: H, value: V) -> Response
523 where
524 H: Into<Cow<'static, str>>,
525 V: Into<Cow<'static, str>>,
526 {
527 // If Vec::retain provided a mutable reference this code would be much simpler and would
528 // only need to iterate once.
529 // See https://github.com/rust-lang/rust/issues/25477
530
531 // TODO: if the response already has a matching header we shouldn't have to build a Cow
532 // from the header
533
534 let header = header.into();
535
536 let mut found_one = false;
537 self.headers.retain(|&(ref h, _)| {
538 if h.eq_ignore_ascii_case(&header) {
539 if !found_one {
540 found_one = true;
541 true
542 } else {
543 false
544 }
545 } else {
546 true
547 }
548 });
549
550 if found_one {
551 for &mut (ref h, ref mut v) in &mut self.headers {
552 if !h.eq_ignore_ascii_case(&header) {
553 continue;
554 }
555 *v = value.into();
556 break;
557 }
558 self
559 } else {
560 self.with_additional_header(header, value)
561 }
562 }
563
564 /// Adds or replaces a `ETag` header to the response, and turns the response into an empty 304
565 /// response if the ETag matches a `If-None-Match` header of the request.
566 ///
567 /// An ETag is a unique representation of the content of a resource. If the content of the
568 /// resource changes, the ETag should change as well.
569 /// The purpose of using ETags is that a client can later ask the server to send the body of
570 /// a response only if it still matches a certain ETag the client has stored in memory.
571 ///
572 /// > **Note**: You should always try to specify an ETag for responses that have a large body.
573 ///
574 /// # Example
575 ///
576 /// ```rust
577 /// use rouille::Request;
578 /// use rouille::Response;
579 ///
580 /// fn handle(request: &Request) -> Response {
581 /// Response::text("hello world").with_etag(request, "my-etag-1234")
582 /// }
583 /// ```
584 #[inline]
585 pub fn with_etag<E>(self, request: &Request, etag: E) -> Response
586 where
587 E: Into<Cow<'static, str>>,
588 {
589 self.with_etag_keep(etag).simplify_if_etag_match(request)
590 }
591
592 /// Turns the response into an empty 304 response if the `ETag` that is stored in it matches a
593 /// `If-None-Match` header of the request.
594 pub fn simplify_if_etag_match(mut self, request: &Request) -> Response {
595 if self.status_code < 200 || self.status_code >= 300 {
596 return self;
597 }
598
599 let mut not_modified = false;
600 for &(ref key, ref etag) in &self.headers {
601 if !key.eq_ignore_ascii_case("ETag") {
602 continue;
603 }
604
605 not_modified = request
606 .header("If-None-Match")
607 .map(|header| header == etag)
608 .unwrap_or(false);
609 }
610
611 if not_modified {
612 self.data = ResponseBody::empty();
613 self.status_code = 304;
614 }
615
616 self
617 }
618
619 /// Adds a `ETag` header to the response, or replaces an existing header if there is one.
620 ///
621 /// > **Note**: Contrary to `with_etag`, this function doesn't try to turn the response into
622 /// > a 304 response. If you're unsure of what to do, prefer `with_etag`.
623 #[inline]
624 pub fn with_etag_keep<E>(self, etag: E) -> Response
625 where
626 E: Into<Cow<'static, str>>,
627 {
628 self.with_unique_header("ETag", etag)
629 }
630
631 /// Adds or replace a `Content-Disposition` header of the response. Tells the browser that the
632 /// body of the request should fire a download popup instead of being shown in the browser.
633 ///
634 /// # Example
635 ///
636 /// ```rust
637 /// use rouille::Request;
638 /// use rouille::Response;
639 ///
640 /// fn handle(request: &Request) -> Response {
641 /// Response::text("hello world").with_content_disposition_attachment("book.txt")
642 /// }
643 /// ```
644 ///
645 /// When the response is sent back to the browser, it will show a popup asking the user to
646 /// download the file "book.txt" whose content will be "hello world".
647 pub fn with_content_disposition_attachment(mut self, filename: &str) -> Response {
648 // The name must be percent-encoded.
649 let name = percent_encoding::percent_encode(filename.as_bytes(), super::DEFAULT_ENCODE_SET);
650
651 // If you find a more elegant way to do the thing below, don't hesitate to open a PR
652
653 // Support for this format varies browser by browser, so this may not be the most
654 // ideal thing.
655 // TODO: it's maybe possible to specify multiple file names
656 let mut header = Some(format!("attachment; filename*=UTF8''{}", name).into());
657
658 for &mut (ref key, ref mut val) in &mut self.headers {
659 if key.eq_ignore_ascii_case("Content-Disposition") {
660 *val = header.take().unwrap();
661 break;
662 }
663 }
664
665 if let Some(header) = header {
666 self.headers.push(("Content-Disposition".into(), header));
667 }
668
669 self
670 }
671
672 /// Adds or replaces a `Cache-Control` header that specifies that the resource is public and
673 /// can be cached for the given number of seconds.
674 ///
675 /// > **Note**: This function doesn't do any caching itself. It just indicates that clients
676 /// > that receive this response are allowed to cache it.
677 #[inline]
678 pub fn with_public_cache(self, max_age_seconds: u64) -> Response {
679 self.with_unique_header(
680 "Cache-Control",
681 format!("public, max-age={}", max_age_seconds),
682 )
683 .without_header("Expires")
684 .without_header("Pragma")
685 }
686
687 /// Adds or replaces a `Cache-Control` header that specifies that the resource is private and
688 /// can be cached for the given number of seconds.
689 ///
690 /// Only the browser or the final client is authorized to cache the resource. Intermediate
691 /// proxies must not cache it.
692 ///
693 /// > **Note**: This function doesn't do any caching itself. It just indicates that clients
694 /// > that receive this response are allowed to cache it.
695 #[inline]
696 pub fn with_private_cache(self, max_age_seconds: u64) -> Response {
697 self.with_unique_header(
698 "Cache-Control",
699 format!("private, max-age={}", max_age_seconds),
700 )
701 .without_header("Expires")
702 .without_header("Pragma")
703 }
704
705 /// Adds or replaces a `Cache-Control` header that specifies that the client must not cache
706 /// the resource.
707 #[inline]
708 pub fn with_no_cache(self) -> Response {
709 self.with_unique_header("Cache-Control", "no-cache, no-store, must-revalidate")
710 .with_unique_header("Expires", "0")
711 .with_unique_header("Pragma", "no-cache")
712 }
713}
714
715/// An opaque type that represents the body of a response.
716///
717/// You can't access the inside of this struct, but you can build one by using one of the provided
718/// constructors.
719///
720/// # Example
721///
722/// ```
723/// use rouille::ResponseBody;
724/// let body = ResponseBody::from_string("hello world");
725/// ```
726pub struct ResponseBody {
727 data: Box<dyn Read + Send>,
728 data_length: Option<usize>,
729}
730
731impl ResponseBody {
732 /// Builds a `ResponseBody` that doesn't return any data.
733 ///
734 /// # Example
735 ///
736 /// ```
737 /// use rouille::ResponseBody;
738 /// let body = ResponseBody::empty();
739 /// ```
740 #[inline]
741 pub fn empty() -> ResponseBody {
742 ResponseBody {
743 data: Box::new(io::empty()),
744 data_length: Some(0),
745 }
746 }
747
748 /// Builds a new `ResponseBody` that will read the data from a `Read`.
749 ///
750 /// Note that this is suboptimal compared to other constructors because the length
751 /// isn't known in advance.
752 ///
753 /// # Example
754 ///
755 /// ```no_run
756 /// use std::io;
757 /// use std::io::Read;
758 /// use rouille::ResponseBody;
759 ///
760 /// let body = ResponseBody::from_reader(io::stdin().take(128));
761 /// ```
762 #[inline]
763 pub fn from_reader<R>(data: R) -> ResponseBody
764 where
765 R: Read + Send + 'static,
766 {
767 ResponseBody {
768 data: Box::new(data),
769 data_length: None,
770 }
771 }
772
773 /// Builds a new `ResponseBody` that will read the data from a `Read`.
774 ///
775 /// The caller must provide the content length. It is unspecified
776 /// what will happen if the content length does not match the actual
777 /// length of the data returned from the reader.
778 ///
779 /// # Example
780 ///
781 /// ```no_run
782 /// use std::io;
783 /// use std::io::Read;
784 /// use rouille::ResponseBody;
785 ///
786 /// let body = ResponseBody::from_reader_and_size(io::stdin().take(128), 128);
787 /// ```
788 #[inline]
789 pub fn from_reader_and_size<R>(data: R, size: usize) -> ResponseBody
790 where
791 R: Read + Send + 'static,
792 {
793 ResponseBody {
794 data: Box::new(data),
795 data_length: Some(size),
796 }
797 }
798
799 /// Builds a new `ResponseBody` that returns the given data.
800 ///
801 /// # Example
802 ///
803 /// ```
804 /// use rouille::ResponseBody;
805 /// let body = ResponseBody::from_data(vec![12u8, 97, 34]);
806 /// ```
807 #[inline]
808 pub fn from_data<D>(data: D) -> ResponseBody
809 where
810 D: Into<Vec<u8>>,
811 {
812 let data = data.into();
813 let len = data.len();
814
815 ResponseBody {
816 data: Box::new(Cursor::new(data)),
817 data_length: Some(len),
818 }
819 }
820
821 /// Builds a new `ResponseBody` that returns the content of the given file.
822 ///
823 /// # Example
824 ///
825 /// ```no_run
826 /// use std::fs::File;
827 /// use rouille::ResponseBody;
828 ///
829 /// let file = File::open("page.html").unwrap();
830 /// let body = ResponseBody::from_file(file);
831 /// ```
832 #[inline]
833 pub fn from_file(file: File) -> ResponseBody {
834 let len = file.metadata().map(|metadata| metadata.len() as usize).ok();
835
836 ResponseBody {
837 data: Box::new(file),
838 data_length: len,
839 }
840 }
841
842 /// Builds a new `ResponseBody` that returns an UTF-8 string.
843 ///
844 /// # Example
845 ///
846 /// ```
847 /// use rouille::ResponseBody;
848 /// let body = ResponseBody::from_string("hello world");
849 /// ```
850 #[inline]
851 pub fn from_string<S>(data: S) -> ResponseBody
852 where
853 S: Into<String>,
854 {
855 ResponseBody::from_data(data.into().into_bytes())
856 }
857
858 /// Extracts the content of the response.
859 ///
860 /// Returns the size of the body and the body itself. If the size is `None`, then it is
861 /// unknown.
862 #[inline]
863 pub fn into_reader_and_size(self) -> (Box<dyn Read + Send>, Option<usize>) {
864 (self.data, self.data_length)
865 }
866}
867
868#[cfg(test)]
869mod tests {
870 use Response;
871
872 #[test]
873 fn unique_header_adds() {
874 let r = Response {
875 headers: vec![],
876 ..Response::empty_400()
877 };
878
879 let r = r.with_unique_header("Foo", "Bar");
880
881 assert_eq!(r.headers.len(), 1);
882 assert_eq!(r.headers[0], ("Foo".into(), "Bar".into()));
883 }
884
885 #[test]
886 fn unique_header_adds_without_touching() {
887 let r = Response {
888 headers: vec![("Bar".into(), "Foo".into())],
889 ..Response::empty_400()
890 };
891
892 let r = r.with_unique_header("Foo", "Bar");
893
894 assert_eq!(r.headers.len(), 2);
895 assert_eq!(r.headers[0], ("Bar".into(), "Foo".into()));
896 assert_eq!(r.headers[1], ("Foo".into(), "Bar".into()));
897 }
898
899 #[test]
900 fn unique_header_replaces() {
901 let r = Response {
902 headers: vec![
903 ("foo".into(), "A".into()),
904 ("fOO".into(), "B".into()),
905 ("Foo".into(), "C".into()),
906 ],
907 ..Response::empty_400()
908 };
909
910 let r = r.with_unique_header("Foo", "Bar");
911
912 assert_eq!(r.headers.len(), 1);
913 assert_eq!(r.headers[0], ("foo".into(), "Bar".into()));
914 }
915}