1use std::{sync::Arc, io::BufRead};
5
6use futures_core::Stream;
7use hyper::body::{Buf, Bytes};
8use log::error;
9use url::Url;
10
11use crate::{Request, StatusCode};
12
13type Inner = hyper::Response<hyper::Body>;
14
15#[derive(Debug)]
17pub struct Response {
18 request: Arc<Request>,
19 inner: Inner,
20}
21
22impl Response {
23 pub(crate) fn new_about(request: Arc<Request>, body: &'static str) -> Self {
24 Self {
25 request,
26 inner: Inner::new(body.into()),
27 }
28 }
29
30 pub(crate) fn new_file<S, O, E>(
31 request: Arc<Request>,
32 stream: S,
33 ) -> Self
34 where S: Stream<Item = Result<O, E>> + Send + 'static,
35 O: Into<Bytes> + 'static,
36 E: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
37 {
38 Self {
39 request,
40 inner: Inner::new(hyper::Body::wrap_stream(stream))
41 }
42 }
43
44 pub fn content_type(&self) -> mime::Mime {
50 let Some(content_type) = self.inner.headers().get(hyper::header::CONTENT_TYPE) else {
51 return mime::APPLICATION_OCTET_STREAM;
52 };
53
54 let Ok(content_type) = content_type.to_str() else {
55 return mime::APPLICATION_OCTET_STREAM;
56 };
57
58 content_type.parse().unwrap_or(mime::APPLICATION_OCTET_STREAM)
59 }
60
61 pub fn redirect_location(&self) -> Option<&str> {
64 if !self.status().is_redirection() {
65 return None;
66 }
67
68 let location = self.inner.headers().get(hyper::header::LOCATION)?;
69 location.to_str().ok()
70 }
71
72 pub fn redirect_url(&self) -> Option<Url> {
75 let location = self.redirect_location()?;
76 match url::Url::parse(location) {
77 Ok(url) => Some(url),
78 Err(e) => {
79 error!("Redirect status ({:?}) with invalid URL: \"{location}\", error: {e}", self.status());
80 None
81 }
82 }
83 }
84
85 pub async fn body(&mut self) -> Box<dyn BufRead + '_> {
87 Box::new(hyper::body::aggregate(self.inner.body_mut()).await.unwrap().reader())
88 }
89
90 pub async fn body_bytes(&mut self) -> Bytes {
93 hyper::body::to_bytes(self.inner.body_mut()).await.unwrap()
94 }
95
96 pub fn request(&self) -> &Request {
98 &self.request
99 }
100
101 pub fn ok(&self) -> bool {
103 self.status().is_successful()
104 }
105
106 pub fn status(&self) -> StatusCode {
108 self.inner.status().into()
109 }
110
111 pub fn url(&self) -> &Url {
113 &self.request.url
114 }
115}
116
117impl From<(Arc<Request>, Inner)> for Response {
118 fn from(value: (Arc<Request>, Inner)) -> Self {
119 let (request, inner) = value;
120 Self { request, inner }
121 }
122}