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