1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-23 23:51:06 +01:00

add ConnectionInfo tests

This commit is contained in:
Nikolay Kim 2017-12-05 21:38:52 -08:00
parent c3de32c3b3
commit d7e65b6212
6 changed files with 114 additions and 42 deletions

View File

@ -175,7 +175,7 @@ impl<T, H> Http1<T, H>
not_ready = false;
// set remote addr
req.set_remove_addr(self.addr);
req.set_peer_addr(self.addr);
// stop keepalive timer
self.keepalive_timer.take();

View File

@ -223,7 +223,7 @@ impl Entry {
parts.method, parts.uri, parts.version, parts.headers, payload);
// set remote addr
req.set_remove_addr(addr);
req.set_peer_addr(addr);
// Payload sender
let psender = PayloadType::new(req.headers(), psender);

View File

@ -140,12 +140,27 @@ impl<S> HttpRequest<S> {
&self.0.headers
}
#[cfg(test)]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.as_mut().headers
}
/// The target path of this Request.
#[inline]
pub fn path(&self) -> &str {
self.0.uri.path()
}
/// Get previously loaded *ConnectionInfo*.
#[inline]
pub fn connection_info(&self) -> Option<&ConnectionInfo> {
if self.0.info.is_none() {
None
} else {
self.0.info.as_ref()
}
}
/// Load *ConnectionInfo* for currect request.
#[inline]
pub fn load_connection_info(&mut self) -> &ConnectionInfo {
@ -157,19 +172,12 @@ impl<S> HttpRequest<S> {
self.0.info.as_ref().unwrap()
}
/// Remote IP of client initiated HTTP request.
///
/// The IP is resolved through the following headers, in this order:
///
/// - Forwarded
/// - X-Forwarded-For
/// - peername of opened socket
#[inline]
pub fn remote(&self) -> Option<&SocketAddr> {
pub fn peer_addr(&self) -> Option<&SocketAddr> {
self.0.addr.as_ref()
}
pub(crate) fn set_remove_addr(&mut self, addr: Option<SocketAddr>) {
pub(crate) fn set_peer_addr(&mut self, addr: Option<SocketAddr>) {
self.as_mut().addr = addr
}

View File

@ -2,6 +2,7 @@ use std::str::FromStr;
use http::header::{self, HeaderName};
use httprequest::HttpRequest;
const X_FORWARDED_FOR: &str = "X-FORWARDED-FOR";
const X_FORWARDED_HOST: &str = "X-FORWARDED-HOST";
const X_FORWARDED_PROTO: &str = "X-FORWARDED-PROTO";
@ -13,9 +14,8 @@ const X_FORWARDED_PROTO: &str = "X-FORWARDED-PROTO";
pub struct ConnectionInfo<'a> {
scheme: &'a str,
host: &'a str,
remote: String,
forwarded_for: Vec<&'a str>,
forwarded_by: Vec<&'a str>,
remote: Option<&'a str>,
peer: Option<String>,
}
impl<'a> ConnectionInfo<'a> {
@ -24,20 +24,21 @@ impl<'a> ConnectionInfo<'a> {
pub fn new<S>(req: &'a HttpRequest<S>) -> ConnectionInfo<'a> {
let mut host = None;
let mut scheme = None;
let mut forwarded_for = Vec::new();
let mut forwarded_by = Vec::new();
let mut remote = None;
let mut peer = None;
// load forwarded header
for hdr in req.headers().get_all(header::FORWARDED) {
if let Ok(val) = hdr.to_str() {
for pair in val.split(';') {
for el in pair.split(',') {
let mut items = el.splitn(1, '=');
let mut items = el.trim().splitn(2, '=');
if let Some(name) = items.next() {
if let Some(val) = items.next() {
match &name.to_lowercase() as &str {
"for" => forwarded_for.push(val.trim()),
"by" => forwarded_by.push(val.trim()),
"for" => if remote.is_none() {
remote = Some(val.trim());
},
"proto" => if scheme.is_none() {
scheme = Some(val.trim());
},
@ -89,12 +90,27 @@ impl<'a> ConnectionInfo<'a> {
}
}
// remote addr
if remote.is_none() {
if let Some(h) = req.headers().get(
HeaderName::from_str(X_FORWARDED_FOR).unwrap()) {
if let Ok(h) = h.to_str() {
remote = h.split(',').next().map(|v| v.trim());
}
}
if remote.is_none() {
if let Some(addr) = req.peer_addr() {
// get peeraddr from socketaddr
peer = Some(format!("{}", addr));
}
}
}
ConnectionInfo {
scheme: scheme.unwrap_or("http"),
host: host.unwrap_or("localhost"),
remote: String::new(),
forwarded_for: forwarded_for,
forwarded_by: forwarded_by,
remote: remote,
peer: peer,
}
}
@ -130,19 +146,66 @@ impl<'a> ConnectionInfo<'a> {
/// - X-Forwarded-For
/// - peername of opened socket
#[inline]
pub fn remote(&self) -> &str {
&self.remote
}
/// List of the nodes making the request to the proxy.
#[inline]
pub fn forwarded_for(&self) -> &Vec<&str> {
&self.forwarded_for
}
/// List of the user-agent facing interface of the proxies
#[inline]
pub fn forwarded_by(&self) -> &Vec<&str> {
&self.forwarded_by
pub fn remote(&self) -> Option<&str> {
if let Some(r) = self.remote {
Some(r)
} else if let Some(ref peer) = self.peer {
Some(peer)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use http::header::HeaderValue;
#[test]
fn test_forwarded() {
let req = HttpRequest::default();
let info = ConnectionInfo::new(&req);
assert_eq!(info.scheme(), "http");
assert_eq!(info.host(), "localhost");
let mut req = HttpRequest::default();
req.headers_mut().insert(
header::FORWARDED,
HeaderValue::from_static(
"for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org"));
let info = ConnectionInfo::new(&req);
assert_eq!(info.scheme(), "https");
assert_eq!(info.host(), "rust-lang.org");
assert_eq!(info.remote(), Some("192.0.2.60"));
let mut req = HttpRequest::default();
req.headers_mut().insert(
header::HOST, HeaderValue::from_static("rust-lang.org"));
let info = ConnectionInfo::new(&req);
assert_eq!(info.scheme(), "http");
assert_eq!(info.host(), "rust-lang.org");
assert_eq!(info.remote(), None);
let mut req = HttpRequest::default();
req.headers_mut().insert(
HeaderName::from_str(X_FORWARDED_FOR).unwrap(), HeaderValue::from_static("192.0.2.60"));
let info = ConnectionInfo::new(&req);
assert_eq!(info.remote(), Some("192.0.2.60"));
let mut req = HttpRequest::default();
req.headers_mut().insert(
HeaderName::from_str(X_FORWARDED_HOST).unwrap(), HeaderValue::from_static("192.0.2.60"));
let info = ConnectionInfo::new(&req);
assert_eq!(info.host(), "192.0.2.60");
assert_eq!(info.remote(), None);
let mut req = HttpRequest::default();
req.headers_mut().insert(
HeaderName::from_str(X_FORWARDED_PROTO).unwrap(), HeaderValue::from_static("https"));
let info = ConnectionInfo::new(&req);
assert_eq!(info.scheme(), "https");
}
}

View File

@ -102,6 +102,7 @@ impl Logger {
impl Middleware for Logger {
fn start(&self, req: &mut HttpRequest) -> Started {
req.load_connection_info();
req.extensions().insert(StartTime(time::now()));
Started::Done
}
@ -112,7 +113,6 @@ impl Middleware for Logger {
}
}
/// A formatting style for the `Logger`, consisting of multiple
/// `FormatText`s concatenated into one line.
#[derive(Clone)]
@ -237,11 +237,12 @@ impl FormatText {
fmt.write_fmt(format_args!("{:.6}", response_time_ms))
},
FormatText::RemoteAddr => {
if let Some(addr) = req.remote() {
addr.fmt(fmt)
} else {
"-".fmt(fmt)
if let Some(addr) = req.connection_info() {
if let Some(remote) = addr.remote() {
return remote.fmt(fmt);
}
}
"-".fmt(fmt)
}
FormatText::RequestTime => {
entry_time.strftime("[%d/%b/%Y:%H:%M:%S %z]")

View File

@ -395,7 +395,7 @@ mod tests {
("/v{val}/{val2}/index.html", None, 4),
("/v/{tail:.*}", None, 5),
];
let mut rec = RouteRecognizer::new("/", routes);
let rec = RouteRecognizer::new("/", routes);
let (params, val) = rec.recognize("/name").unwrap();
assert_eq!(*val, 1);