1 use std::convert::TryInto;
2 use std::time::Duration;
3 
4 use http::header::{HeaderValue, IntoHeaderName};
5 use http::Method;
6 
7 #[cfg(feature = "charsets")]
8 use crate::charsets::Charset;
9 use crate::error::{Error, Result};
10 use crate::request::proxy::ProxySettings;
11 use crate::request::{header_append, header_insert, BaseSettings, RequestBuilder};
12 use crate::tls::Certificate;
13 
14 /// `Session` is a type that can carry settings over multiple requests. The settings applied to the
15 /// `Session` are applied to every request created from this `Session`.
16 #[derive(Debug, Default)]
17 pub struct Session {
18     base_settings: BaseSettings,
19 }
20 
21 impl Session {
22     /// Create a new `Session` with default settings.
new() -> Session23     pub fn new() -> Session {
24         Session {
25             base_settings: BaseSettings::default(),
26         }
27     }
28 
29     /// Create a new `RequestBuilder` with the GET method and this Session's settings applied on it.
get<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,30     pub fn get<U>(&self, base_url: U) -> RequestBuilder
31     where
32         U: AsRef<str>,
33     {
34         RequestBuilder::with_settings(Method::GET, base_url, self.base_settings.clone())
35     }
36 
37     /// Create a new `RequestBuilder` with the POST method and this Session's settings applied on it.
post<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,38     pub fn post<U>(&self, base_url: U) -> RequestBuilder
39     where
40         U: AsRef<str>,
41     {
42         RequestBuilder::with_settings(Method::POST, base_url, self.base_settings.clone())
43     }
44 
45     /// Create a new `RequestBuilder` with the PUT method and this Session's settings applied on it.
put<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,46     pub fn put<U>(&self, base_url: U) -> RequestBuilder
47     where
48         U: AsRef<str>,
49     {
50         RequestBuilder::with_settings(Method::PUT, base_url, self.base_settings.clone())
51     }
52 
53     /// Create a new `RequestBuilder` with the DELETE method and this Session's settings applied on it.
delete<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,54     pub fn delete<U>(&self, base_url: U) -> RequestBuilder
55     where
56         U: AsRef<str>,
57     {
58         RequestBuilder::with_settings(Method::DELETE, base_url, self.base_settings.clone())
59     }
60 
61     /// Create a new `RequestBuilder` with the HEAD method and this Session's settings applied on it.
head<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,62     pub fn head<U>(&self, base_url: U) -> RequestBuilder
63     where
64         U: AsRef<str>,
65     {
66         RequestBuilder::with_settings(Method::HEAD, base_url, self.base_settings.clone())
67     }
68 
69     /// Create a new `RequestBuilder` with the OPTIONS method and this Session's settings applied on it.
options<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,70     pub fn options<U>(&self, base_url: U) -> RequestBuilder
71     where
72         U: AsRef<str>,
73     {
74         RequestBuilder::with_settings(Method::OPTIONS, base_url, self.base_settings.clone())
75     }
76 
77     /// Create a new `RequestBuilder` with the PATCH method and this Session's settings applied on it.
patch<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,78     pub fn patch<U>(&self, base_url: U) -> RequestBuilder
79     where
80         U: AsRef<str>,
81     {
82         RequestBuilder::with_settings(Method::PATCH, base_url, self.base_settings.clone())
83     }
84 
85     /// Create a new `RequestBuilder` with the TRACE method and this Session's settings applied on it.
trace<U>(&self, base_url: U) -> RequestBuilder where U: AsRef<str>,86     pub fn trace<U>(&self, base_url: U) -> RequestBuilder
87     where
88         U: AsRef<str>,
89     {
90         RequestBuilder::with_settings(Method::TRACE, base_url, self.base_settings.clone())
91     }
92 
93     //
94     // Settings
95     //
96 
97     /// Modify a header for this `Request`.
98     ///
99     /// If the header is already present, the value will be replaced. If you wish to append a new header,
100     /// use `header_append`.
101     ///
102     /// # Panics
103     /// This method will panic if the value is invalid.
header<H, V>(&mut self, header: H, value: V) where H: IntoHeaderName, V: TryInto<HeaderValue>, Error: From<V::Error>,104     pub fn header<H, V>(&mut self, header: H, value: V)
105     where
106         H: IntoHeaderName,
107         V: TryInto<HeaderValue>,
108         Error: From<V::Error>,
109     {
110         self.try_header(header, value).expect("invalid header value");
111     }
112 
113     /// Append a new header for this `Request`.
114     ///
115     /// The new header is always appended to the request, even if the header already exists.
116     ///
117     /// # Panics
118     /// This method will panic if the value is invalid.
header_append<H, V>(&mut self, header: H, value: V) where H: IntoHeaderName, V: TryInto<HeaderValue>, Error: From<V::Error>,119     pub fn header_append<H, V>(&mut self, header: H, value: V)
120     where
121         H: IntoHeaderName,
122         V: TryInto<HeaderValue>,
123         Error: From<V::Error>,
124     {
125         self.try_header_append(header, value).expect("invalid header value");
126     }
127 
128     /// Modify a header for this `Request`.
129     ///
130     /// If the header is already present, the value will be replaced. If you wish to append a new header,
131     /// use `header_append`.
try_header<H, V>(&mut self, header: H, value: V) -> Result<()> where H: IntoHeaderName, V: TryInto<HeaderValue>, Error: From<V::Error>,132     pub fn try_header<H, V>(&mut self, header: H, value: V) -> Result<()>
133     where
134         H: IntoHeaderName,
135         V: TryInto<HeaderValue>,
136         Error: From<V::Error>,
137     {
138         header_insert(&mut self.base_settings.headers, header, value)?;
139         Ok(())
140     }
141 
142     /// Append a new header to this `Request`.
143     ///
144     /// The new header is always appended to the `Request`, even if the header already exists.
try_header_append<H, V>(&mut self, header: H, value: V) -> Result<()> where H: IntoHeaderName, V: TryInto<HeaderValue>, Error: From<V::Error>,145     pub fn try_header_append<H, V>(&mut self, header: H, value: V) -> Result<()>
146     where
147         H: IntoHeaderName,
148         V: TryInto<HeaderValue>,
149         Error: From<V::Error>,
150     {
151         header_append(&mut self.base_settings.headers, header, value)?;
152         Ok(())
153     }
154 
155     /// Set the maximum number of headers accepted in responses to this request.
156     ///
157     /// The default is 100.
max_headers(&mut self, max_headers: usize)158     pub fn max_headers(&mut self, max_headers: usize) {
159         self.base_settings.max_headers = max_headers;
160     }
161 
162     /// Set the maximum number of redirections this `Request` can perform.
163     ///
164     /// The default is 5.
max_redirections(&mut self, max_redirections: u32)165     pub fn max_redirections(&mut self, max_redirections: u32) {
166         self.base_settings.max_redirections = max_redirections;
167     }
168 
169     /// Sets if this `Request` should follow redirects, 3xx codes.
170     ///
171     /// This value defaults to true.
follow_redirects(&mut self, follow_redirects: bool)172     pub fn follow_redirects(&mut self, follow_redirects: bool) {
173         self.base_settings.follow_redirects = follow_redirects;
174     }
175 
176     /// Sets a connect timeout for this request.
177     ///
178     /// The default is 30 seconds.
connect_timeout(&mut self, duration: Duration)179     pub fn connect_timeout(&mut self, duration: Duration) {
180         self.base_settings.connect_timeout = duration;
181     }
182 
183     /// Sets a read timeout for this request.
184     ///
185     /// The default is 30 seconds.
read_timeout(&mut self, duration: Duration)186     pub fn read_timeout(&mut self, duration: Duration) {
187         self.base_settings.read_timeout = duration;
188     }
189 
190     /// Sets a timeout for the whole request.
191     ///
192     /// Applies after a TCP connection is established. Defaults to no timeout.
timeout(&mut self, duration: Duration)193     pub fn timeout(&mut self, duration: Duration) {
194         self.base_settings.timeout = Some(duration);
195     }
196 
197     /// Sets the proxy settigns for this request.
198     ///
199     /// If left untouched, the defaults are to use system proxy settings found in environment variables.
proxy_settings(&mut self, settings: ProxySettings)200     pub fn proxy_settings(&mut self, settings: ProxySettings) {
201         self.base_settings.proxy_settings = settings;
202     }
203 
204     /// Set the default charset to use while parsing the response of this `Request`.
205     ///
206     /// If the response does not say which charset it uses, this charset will be used to decode the request.
207     /// This value defaults to `None`, in which case ISO-8859-1 is used.
208     #[cfg(feature = "charsets")]
default_charset(&mut self, default_charset: Option<Charset>)209     pub fn default_charset(&mut self, default_charset: Option<Charset>) {
210         self.base_settings.default_charset = default_charset;
211     }
212 
213     /// Sets if this `Request` will announce that it accepts compression.
214     ///
215     /// This value defaults to true. Note that this only lets the browser know that this `Request` supports
216     /// compression, the server might choose not to compress the content.
217     #[cfg(feature = "compress")]
allow_compression(&mut self, allow_compression: bool)218     pub fn allow_compression(&mut self, allow_compression: bool) {
219         self.base_settings.allow_compression = allow_compression;
220     }
221 
222     /// Sets if this `Request` will accept invalid TLS certificates.
223     ///
224     /// Accepting invalid certificates implies that invalid hostnames are accepted
225     /// as well.
226     ///
227     /// The default value is `false`.
228     ///
229     /// # Danger
230     /// Use this setting with care. This will accept **any** TLS certificate valid or not.
231     /// If you are using self signed certificates, it is much safer to add their root CA
232     /// to the list of trusted root CAs by your system.
danger_accept_invalid_certs(&mut self, accept_invalid_certs: bool)233     pub fn danger_accept_invalid_certs(&mut self, accept_invalid_certs: bool) {
234         self.base_settings.accept_invalid_certs = accept_invalid_certs;
235     }
236 
237     /// Sets if this `Request` will accept an invalid hostname in a TLS certificate.
238     ///
239     /// The default value is `false`.
240     ///
241     /// # Danger
242     /// Use this setting with care. This will accept TLS certificates that do not match
243     /// the hostname.
danger_accept_invalid_hostnames(&mut self, accept_invalid_hostnames: bool)244     pub fn danger_accept_invalid_hostnames(&mut self, accept_invalid_hostnames: bool) {
245         self.base_settings.accept_invalid_hostnames = accept_invalid_hostnames;
246     }
247 
248     /// Adds a root certificate that will be trusted.
add_root_certificate(&mut self, cert: Certificate)249     pub fn add_root_certificate(&mut self, cert: Certificate) {
250         self.base_settings.root_certificates.0.push(cert);
251     }
252 }
253