Skip to content

Commit 479ab6e

Browse files
committed
add Request::remote and Request::forwarded_for
1 parent bb38f55 commit 479ab6e

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

src/request.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,35 @@ impl Request {
7070
self.local_addr
7171
}
7272

73+
/// Get the remote address for this request.
74+
pub fn remote(&self) -> Option<String> {
75+
self.forwarded_for()
76+
.or_else(|| self.peer_addr.map(|peer| peer.to_string()))
77+
}
78+
79+
/// Parses the Forwarded or X-Forwarded-For headers.
80+
/// The returned String will either be an IP address or a domain and an optional port.
81+
pub fn forwarded_for(&self) -> Option<String> {
82+
if let Some(header) = self.header(&"Forwarded".parse().unwrap()) {
83+
header.as_str().split(";").find_map(|key_equals_value| {
84+
let parts = key_equals_value.split("=").collect::<Vec<_>>();
85+
if parts.len() == 2 && parts[0].eq_ignore_ascii_case("for") {
86+
Some(String::from(parts[1]))
87+
} else {
88+
None
89+
}
90+
})
91+
} else if let Some(header) = self.header(&"X-Forwarded-For".parse().unwrap()) {
92+
header
93+
.as_str()
94+
.split(",")
95+
.next()
96+
.map(|client| String::from(client))
97+
} else {
98+
None
99+
}
100+
}
101+
73102
/// Get the HTTP method
74103
pub fn method(&self) -> Method {
75104
self.method
@@ -556,3 +585,97 @@ impl<'a> IntoIterator for &'a mut Request {
556585
self.headers.iter_mut()
557586
}
558587
}
588+
589+
#[cfg(test)]
590+
mod tests {
591+
use super::*;
592+
593+
fn build_test_request() -> Request {
594+
Request::new(Method::Get, "http://irrelevant/".parse().unwrap())
595+
}
596+
597+
fn set_x_forwarded_for(request: &mut Request, client: &'static str) {
598+
request
599+
.insert_header(
600+
"x-forwarded-for",
601+
format!("{},proxy.com,other-proxy.com", client),
602+
)
603+
.unwrap();
604+
}
605+
606+
fn set_forwarded(request: &mut Request, client: &'static str) {
607+
request
608+
.insert_header(
609+
"Forwarded",
610+
format!("by=something.com;for={};host=host.com;proto=http", client),
611+
)
612+
.unwrap();
613+
}
614+
615+
#[test]
616+
fn test_remote_and_forwarded_for_when_forwarded_is_properly_formatted() {
617+
let mut request = build_test_request();
618+
request.peer_addr = Some("127.0.0.1:8000".parse().unwrap());
619+
set_forwarded(&mut request, "127.0.0.1:8001");
620+
621+
assert_eq!(
622+
request.forwarded_for(),
623+
Some(String::from("127.0.0.1:8001"))
624+
);
625+
assert_eq!(request.remote(), Some(String::from("127.0.0.1:8001")));
626+
}
627+
628+
#[test]
629+
fn test_remote_and_forwarded_for_when_forwarded_is_improperly_formatted() {
630+
let mut request = build_test_request();
631+
request.peer_addr = Some("127.0.0.1:8000".parse().unwrap());
632+
633+
request
634+
.insert_header("Forwarded", "this is an improperly ;;; formatted header")
635+
.unwrap();
636+
637+
assert_eq!(request.forwarded_for(), None);
638+
assert_eq!(request.remote(), Some(String::from("127.0.0.1:8000")));
639+
}
640+
641+
#[test]
642+
fn test_remote_and_forwarded_for_when_x_forwarded_for_is_set() {
643+
let mut request = build_test_request();
644+
request.peer_addr = Some("127.0.0.1:8000".parse().unwrap());
645+
set_x_forwarded_for(&mut request, "forwarded-host.com");
646+
647+
assert_eq!(
648+
request.forwarded_for(),
649+
Some(String::from("forwarded-host.com"))
650+
);
651+
652+
assert_eq!(request.remote(), Some(String::from("forwarded-host.com")));
653+
}
654+
655+
#[test]
656+
fn test_remote_and_forwarded_for_when_both_forwarding_headers_are_set() {
657+
let mut request = build_test_request();
658+
set_forwarded(&mut request, "forwarded.com");
659+
set_x_forwarded_for(&mut request, "forwarded-for-client.com");
660+
request.peer_addr = Some("127.0.0.1:8000".parse().unwrap());
661+
662+
assert_eq!(request.forwarded_for(), Some("forwarded.com".into()));
663+
assert_eq!(request.remote(), Some("forwarded.com".into()));
664+
}
665+
666+
#[test]
667+
fn test_remote_and_forwarded_for_falling_back_to_peer_addr() {
668+
let mut request = build_test_request();
669+
request.peer_addr = Some("127.0.0.1:8000".parse().unwrap());
670+
671+
assert_eq!(request.forwarded_for(), None);
672+
assert_eq!(request.remote(), Some("127.0.0.1:8000".into()));
673+
}
674+
675+
#[test]
676+
fn test_remote_and_forwarded_for_when_no_remote_available() {
677+
let request = build_test_request();
678+
assert_eq!(request.forwarded_for(), None);
679+
assert_eq!(request.remote(), None);
680+
}
681+
}

0 commit comments

Comments
 (0)