1use std::{convert::Infallible, net::SocketAddr};
2
3use actix_utils::future::{err, ok, Ready};
4use derive_more::{Display, Error};
5
6use crate::{
7 dev::{AppConfig, Payload, RequestHead},
8 http::{
9 header::{self, HeaderName},
10 uri::{Authority, Scheme},
11 },
12 FromRequest, HttpRequest, ResponseError,
13};
14
15static X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for");
16static X_FORWARDED_HOST: HeaderName = HeaderName::from_static("x-forwarded-host");
17static X_FORWARDED_PROTO: HeaderName = HeaderName::from_static("x-forwarded-proto");
18
19fn unquote(val: &str) -> &str {
21 val.trim().trim_start_matches('"').trim_end_matches('"')
22}
23
24fn bare_address(val: &str) -> &str {
26 if val.starts_with('[') {
27 val.split("]:")
28 .next()
29 .map(|s| s.trim_start_matches('[').trim_end_matches(']'))
30 .unwrap_or(val)
33 } else {
34 val.split(':').next().unwrap_or(val)
35 }
36}
37
38fn first_header_value<'a>(req: &'a RequestHead, name: &'_ HeaderName) -> Option<&'a str> {
40 let hdr = req.headers.get(name)?.to_str().ok()?;
41 let val = hdr.split(',').next()?.trim();
42 Some(val)
43}
44
45#[derive(Debug, Clone, Default)]
77pub struct ConnectionInfo {
78 host: String,
79 scheme: String,
80 peer_addr: Option<String>,
81 realip_remote_addr: Option<String>,
82}
83
84impl ConnectionInfo {
85 pub(crate) fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {
86 let mut host = None;
87 let mut scheme = None;
88 let mut realip_remote_addr = None;
89
90 for (name, val) in req
91 .headers
92 .get_all(&header::FORWARDED)
93 .filter_map(|hdr| hdr.to_str().ok())
94 .flat_map(|val| val.split(';'))
96 .flat_map(|vals| vals.split(','))
98 .flat_map(|pair| {
100 let mut items = pair.trim().splitn(2, '=');
101 Some((items.next()?, items.next()?))
102 })
103 {
104 match name.trim().to_lowercase().as_str() {
117 "for" => realip_remote_addr.get_or_insert_with(|| bare_address(unquote(val))),
118 "proto" => scheme.get_or_insert_with(|| unquote(val)),
119 "host" => host.get_or_insert_with(|| unquote(val)),
120 "by" => {
121 continue;
123 }
124 _ => continue,
125 };
126 }
127
128 let scheme = scheme
129 .or_else(|| first_header_value(req, &X_FORWARDED_PROTO))
130 .or_else(|| req.uri.scheme().map(Scheme::as_str))
131 .or_else(|| Some("https").filter(|_| cfg.secure()))
132 .unwrap_or("http")
133 .to_owned();
134
135 let host = host
136 .or_else(|| first_header_value(req, &X_FORWARDED_HOST))
137 .or_else(|| req.headers.get(&header::HOST)?.to_str().ok())
138 .or_else(|| req.uri.authority().map(Authority::as_str))
139 .unwrap_or_else(|| cfg.host())
140 .to_owned();
141
142 let realip_remote_addr = realip_remote_addr
143 .or_else(|| first_header_value(req, &X_FORWARDED_FOR))
144 .map(str::to_owned);
145
146 let peer_addr = req.peer_addr.map(|addr| addr.ip().to_string());
147
148 ConnectionInfo {
149 host,
150 scheme,
151 peer_addr,
152 realip_remote_addr,
153 }
154 }
155
156 #[inline]
168 pub fn realip_remote_addr(&self) -> Option<&str> {
169 self.realip_remote_addr
170 .as_deref()
171 .or(self.peer_addr.as_deref())
172 }
173
174 #[inline]
178 pub fn peer_addr(&self) -> Option<&str> {
179 self.peer_addr.as_deref()
180 }
181
182 #[inline]
191 pub fn host(&self) -> &str {
192 &self.host
193 }
194
195 #[inline]
202 pub fn scheme(&self) -> &str {
203 &self.scheme
204 }
205
206 #[doc(hidden)]
207 #[deprecated(since = "4.0.0", note = "Renamed to `peer_addr`.")]
208 pub fn remote_addr(&self) -> Option<&str> {
209 self.peer_addr()
210 }
211}
212
213impl FromRequest for ConnectionInfo {
214 type Error = Infallible;
215 type Future = Ready<Result<Self, Self::Error>>;
216
217 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
218 ok(req.connection_info().clone())
219 }
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display)]
238#[display("{}", _0)]
239pub struct PeerAddr(pub SocketAddr);
240
241impl PeerAddr {
242 pub fn into_inner(self) -> SocketAddr {
244 self.0
245 }
246}
247
248#[derive(Debug, Display, Error)]
249#[non_exhaustive]
250#[display("Missing peer address")]
251pub struct MissingPeerAddr;
252
253impl ResponseError for MissingPeerAddr {}
254
255impl FromRequest for PeerAddr {
256 type Error = MissingPeerAddr;
257 type Future = Ready<Result<Self, Self::Error>>;
258
259 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
260 match req.peer_addr() {
261 Some(addr) => ok(PeerAddr(addr)),
262 None => {
263 log::error!("Missing peer address.");
264 err(MissingPeerAddr)
265 }
266 }
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273 use crate::test::TestRequest;
274
275 const X_FORWARDED_FOR: &str = "x-forwarded-for";
276 const X_FORWARDED_HOST: &str = "x-forwarded-host";
277 const X_FORWARDED_PROTO: &str = "x-forwarded-proto";
278
279 #[test]
280 fn info_default() {
281 let req = TestRequest::default().to_http_request();
282 let info = req.connection_info();
283 assert_eq!(info.scheme(), "http");
284 assert_eq!(info.host(), "localhost:8080");
285 }
286
287 #[test]
288 fn host_header() {
289 let req = TestRequest::default()
290 .insert_header((header::HOST, "rust-lang.org"))
291 .to_http_request();
292
293 let info = req.connection_info();
294 assert_eq!(info.scheme(), "http");
295 assert_eq!(info.host(), "rust-lang.org");
296 assert_eq!(info.realip_remote_addr(), None);
297 }
298
299 #[test]
300 fn x_forwarded_for_header() {
301 let req = TestRequest::default()
302 .insert_header((X_FORWARDED_FOR, "192.0.2.60"))
303 .to_http_request();
304 let info = req.connection_info();
305 assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
306 }
307
308 #[test]
309 fn x_forwarded_host_header() {
310 let req = TestRequest::default()
311 .insert_header((X_FORWARDED_HOST, "192.0.2.60"))
312 .to_http_request();
313 let info = req.connection_info();
314 assert_eq!(info.host(), "192.0.2.60");
315 assert_eq!(info.realip_remote_addr(), None);
316 }
317
318 #[test]
319 fn x_forwarded_proto_header() {
320 let req = TestRequest::default()
321 .insert_header((X_FORWARDED_PROTO, "https"))
322 .to_http_request();
323 let info = req.connection_info();
324 assert_eq!(info.scheme(), "https");
325 }
326
327 #[test]
328 fn forwarded_header() {
329 let req = TestRequest::default()
330 .insert_header((
331 header::FORWARDED,
332 "for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org",
333 ))
334 .to_http_request();
335
336 let info = req.connection_info();
337 assert_eq!(info.scheme(), "https");
338 assert_eq!(info.host(), "rust-lang.org");
339 assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
340
341 let req = TestRequest::default()
342 .insert_header((
343 header::FORWARDED,
344 "for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org",
345 ))
346 .to_http_request();
347
348 let info = req.connection_info();
349 assert_eq!(info.scheme(), "https");
350 assert_eq!(info.host(), "rust-lang.org");
351 assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
352 }
353
354 #[test]
355 fn forwarded_case_sensitivity() {
356 let req = TestRequest::default()
357 .insert_header((header::FORWARDED, "For=192.0.2.60"))
358 .to_http_request();
359 let info = req.connection_info();
360 assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
361 }
362
363 #[test]
364 fn forwarded_weird_whitespace() {
365 let req = TestRequest::default()
366 .insert_header((header::FORWARDED, "for= 1.2.3.4; proto= https"))
367 .to_http_request();
368 let info = req.connection_info();
369 assert_eq!(info.realip_remote_addr(), Some("1.2.3.4"));
370 assert_eq!(info.scheme(), "https");
371
372 let req = TestRequest::default()
373 .insert_header((header::FORWARDED, " for = 1.2.3.4 "))
374 .to_http_request();
375 let info = req.connection_info();
376 assert_eq!(info.realip_remote_addr(), Some("1.2.3.4"));
377 }
378
379 #[test]
380 fn forwarded_for_quoted() {
381 let req = TestRequest::default()
382 .insert_header((header::FORWARDED, r#"for="192.0.2.60:8080""#))
383 .to_http_request();
384 let info = req.connection_info();
385 assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
386 }
387
388 #[test]
389 fn forwarded_for_ipv6() {
390 let req = TestRequest::default()
391 .insert_header((header::FORWARDED, r#"for="[2001:db8:cafe::17]""#))
392 .to_http_request();
393 let info = req.connection_info();
394 assert_eq!(info.realip_remote_addr(), Some("2001:db8:cafe::17"));
395 }
396
397 #[test]
398 fn forwarded_for_ipv6_with_port() {
399 let req = TestRequest::default()
400 .insert_header((header::FORWARDED, r#"for="[2001:db8:cafe::17]:4711""#))
401 .to_http_request();
402 let info = req.connection_info();
403 assert_eq!(info.realip_remote_addr(), Some("2001:db8:cafe::17"));
404 }
405
406 #[test]
407 fn forwarded_for_multiple() {
408 let req = TestRequest::default()
409 .insert_header((header::FORWARDED, "for=192.0.2.60, for=198.51.100.17"))
410 .to_http_request();
411 let info = req.connection_info();
412 assert_eq!(info.realip_remote_addr(), Some("192.0.2.60"));
414 }
415
416 #[test]
417 fn scheme_from_uri() {
418 let req = TestRequest::get()
419 .uri("https://actix.rs/test")
420 .to_http_request();
421 let info = req.connection_info();
422 assert_eq!(info.scheme(), "https");
423 }
424
425 #[test]
426 fn host_from_uri() {
427 let req = TestRequest::get()
428 .uri("https://actix.rs/test")
429 .to_http_request();
430 let info = req.connection_info();
431 assert_eq!(info.host(), "actix.rs");
432 }
433
434 #[test]
435 fn host_from_server_hostname() {
436 let mut req = TestRequest::get();
437 req.set_server_hostname("actix.rs");
438 let req = req.to_http_request();
439
440 let info = req.connection_info();
441 assert_eq!(info.host(), "actix.rs");
442 }
443
444 #[actix_rt::test]
445 async fn conn_info_extract() {
446 let req = TestRequest::default()
447 .uri("https://actix.rs/test")
448 .to_http_request();
449 let conn_info = ConnectionInfo::extract(&req).await.unwrap();
450 assert_eq!(conn_info.scheme(), "https");
451 assert_eq!(conn_info.host(), "actix.rs");
452 }
453
454 #[actix_rt::test]
455 async fn peer_addr_extract() {
456 let req = TestRequest::default().to_http_request();
457 let res = PeerAddr::extract(&req).await;
458 assert!(res.is_err());
459
460 let addr = "127.0.0.1:8080".parse().unwrap();
461 let req = TestRequest::default().peer_addr(addr).to_http_request();
462 let peer_addr = PeerAddr::extract(&req).await.unwrap();
463 assert_eq!(peer_addr, PeerAddr(addr));
464 }
465
466 #[actix_rt::test]
467 async fn remote_address() {
468 let req = TestRequest::default().to_http_request();
469 let res = ConnectionInfo::extract(&req).await.unwrap();
470 assert!(res.peer_addr().is_none());
471
472 let addr = "127.0.0.1:8080".parse().unwrap();
473 let req = TestRequest::default().peer_addr(addr).to_http_request();
474 let conn_info = ConnectionInfo::extract(&req).await.unwrap();
475 assert_eq!(conn_info.peer_addr().unwrap(), "127.0.0.1");
476 }
477
478 #[actix_rt::test]
479 async fn real_ip_from_socket_addr() {
480 let req = TestRequest::default().to_http_request();
481 let res = ConnectionInfo::extract(&req).await.unwrap();
482 assert!(res.realip_remote_addr().is_none());
483
484 let addr = "127.0.0.1:8080".parse().unwrap();
485 let req = TestRequest::default().peer_addr(addr).to_http_request();
486 let conn_info = ConnectionInfo::extract(&req).await.unwrap();
487 assert_eq!(conn_info.realip_remote_addr().unwrap(), "127.0.0.1");
488 }
489}