1use std::{cell::Ref, cell::RefMut, convert::Infallible, fmt, net, rc::Rc};
2
3use urly::Url;
4
5use crate::http::{HeaderMap, HttpMessage, Message, Method, Payload, RequestHead, Version};
6use crate::{Cfg, io::IoRef, io::types, router::Path, util::Extensions};
7
8use super::config::WebAppConfig;
9use super::info::ConnectionInfo;
10use super::rmap::ResourceMap;
11use super::{FromRequest, State};
12
13#[derive(Clone)]
14pub struct HttpRequest(pub(crate) Rc<HttpRequestInner>);
16
17pub(crate) struct HttpRequestInner {
18 pub(crate) head: Message<RequestHead>,
19 pub(crate) path: Path<Url>,
20 pub(crate) config: Cfg<WebAppConfig>,
21 pub(crate) rmap: Rc<ResourceMap>,
22}
23
24impl HttpRequest {
25 #[inline]
26 pub(crate) fn new(
27 path: Path<Url>,
28 head: Message<RequestHead>,
29 rmap: Rc<ResourceMap>,
30 config: Cfg<WebAppConfig>,
31 ) -> HttpRequest {
32 HttpRequest(Rc::new(HttpRequestInner {
33 head,
34 path,
35 config,
36 rmap,
37 }))
38 }
39}
40
41impl HttpRequest {
42 #[inline]
44 pub fn head(&self) -> &RequestHead {
45 &self.0.head
46 }
47
48 #[inline]
51 pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
52 &mut Rc::get_mut(&mut self.0).unwrap().head
53 }
54
55 #[inline]
57 pub fn uri(&self) -> &Url {
58 &self.head().uri
59 }
60
61 #[inline]
63 pub fn method(&self) -> &Method {
64 &self.head().method
65 }
66
67 #[inline]
69 pub fn version(&self) -> Version {
70 self.head().version
71 }
72
73 #[inline]
74 pub fn headers(&self) -> &HeaderMap {
76 &self.head().headers
77 }
78
79 #[inline]
81 pub fn path(&self) -> &str {
82 self.head().uri.path().as_str()
83 }
84
85 #[inline]
89 pub fn query_string(&self) -> &str {
90 self.uri().query().map_or("", urly::Query::as_str)
91 }
92
93 #[inline]
95 pub fn io(&self) -> Option<&IoRef> {
96 self.head().io.as_ref()
97 }
98
99 #[inline]
104 pub fn peer_addr(&self) -> Option<net::SocketAddr> {
105 self.io()
106 .and_then(|io| io.query::<types::PeerAddr>().get().map(|addr| addr.0))
107 }
108
109 #[inline]
116 pub fn match_info(&self) -> &Path<Url> {
117 &self.0.path
118 }
119
120 #[inline]
121 pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
122 &mut Rc::get_mut(&mut self.0).unwrap().path
123 }
124
125 #[inline]
127 pub fn extensions(&self) -> Ref<'_, Extensions> {
128 self.head().extensions()
129 }
130
131 #[inline]
133 pub fn extensions_mut(&self) -> RefMut<'_, Extensions> {
134 self.head().extensions_mut()
135 }
136
137 pub fn url_for<U, I>(
159 &self,
160 name: &str,
161 elements: U,
162 ) -> Result<urly::Url, super::error::UrlGenerationError>
163 where
164 U: IntoIterator<Item = I>,
165 I: AsRef<str>,
166 {
167 self.0.rmap.url_for(self, name, elements)
168 }
169
170 pub fn url_for_static(
175 &self,
176 name: &str,
177 ) -> Result<urly::Url, super::error::UrlGenerationError> {
178 const NO_PARAMS: [&str; 0] = [];
179 self.url_for(name, NO_PARAMS)
180 }
181
182 #[inline]
183 pub fn resource_map(&self) -> &ResourceMap {
185 &self.0.rmap
186 }
187
188 #[inline]
193 pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
194 ConnectionInfo::get(self.head(), &self.0.config)
195 }
196
197 #[inline]
199 pub fn app_config(&self) -> &WebAppConfig {
200 &self.0.config
201 }
202
203 #[inline]
204 pub fn app_state<T: 'static>(&self) -> Option<&T> {
206 self.0.config.state::<T>()
207 }
208}
209
210impl HttpMessage for HttpRequest {
211 #[inline]
212 fn message_headers(&self) -> &HeaderMap {
214 &self.head().headers
215 }
216
217 #[inline]
219 fn message_extensions(&self) -> Ref<'_, Extensions> {
220 self.0.head.extensions()
221 }
222
223 #[inline]
225 fn message_extensions_mut(&self) -> RefMut<'_, Extensions> {
226 self.0.head.extensions_mut()
227 }
228}
229
230impl Drop for HttpRequest {
231 fn drop(&mut self) {
232 let id = self.0.config.id();
233 let pool_size = self.0.config.pool_size;
234 super::config::put_request(id, pool_size, &mut self.0);
235 }
236}
237
238impl<St: State> FromRequest<St> for HttpRequest {
258 type Error = Infallible;
259
260 #[inline]
261 async fn from_request(_: &St, req: &HttpRequest, _: &mut Payload) -> Result<Self, Self::Error> {
262 Ok(req.clone())
263 }
264}
265
266impl fmt::Debug for HttpRequest {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 writeln!(
269 f,
270 "\nHttpRequest {:?} {}:{}",
271 self.0.head.version,
272 self.0.head.method,
273 self.path()
274 )?;
275 if !self.query_string().is_empty() {
276 writeln!(f, " query: ?{:?}", self.query_string())?;
277 }
278 if !self.match_info().is_empty() {
279 writeln!(f, " params: {:?}", self.match_info())?;
280 }
281 writeln!(f, " headers:")?;
282 for (key, val) in self.headers() {
283 writeln!(f, " {key:?}: {val:?}")?;
284 }
285 Ok(())
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 use crate::http::header;
293 use crate::router::ResourceDef;
294 use crate::web::test::TestRequest;
295
296 #[test]
297 fn test_debug() {
298 let req = TestRequest::with_header("content-type", "text/plain").to_http_request();
299 let dbg = format!("{req:?}");
300 assert!(dbg.contains("HttpRequest"));
301
302 let req = TestRequest::with_uri("/index.html?q=?").to_http_request();
303 let dbg = format!("{req:?}");
304 assert!(dbg.contains("HttpRequest"));
305 assert!(req.peer_addr().is_none());
306 assert_eq!(req.method(), &Method::GET);
307 }
308
309 #[cfg(feature = "cookie")]
310 #[test]
311 fn test_no_request_cookies() {
312 let req = TestRequest::default().to_http_request();
313 assert!(req.cookies().unwrap().is_empty());
314 }
315
316 #[cfg(feature = "cookie")]
317 #[test]
318 fn test_request_cookies() {
319 let req = TestRequest::default()
320 .header(header::COOKIE, "cookie1=value1")
321 .header(header::COOKIE, "cookie2=value2")
322 .to_http_request();
323 {
324 let cookies = req.cookies().unwrap();
325 assert_eq!(cookies.len(), 2);
326 assert_eq!(cookies[0].name(), "cookie1");
327 assert_eq!(cookies[0].value(), "value1");
328 assert_eq!(cookies[1].name(), "cookie2");
329 assert_eq!(cookies[1].value(), "value2");
330 }
331
332 let cookie = req.cookie("cookie1");
333 assert!(cookie.is_some());
334 let cookie = cookie.unwrap();
335 assert_eq!(cookie.name(), "cookie1");
336 assert_eq!(cookie.value(), "value1");
337
338 let cookie = req.cookie("cookie-unknown");
339 assert!(cookie.is_none());
340 }
341
342 #[test]
343 fn test_request_query() {
344 let req = TestRequest::with_uri("/?id=test").to_http_request();
345 assert_eq!(req.query_string(), "id=test");
346 }
347
348 #[test]
349 fn test_url_for() {
350 let mut res = ResourceDef::new("/user/{name}.{ext}");
351 res.set_name("index");
352
353 let mut rmap = ResourceMap::new(ResourceDef::new(""));
354 rmap.add(&mut res, None);
355 let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
359 .rmap(rmap)
360 .to_http_request();
361
362 assert_eq!(
363 req.url_for("unknown", ["test"]),
364 Err(crate::web::error::UrlGenerationError::ResourceNotFound)
365 );
366 assert_eq!(
367 req.url_for("index", ["test"]),
368 Err(crate::web::error::UrlGenerationError::NotEnoughElements)
369 );
370 let url = req.url_for("index", ["test", "html"]);
371 assert_eq!(
372 url.ok().unwrap().as_str(),
373 "http://www.rust-lang.org/user/test.html"
374 );
375 }
376
377 #[test]
378 fn test_url_for_static() {
379 let mut rdef = ResourceDef::new("/index.html");
380 rdef.set_name("index");
381
382 let mut rmap = ResourceMap::new(ResourceDef::new(""));
383 rmap.add(&mut rdef, None);
384
385 let req = TestRequest::with_uri("/test")
388 .header(header::HOST, "www.rust-lang.org")
389 .rmap(rmap)
390 .to_http_request();
391 let url = req.url_for_static("index");
392 assert_eq!(
393 url.ok().unwrap().as_str(),
394 "http://www.rust-lang.org/index.html"
395 );
396 }
397
398 #[test]
399 fn test_url_for_external() {
400 let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
401
402 rdef.set_name("youtube");
403
404 let mut rmap = ResourceMap::new(ResourceDef::new(""));
405 rmap.add(&mut rdef, None);
406 let req = TestRequest::default().rmap(rmap).to_http_request();
409 let url = req.url_for("youtube", ["oHg5SJYRHA0"]);
410 assert_eq!(
411 url.ok().unwrap().as_str(),
412 "https://youtube.com/watch/oHg5SJYRHA0"
413 );
414 }
415}