Skip to main content

leyline/core/session/execute/
response.rs

1use std::sync::Arc;
2use std::time::SystemTime;
3
4use url::Url;
5
6use super::super::decompress::{decompress_and_strip, drain_stream_into_vec};
7use super::journey::Journey;
8use crate::core::Session;
9use crate::core::deadline::Deadline;
10use crate::core::device::{SessionState, StateParts, unix_secs};
11use crate::core::error::Result;
12use crate::core::response::Response;
13use crate::core::transport::TransportResponse;
14use crate::util::lock;
15
16impl Session {
17    pub(super) fn store_cookies(
18        &self,
19        resp_headers: &[(http::HeaderName, http::HeaderValue)],
20        url: &Url,
21    ) {
22        let set_cookies: Vec<&str> = resp_headers
23            .iter()
24            .filter(|(k, _)| *k == "set-cookie")
25            .filter_map(|(_, v)| v.to_str().ok())
26            .collect();
27        if !set_cookies.is_empty() {
28            self.inner
29                .cookie_jar
30                .store_response_cookies(set_cookies.as_slice(), url);
31        }
32    }
33
34    #[must_use]
35    pub fn state(&self) -> SessionState {
36        let now = SystemTime::now();
37        SessionState::from_parts(StateParts {
38            tls_sessions: self.inner.connector.session_cache().export(unix_secs(now)),
39            #[cfg(feature = "http3")]
40            alt_svc: self.inner.pool.alt_svc_entries(),
41            #[cfg(not(feature = "http3"))]
42            alt_svc: Vec::new(),
43            hsts: lock(&self.inner.pool.hsts).export(now),
44        })
45    }
46
47    pub(crate) fn restore_state(&self, parts: &StateParts) {
48        let now = SystemTime::now();
49        self.inner
50            .connector
51            .session_cache()
52            .import(&parts.tls_sessions, unix_secs(now));
53        #[cfg(feature = "http3")]
54        self.inner.pool.restore_alt_svc(&parts.alt_svc);
55        lock(&self.inner.pool.hsts).import(&parts.hsts, now);
56    }
57
58    pub(super) fn note_hsts(
59        &self,
60        url: &Url,
61        resp_headers: &[(http::HeaderName, http::HeaderValue)],
62    ) {
63        if self.inner.tls_trust.accepts_invalid_certs() {
64            return;
65        }
66        let values: Vec<&str> = resp_headers
67            .iter()
68            .filter(|(k, _)| *k == "strict-transport-security")
69            .filter_map(|(_, v)| v.to_str().ok())
70            .collect();
71        if !values.is_empty() {
72            lock(&self.inner.pool.hsts).note(url, &values, SystemTime::now());
73        }
74    }
75
76    pub(super) fn hsts_upgrade(&self, url: &Arc<Url>) -> Arc<Url> {
77        lock(&self.inner.pool.hsts)
78            .upgrade(url, SystemTime::now())
79            .map_or_else(|| Arc::clone(url), Arc::new)
80    }
81
82    #[cfg(feature = "http3")]
83    pub(super) fn note_alt_svc(
84        &self,
85        url: &Url,
86        resp_headers: &[(http::HeaderName, http::HeaderValue)],
87    ) {
88        let owned: Vec<String> = resp_headers
89            .iter()
90            .filter(|(k, _)| *k == "alt-svc")
91            .map(|(_, v)| String::from_utf8_lossy(v.as_bytes()).into_owned())
92            .collect();
93        let fields: Vec<&str> = owned.iter().map(String::as_str).collect();
94        if fields.is_empty() {
95            return;
96        }
97        let age = resp_headers
98            .iter()
99            .find(|(k, _)| *k == "age")
100            .and_then(|(_, v)| v.to_str().ok())
101            .and_then(|v| crate::util::delta_seconds(v.split(',').next().unwrap_or_default()))
102            .map_or(std::time::Duration::ZERO, std::time::Duration::from_secs);
103        if let Some(host) = url.host_str()
104            && let Some(port) = url.port_or_known_default()
105        {
106            self.inner.pool.note_alt_svc(host, port, &fields, age);
107        }
108    }
109
110    pub(super) async fn assemble_response(
111        &self,
112        leg: TransportResponse,
113        journey: Journey,
114        audit_headers: Vec<(String, String)>,
115        stream_response: bool,
116        deadline: &Deadline,
117    ) -> Result<Response> {
118        let TransportResponse {
119            status,
120            headers,
121            trailers,
122            body,
123            final_url,
124            version,
125            tls,
126            ..
127        } = leg;
128        let (body, headers) = self
129            .finalize_response_body(body, headers, stream_response, deadline)
130            .await?;
131        let audited = self.inner.audit_tls.is_some();
132        let mut audit_headers = audit_headers;
133        audit_headers.retain(|(name, value)| crate::core::transport::sent_on(version, name, value));
134        Ok(Response {
135            status,
136            headers: headers.into_iter().collect(),
137            body,
138            url: final_url,
139            redirect_chain: journey.chain,
140            version,
141            trailers,
142            request_headers: audit_headers,
143            tls,
144            request_method: if audited {
145                journey.method
146            } else {
147                String::new()
148            },
149            audit_tls: self.inner.audit_tls.clone(),
150            audit_cache: std::sync::OnceLock::new(),
151            compression: self.inner.compression,
152            timing: journey.timing,
153            attempts: 1,
154            proxy: None,
155        })
156    }
157
158    pub(super) async fn finalize_response_body(
159        &self,
160        resp_body_shape: crate::core::transport::TransportBody,
161        resp_headers: Vec<(http::HeaderName, http::HeaderValue)>,
162        stream_response: bool,
163        deadline: &Deadline,
164    ) -> Result<(
165        crate::core::response::ResponseBody,
166        Vec<(http::HeaderName, http::HeaderValue)>,
167    )> {
168        Ok(match resp_body_shape {
169            crate::core::transport::TransportBody::Streaming(mut bs) if stream_response => {
170                bs.set_read_timeout(deadline.read());
171                bs.set_body_timeout(deadline.body());
172                bs.stop_on(&self.inner.shutdown);
173                bs.watch_end();
174                (
175                    crate::core::response::ResponseBody::Streaming(bs),
176                    resp_headers,
177                )
178            }
179            crate::core::transport::TransportBody::Streaming(mut bs) => {
180                bs.set_read_timeout(deadline.read());
181                bs.set_body_timeout(deadline.body());
182                let buf = drain_stream_into_vec(bs, self.inner.compression.max_body_size).await?;
183                let (buf, resp_headers) =
184                    decompress_and_strip(buf, resp_headers, &self.inner.compression)?;
185                (
186                    crate::core::response::ResponseBody::Buffered(buf),
187                    resp_headers,
188                )
189            }
190            crate::core::transport::TransportBody::Buffered(buf) => {
191                if stream_response {
192                    let mut bs =
193                        crate::core::body_stream::BodyStream::from_bytes(bytes::Bytes::from(buf));
194                    bs.stop_on(&self.inner.shutdown);
195                    bs.watch_end();
196                    (
197                        crate::core::response::ResponseBody::Streaming(bs),
198                        resp_headers,
199                    )
200                } else {
201                    let (buf, resp_headers) =
202                        decompress_and_strip(buf, resp_headers, &self.inner.compression)?;
203                    (
204                        crate::core::response::ResponseBody::Buffered(buf),
205                        resp_headers,
206                    )
207                }
208            }
209        })
210    }
211}