1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-27 17:22:57 +01:00

added ConnectionInfo

This commit is contained in:
Nikolay Kim 2017-12-05 17:09:15 -08:00
parent d8b880e167
commit c3de32c3b3
6 changed files with 180 additions and 31 deletions

View File

@ -42,8 +42,8 @@ fn main() {
.header("LOCATION", "/index.html")
.body(Body::Empty)
})))
.serve_tls::<_, ()>("127.0.0.1:8080", pkcs12).unwrap();
.serve_tls::<_, ()>("127.0.0.1:8443", pkcs12).unwrap();
println!("Started http server: 127.0.0.1:8080");
println!("Started http server: 127.0.0.1:8443");
let _ = sys.run();
}

View File

@ -9,12 +9,14 @@ use url::form_urlencoded;
use http::{header, Uri, Method, Version, HeaderMap, Extensions};
use {Cookie, HttpRange};
use info::ConnectionInfo;
use recognizer::Params;
use payload::Payload;
use multipart::Multipart;
use error::{ParseError, PayloadError,
MultipartError, CookieParseError, HttpRangeError, UrlencodedError};
struct HttpMessage {
version: Version,
method: Method,
@ -27,6 +29,7 @@ struct HttpMessage {
cookies_loaded: bool,
addr: Option<SocketAddr>,
payload: Payload,
info: Option<ConnectionInfo<'static>>,
}
impl Default for HttpMessage {
@ -44,6 +47,7 @@ impl Default for HttpMessage {
addr: None,
payload: Payload::empty(),
extensions: Extensions::new(),
info: None,
}
}
}
@ -70,6 +74,7 @@ impl HttpRequest<()> {
addr: None,
payload: payload,
extensions: Extensions::new(),
info: None,
}),
Rc::new(())
)
@ -106,6 +111,15 @@ impl<S> HttpRequest<S> {
&mut self.as_mut().extensions
}
pub(crate) fn set_prefix(&mut self, idx: usize) {
self.as_mut().prefix = idx;
}
#[doc(hidden)]
pub fn prefix_len(&self) -> usize {
self.0.prefix
}
/// Read the Request Uri.
#[inline]
pub fn uri(&self) -> &Uri { &self.0.uri }
@ -132,13 +146,15 @@ impl<S> HttpRequest<S> {
self.0.uri.path()
}
pub(crate) fn set_prefix(&mut self, idx: usize) {
self.as_mut().prefix = idx;
}
#[doc(hidden)]
pub fn prefix_len(&self) -> usize {
self.0.prefix
/// Load *ConnectionInfo* for currect request.
#[inline]
pub fn load_connection_info(&mut self) -> &ConnectionInfo {
if self.0.info.is_none() {
let info: ConnectionInfo<'static> = unsafe{
mem::transmute(ConnectionInfo::new(self))};
self.as_mut().info = Some(info);
}
self.0.info.as_ref().unwrap()
}
/// Remote IP of client initiated HTTP request.

148
src/info.rs Normal file
View File

