1 //! Reply to requests.
2 //!
3 //! A [`Reply`](./trait.Reply.html) is a type that can be converted into an HTTP
4 //! response to be sent to the client. These are typically the successful
5 //! counterpart to a [rejection](../reject).
6 //!
7 //! The functions in this module are helpers for quickly creating a reply.
8 //! Besides them, you can return a type that implements [`Reply`](./trait.Reply.html). This
9 //! could be any of the following:
10 //!
11 //! - [`http::Response<impl Into<hyper::Body>`](https://docs.rs/http)
12 //! - `String`
13 //! - `&'static str`
14 //! - `http::StatusCode`
15 //!
16 //! # Example
17 //!
18 //! ```
19 //! use warp::{Filter, http::Response};
20 //!
21 //! // Returns an empty `200 OK` response.
22 //! let empty_200 = warp::any().map(warp::reply);
23 //!
24 //! // Returns a `200 OK` response with custom header and body.
25 //! let custom = warp::any().map(|| {
26 //!     Response::builder()
27 //!         .header("my-custom-header", "some-value")
28 //!         .body("and a custom body")
29 //! });
30 //!
31 //! // GET requests return the empty 200, POST return the custom.
32 //! let routes = warp::get().and(empty_200)
33 //!     .or(warp::post().and(custom));
34 //! ```
35 
36 use std::borrow::Cow;
37 use std::convert::TryFrom;
38 use std::error::Error as StdError;
39 use std::fmt;
40 
41 use crate::generic::{Either, One};
42 use http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
43 use http::StatusCode;
44 use hyper::Body;
45 use serde::Serialize;
46 use serde_json;
47 
48 // This re-export just looks weird in docs...
49 pub(crate) use self::sealed::Reply_;
50 use self::sealed::{BoxedReply, Internal};
51 #[doc(hidden)]
52 pub use crate::filters::reply as with;
53 
54 /// Response type into which types implementing the `Reply` trait are convertable.
55 pub type Response = ::http::Response<Body>;
56 
57 /// Returns an empty `Reply` with status code `200 OK`.
58 ///
59 /// # Example
60 ///
61 /// ```
62 /// use warp::Filter;
63 ///
64 /// // GET /just-ok returns an empty `200 OK`.
65 /// let route = warp::path("just-ok")
66 ///     .map(|| {
67 ///         println!("got a /just-ok request!");
68 ///         warp::reply()
69 ///     });
70 /// ```
71 #[inline]
reply() -> impl Reply72 pub fn reply() -> impl Reply {
73     StatusCode::OK
74 }
75 
76 /// Convert the value into a `Reply` with the value encoded as JSON.
77 ///
78 /// The passed value must implement [`Serialize`][ser]. Many
79 /// collections do, and custom domain types can have `Serialize` derived.
80 ///
81 /// [ser]: https://serde.rs
82 ///
83 /// # Example
84 ///
85 /// ```
86 /// use warp::Filter;
87 ///
88 /// // GET /ids returns a `200 OK` with a JSON array of ids:
89 /// // `[1, 3, 7, 13]`
90 /// let route = warp::path("ids")
91 ///     .map(|| {
92 ///         let our_ids = vec![1, 3, 7, 13];
93 ///         warp::reply::json(&our_ids)
94 ///     });
95 /// ```
96 ///
97 /// # Note
98 ///
99 /// If a type fails to be serialized into JSON, the error is logged at the
100 /// `error` level, and the returned `impl Reply` will be an empty
101 /// `500 Internal Server Error` response.
json<T>(val: &T) -> Json where T: Serialize,102 pub fn json<T>(val: &T) -> Json
103 where
104     T: Serialize,
105 {
106     Json {
107         inner: serde_json::to_vec(val).map_err(|err| {
108             log::error!("reply::json error: {}", err);
109         }),
110     }
111 }
112 
113 /// A JSON formatted reply.
114 #[allow(missing_debug_implementations)]
115 pub struct Json {
116     inner: Result<Vec<u8>, ()>,
117 }
118 
119 impl Reply for Json {
120     #[inline]
into_response(self) -> Response121     fn into_response(self) -> Response {
122         match self.inner {
123             Ok(body) => {
124                 let mut res = Response::new(body.into());
125                 res.headers_mut()
126                     .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
127                 res
128             }
129             Err(()) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
130         }
131     }
132 }
133 
134 #[derive(Debug)]
135 pub(crate) struct ReplyJsonError;
136 
137 impl fmt::Display for ReplyJsonError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result138     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139         f.write_str("warp::reply::json() failed")
140     }
141 }
142 
143 impl StdError for ReplyJsonError {}
144 
145 /// Reply with a body and `content-type` set to `text/html; charset=utf-8`.
146 ///
147 /// # Example
148 ///
149 /// ```
150 /// use warp::Filter;
151 ///
152 /// let body = r#"
153 /// <html>
154 ///     <head>
155 ///         <title>HTML with warp!</title>
156 ///     </head>
157 ///     <body>
158 ///         <h1>warp + HTML = :heart:</h1>
159 ///     </body>
160 /// </html>
161 /// "#;
162 ///
163 /// let route = warp::any()
164 ///     .map(|| {
165 ///         warp::reply::html(body)
166 ///     });
167 /// ```
html<T>(body: T) -> impl Reply where Body: From<T>, T: Send,168 pub fn html<T>(body: T) -> impl Reply
169 where
170     Body: From<T>,
171     T: Send,
172 {
173     Html { body }
174 }
175 
176 #[allow(missing_debug_implementations)]
177 struct Html<T> {
178     body: T,
179 }
180 
181 impl<T> Reply for Html<T>
182 where
183     Body: From<T>,
184     T: Send,
185 {
186     #[inline]
into_response(self) -> Response187     fn into_response(self) -> Response {
188         let mut res = Response::new(Body::from(self.body));
189         res.headers_mut().insert(
190             CONTENT_TYPE,
191             HeaderValue::from_static("text/html; charset=utf-8"),
192         );
193         res
194     }
195 }
196 
197 /// Types that can be converted into a `Response`.
198 ///
199 /// This trait is implemented for the following:
200 ///
201 /// - `http::StatusCode`
202 /// - `http::Response<impl Into<hyper::Body>>`
203 /// - `String`
204 /// - `&'static str`
205 ///
206 /// # Example
207 ///
208 /// ```rust
209 /// use warp::{Filter, http::Response};
210 ///
211 /// struct Message {
212 ///     msg: String
213 /// }
214 ///
215 /// impl warp::Reply for Message {
216 ///     fn into_response(self) -> warp::reply::Response {
217 ///         Response::new(format!("message: {}", self.msg).into())
218 ///     }
219 /// }
220 ///
221 /// fn handler() -> Message {
222 ///     Message { msg: "Hello".to_string() }
223 /// }
224 ///
225 /// let route = warp::any().map(handler);
226 /// ```
227 pub trait Reply: BoxedReply + Send {
228     /// Converts the given value into a [`Response`].
229     ///
230     /// [`Response`]: type.Response.html
into_response(self) -> Response231     fn into_response(self) -> Response;
232 
233     /*
234     TODO: Currently unsure about having trait methods here, as it
235     requires returning an exact type, which I'd rather not commit to.
236     Additionally, it doesn't work great with `Box<Reply>`.
237 
238     A possible alternative is to have wrappers, like
239 
240     - `WithStatus<R: Reply>(StatusCode, R)`
241 
242 
243     /// Change the status code of this `Reply`.
244     fn with_status(self, status: StatusCode) -> Reply_
245     where
246         Self: Sized,
247     {
248         let mut res = self.into_response();
249         *res.status_mut() = status;
250         Reply_(res)
251     }
252 
253     /// Add a header to this `Reply`.
254     ///
255     /// # Example
256     ///
257     /// ```rust
258     /// use warp::Reply;
259     ///
260     /// let reply = warp::reply()
261     ///     .with_header("x-foo", "bar");
262     /// ```
263     fn with_header<K, V>(self, name: K, value: V) -> Reply_
264     where
265         Self: Sized,
266         HeaderName: TryFrom<K>,
267         HeaderValue: TryFrom<V>,
268     {
269         match <HeaderName as TryFrom<K>>::try_from(name) {
270             Ok(name) => match <HeaderValue as TryFrom<V>>::try_from(value) {
271                 Ok(value) => {
272                     let mut res = self.into_response();
273                     res.headers_mut().append(name, value);
274                     Reply_(res)
275                 },
276                 Err(err) => {
277                     log::error!("with_header value error: {}", err.into());
278                     Reply_(::reject::server_error()
279                         .into_response())
280                 }
281             },
282             Err(err) => {
283                 log::error!("with_header name error: {}", err.into());
284                 Reply_(::reject::server_error()
285                     .into_response())
286             }
287         }
288     }
289     */
290 }
291 
292 impl<T: Reply + ?Sized> Reply for Box<T> {
into_response(self) -> Response293     fn into_response(self) -> Response {
294         self.boxed_into_response(Internal)
295     }
296 }
297 
_assert_object_safe()298 fn _assert_object_safe() {
299     fn _assert(_: &dyn Reply) {}
300 }
301 
302 /// Wrap an `impl Reply` to change its `StatusCode`.
303 ///
304 /// # Example
305 ///
306 /// ```
307 /// use warp::Filter;
308 ///
309 /// let route = warp::any()
310 ///     .map(warp::reply)
311 ///     .map(|reply| {
312 ///         warp::reply::with_status(reply, warp::http::StatusCode::CREATED)
313 ///     });
314 /// ```
with_status<T: Reply>(reply: T, status: StatusCode) -> WithStatus<T>315 pub fn with_status<T: Reply>(reply: T, status: StatusCode) -> WithStatus<T> {
316     WithStatus { reply, status }
317 }
318 
319 /// Wrap an `impl Reply` to change its `StatusCode`.
320 ///
321 /// Returned by `warp::reply::with_status`.
322 #[derive(Debug)]
323 pub struct WithStatus<T> {
324     reply: T,
325     status: StatusCode,
326 }
327 
328 impl<T: Reply> Reply for WithStatus<T> {
into_response(self) -> Response329     fn into_response(self) -> Response {
330         let mut res = self.reply.into_response();
331         *res.status_mut() = self.status;
332         res
333     }
334 }
335 
336 /// Wrap an `impl Reply` to add a header when rendering.
337 ///
338 /// # Example
339 ///
340 /// ```
341 /// use warp::Filter;
342 ///
343 /// let route = warp::any()
344 ///     .map(warp::reply)
345 ///     .map(|reply| {
346 ///         warp::reply::with_header(reply, "server", "warp")
347 ///     });
348 /// ```
with_header<T: Reply, K, V>(reply: T, name: K, value: V) -> WithHeader<T> where HeaderName: TryFrom<K>, <HeaderName as TryFrom<K>>::Error: Into<http::Error>, HeaderValue: TryFrom<V>, <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,349 pub fn with_header<T: Reply, K, V>(reply: T, name: K, value: V) -> WithHeader<T>
350 where
351     HeaderName: TryFrom<K>,
352     <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
353     HeaderValue: TryFrom<V>,
354     <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
355 {
356     let header = match <HeaderName as TryFrom<K>>::try_from(name) {
357         Ok(name) => match <HeaderValue as TryFrom<V>>::try_from(value) {
358             Ok(value) => Some((name, value)),
359             Err(err) => {
360                 log::error!("with_header value error: {}", err.into());
361                 None
362             }
363         },
364         Err(err) => {
365             log::error!("with_header name error: {}", err.into());
366             None
367         }
368     };
369 
370     WithHeader { header, reply }
371 }
372 
373 /// Wraps an `impl Reply` and adds a header when rendering.
374 ///
375 /// Returned by `warp::reply::with_header`.
376 #[derive(Debug)]
377 pub struct WithHeader<T> {
378     header: Option<(HeaderName, HeaderValue)>,
379     reply: T,
380 }
381 
382 impl<T: Reply> Reply for WithHeader<T> {
into_response(self) -> Response383     fn into_response(self) -> Response {
384         let mut res = self.reply.into_response();
385         if let Some((name, value)) = self.header {
386             res.headers_mut().insert(name, value);
387         }
388         res
389     }
390 }
391 
392 impl<T: Send> Reply for ::http::Response<T>
393 where
394     Body: From<T>,
395 {
396     #[inline]
into_response(self) -> Response397     fn into_response(self) -> Response {
398         self.map(Body::from)
399     }
400 }
401 
402 impl Reply for ::http::StatusCode {
403     #[inline]
into_response(self) -> Response404     fn into_response(self) -> Response {
405         let mut res = Response::default();
406         *res.status_mut() = self;
407         res
408     }
409 }
410 
411 impl<T> Reply for Result<T, ::http::Error>
412 where
413     T: Reply + Send,
414 {
415     #[inline]
into_response(self) -> Response416     fn into_response(self) -> Response {
417         match self {
418             Ok(t) => t.into_response(),
419             Err(e) => {
420                 log::error!("reply error: {:?}", e);
421                 StatusCode::INTERNAL_SERVER_ERROR.into_response()
422             }
423         }
424     }
425 }
426 
text_plain<T: Into<Body>>(body: T) -> Response427 fn text_plain<T: Into<Body>>(body: T) -> Response {
428     let mut response = ::http::Response::new(body.into());
429     response.headers_mut().insert(
430         CONTENT_TYPE,
431         HeaderValue::from_static("text/plain; charset=utf-8"),
432     );
433     response
434 }
435 
436 impl Reply for String {
437     #[inline]
into_response(self) -> Response438     fn into_response(self) -> Response {
439         text_plain(self)
440     }
441 }
442 
443 impl Reply for Vec<u8> {
444     #[inline]
into_response(self) -> Response445     fn into_response(self) -> Response {
446         ::http::Response::builder()
447             .header(
448                 CONTENT_TYPE,
449                 HeaderValue::from_static("application/octet-stream"),
450             )
451             .body(Body::from(self))
452             .unwrap()
453     }
454 }
455 
456 impl Reply for &'static str {
457     #[inline]
into_response(self) -> Response458     fn into_response(self) -> Response {
459         text_plain(self)
460     }
461 }
462 
463 impl Reply for Cow<'static, str> {
464     #[inline]
into_response(self) -> Response465     fn into_response(self) -> Response {
466         match self {
467             Cow::Borrowed(s) => s.into_response(),
468             Cow::Owned(s) => s.into_response(),
469         }
470     }
471 }
472 
473 impl Reply for &'static [u8] {
474     #[inline]
into_response(self) -> Response475     fn into_response(self) -> Response {
476         ::http::Response::builder()
477             .header(
478                 CONTENT_TYPE,
479                 HeaderValue::from_static("application/octet-stream"),
480             )
481             .body(Body::from(self))
482             .unwrap()
483     }
484 }
485 
486 impl<T, U> Reply for Either<T, U>
487 where
488     T: Reply,
489     U: Reply,
490 {
491     #[inline]
into_response(self) -> Response492     fn into_response(self) -> Response {
493         match self {
494             Either::A(a) => a.into_response(),
495             Either::B(b) => b.into_response(),
496         }
497     }
498 }
499 
500 impl<T> Reply for One<T>
501 where
502     T: Reply,
503 {
504     #[inline]
into_response(self) -> Response505     fn into_response(self) -> Response {
506         self.0.into_response()
507     }
508 }
509 
510 impl Reply for std::convert::Infallible {
511     #[inline(always)]
into_response(self) -> Response512     fn into_response(self) -> Response {
513         match self {}
514     }
515 }
516 
517 mod sealed {
518     use super::{Reply, Response};
519 
520     // An opaque type to return `impl Reply` from trait methods.
521     #[allow(missing_debug_implementations)]
522     pub struct Reply_(pub(crate) Response);
523 
524     impl Reply for Reply_ {
525         #[inline]
into_response(self) -> Response526         fn into_response(self) -> Response {
527             self.0
528         }
529     }
530 
531     #[allow(missing_debug_implementations)]
532     pub struct Internal;
533 
534     // Implemented for all types that implement `Reply`.
535     //
536     // A user doesn't need to worry about this, it's just trait
537     // hackery to get `Box<dyn Reply>` working.
538     pub trait BoxedReply {
boxed_into_response(self: Box<Self>, internal: Internal) -> Response539         fn boxed_into_response(self: Box<Self>, internal: Internal) -> Response;
540     }
541 
542     impl<T: Reply> BoxedReply for T {
boxed_into_response(self: Box<Self>, _: Internal) -> Response543         fn boxed_into_response(self: Box<Self>, _: Internal) -> Response {
544             (*self).into_response()
545         }
546     }
547 }
548 
549 #[cfg(test)]
550 mod tests {
551     use std::collections::HashMap;
552 
553     use super::*;
554 
555     #[test]
json_serde_error()556     fn json_serde_error() {
557         // a HashMap<Vec, _> cannot be serialized to JSON
558         let mut map = HashMap::new();
559         map.insert(vec![1, 2], 45);
560 
561         let res = json(&map).into_response();
562         assert_eq!(res.status(), 500);
563     }
564 
565     #[test]
response_builder_error()566     fn response_builder_error() {
567         let res = ::http::Response::builder()
568             .status(1337)
569             .body("woops")
570             .into_response();
571 
572         assert_eq!(res.status(), 500);
573     }
574 
575     #[test]
boxed_reply()576     fn boxed_reply() {
577         let r: Box<dyn Reply> = Box::new(reply());
578         let resp = r.into_response();
579         assert_eq!(resp.status(), 200);
580     }
581 }
582