@ -0,0 +1,148 @@
use std::str::FromStr;
use http::header::{self, HeaderName};
use httprequest::HttpRequest;
const X_FORWARDED_HOST: &str = "X-FORWARDED-HOST";
const X_FORWARDED_PROTO: &str = "X-FORWARDED-PROTO";
/// `HttpRequest` connection information
///
/// While it is possible to create `ConnectionInfo` directly,
/// consider using `HttpRequest::load_connection_info()` which cache result.
pub struct ConnectionInfo<'a> {
scheme: &'a str,
host: &'a str,
remote: String,
forwarded_for: Vec<&'a str>,
forwarded_by: Vec<&'a str>,
}
impl<'a> ConnectionInfo<'a> {
/// Create *ConnectionInfo* instance for a request.
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();
// 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, '=');
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()),
"proto" => if scheme.is_none() {
scheme = Some(val.trim());
},
"host" => if host.is_none() {
host = Some(val.trim());
},
_ => (),
}
}
}
}
}
}
}
// scheme
if scheme.is_none() {
if let Some(h) = req.headers().get(
HeaderName::from_str(X_FORWARDED_PROTO).unwrap()) {
if let Ok(h) = h.to_str() {
scheme = h.split(',').next().map(|v| v.trim());
}
}
if scheme.is_none() {
if let Some(a) = req.uri().scheme_part() {
scheme = Some(a.as_str())
}
}
}
// host
if host.is_none() {
if let Some(h) = req.headers().get(HeaderName::from_str(X_FORWARDED_HOST).unwrap()) {
if let Ok(h) = h.to_str() {
host = h.split(',').next().map(|v| v.trim());
}
}
if host.is_none() {
if let Some(h) = req.headers().get(header::HOST) {
if let Ok(h) = h.to_str() {
host = Some(h);
}
}
if host.is_none() {
if let Some(a) = req.uri().authority_part() {
host = Some(a.as_str())
}
}
}
}
ConnectionInfo {
scheme: scheme.unwrap_or("http"),
host: host.unwrap_or("localhost"),
remote: String::new(),
forwarded_for: forwarded_for,
forwarded_by: forwarded_by,
}
}
/// Scheme of the request.
///
/// Scheme is resolved through the following headers, in this order:
///
/// - Forwarded
/// - X-Forwarded-Proto
/// - Uri
#[inline]
pub fn scheme(&self) -> &str {
self.scheme
}
/// Hostname of the request.
///
/// Hostname is resolved through the following headers, in this order:
///
/// - Forwarded
/// - X-Forwarded-Host
/// - Host
/// - Uri
pub fn host(&self) -> &str {
self.host
}
/// 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) -> &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
}
}

View File

@ -55,6 +55,7 @@ mod encoding;
mod httprequest;
mod httpresponse;
mod payload;
mod info;
mod route;
mod resource;
mod recognizer;
@ -81,6 +82,7 @@ pub use error::{Error, Result};
pub use encoding::ContentEncoding;
pub use body::{Body, Binary};
pub use application::Application;
pub use info::ConnectionInfo;
pub use httprequest::{HttpRequest, UrlEncoded};
pub use httpresponse::HttpResponse;
pub use payload::{Payload, PayloadItem};

View File

@ -209,28 +209,17 @@ pub struct RouteRecognizer<T> {
patterns: HashMap<String, Pattern>,
}
impl<T> Default for RouteRecognizer<T> {
fn default() -> Self {
RouteRecognizer {
prefix: 0,
re: RegexSet::new([""].iter()).unwrap(),
routes: Vec::new(),
patterns: HashMap::new(),
}
}
}
impl<T> RouteRecognizer<T> {
pub fn new<P: Into<String>, U>(prefix: P, routes: U) -> Self
where U: IntoIterator<Item=(String, Option<String>, T)>
pub fn new<P: Into<String>, U, K>(prefix: P, routes: U) -> Self
where U: IntoIterator<Item=(K, Option<String>, T)>,
K: Into<String>,
{
let mut paths = Vec::new();
let mut handlers = Vec::new();
let mut patterns = HashMap::new();
for item in routes {
let (pat, elements) = parse(&item.0);
let (pat, elements) = parse(&item.0.into());
let pattern = Pattern::new(&pat, elements);
if let Some(ref name) = item.1 {
let _ = patterns.insert(name.clone(), pattern.clone());
@ -399,8 +388,6 @@ mod tests {
#[test]
fn test_recognizer() {
let mut rec = RouteRecognizer::<usize>::default();
let routes = vec![
("/name", None, 1),
("/name/{val}", None, 2),
@ -408,7 +395,7 @@ mod tests {
("/v{val}/{val2}/index.html", None, 4),
("/v/{tail:.*}", None, 5),
];
rec.set_routes(routes);
let mut rec = RouteRecognizer::new("/", routes);
let (params, val) = rec.recognize("/name").unwrap();
assert_eq!(*val, 1);

View File

@ -66,13 +66,9 @@ use wsframe;
use wsproto::*;
pub use wsproto::CloseCode;
#[doc(hidden)]
const SEC_WEBSOCKET_ACCEPT: &str = "SEC-WEBSOCKET-ACCEPT";
#[doc(hidden)]
const SEC_WEBSOCKET_KEY: &str = "SEC-WEBSOCKET-KEY";
#[doc(hidden)]
const SEC_WEBSOCKET_VERSION: &str = "SEC-WEBSOCKET-VERSION";
// #[doc(hidden)]
// const SEC_WEBSOCKET_PROTOCOL: &'static str = "SEC-WEBSOCKET-PROTOCOL";