mirror of https://github.com/rwf2/Rocket.git
Expose the remote address via `remote()` in `Request`.
This commit also includes the following changes: * `FromRequest` for `SocketAddr` implemented: extracts remote address. * All built-in `FromRequest` implementations are documented. * Request preprocessing overrides remote IP with value from X-Real-IP header. * `MockRequest` allows setting the remote address with `remote()`. Resolves #38.
This commit is contained in:
parent
725191d3c3
commit
41aecc3e7f
|
@ -1,4 +1,5 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use outcome::{self, IntoOutcome};
|
use outcome::{self, IntoOutcome};
|
||||||
use request::Request;
|
use request::Request;
|
||||||
|
@ -65,6 +66,65 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
|
||||||
/// matching request. Note that users can request an `Option<S>` to catch
|
/// matching request. Note that users can request an `Option<S>` to catch
|
||||||
/// `Forward`s.
|
/// `Forward`s.
|
||||||
///
|
///
|
||||||
|
/// # Provided Implementations
|
||||||
|
///
|
||||||
|
/// Rocket implements `FromRequest` for several built-in types. Their behavior
|
||||||
|
/// is documented here.
|
||||||
|
///
|
||||||
|
/// * **URI**
|
||||||
|
///
|
||||||
|
/// Extracts the [URI](/rocket/http/uri/struct.URI.html) from the incoming
|
||||||
|
/// request.
|
||||||
|
///
|
||||||
|
/// _This implementation always returns successfully._
|
||||||
|
///
|
||||||
|
/// * **Method**
|
||||||
|
///
|
||||||
|
/// Extracts the [Method](/rocket/http/enum.Method.html) from the incoming
|
||||||
|
/// request.
|
||||||
|
///
|
||||||
|
/// _This implementation always returns successfully._
|
||||||
|
///
|
||||||
|
/// * **&Cookies**
|
||||||
|
///
|
||||||
|
/// Returns a borrow to the [Cookies](/rocket/http/type.Cookies.html) in the
|
||||||
|
/// incoming request. Note that `Cookies` implements internal mutability, so
|
||||||
|
/// a handle to `&Cookies` allows you to get _and_ set cookies in the
|
||||||
|
/// request.
|
||||||
|
///
|
||||||
|
/// _This implementation always returns successfully._
|
||||||
|
///
|
||||||
|
/// * **ContentType**
|
||||||
|
///
|
||||||
|
/// Extracts the [ContentType](/rocket/http/struct.ContentType.html) from
|
||||||
|
/// the incoming request. If the request didn't specify a Content-Type, a
|
||||||
|
/// Content-Type of `*/*` (`Any`) is returned.
|
||||||
|
///
|
||||||
|
/// _This implementation always returns successfully._
|
||||||
|
///
|
||||||
|
/// * **SocketAddr**
|
||||||
|
///
|
||||||
|
/// Extracts the remote address of the incoming request as a `SocketAddr`.
|
||||||
|
/// If the remote address is not known, the request is forwarded.
|
||||||
|
///
|
||||||
|
/// _This implementation always returns successfully._
|
||||||
|
///
|
||||||
|
/// * **Option<T>** _where_ **T: FromRequest**
|
||||||
|
///
|
||||||
|
/// The type `T` is derived from the incoming request using `T`'s
|
||||||
|
/// `FromRequest` implementation. If the derivation is a `Success`, the
|
||||||
|
/// dervived value is returned in `Some`. Otherwise, a `None` is returned.
|
||||||
|
///
|
||||||
|
/// _This implementation always returns successfully._
|
||||||
|
///
|
||||||
|
/// * **Result<T, T::Error>** _where_ **T: FromRequest**
|
||||||
|
///
|
||||||
|
/// The type `T` is derived from the incoming request using `T`'s
|
||||||
|
/// `FromRequest` implementation. If derivation is a `Success`, the value is
|
||||||
|
/// returned in `Ok`. If the derivation is a `Failure`, the error value is
|
||||||
|
/// returned in `Err`. If the derivation is a `Forward`, the request is
|
||||||
|
/// forwarded.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// Imagine you're running an authenticated API service that requires that some
|
/// Imagine you're running an authenticated API service that requires that some
|
||||||
|
@ -161,6 +221,17 @@ impl<'a, 'r> FromRequest<'a, 'r> for ContentType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'r> FromRequest<'a, 'r> for SocketAddr {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
|
||||||
|
match request.remote() {
|
||||||
|
Some(addr) => Success(addr),
|
||||||
|
None => Forward(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'r, T: FromRequest<'a, 'r>> FromRequest<'a, 'r> for Result<T, T::Error> {
|
impl<'a, 'r, T: FromRequest<'a, 'r>> FromRequest<'a, 'r> for Result<T, T::Error> {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use term_painter::Color::*;
|
use term_painter::Color::*;
|
||||||
|
@ -24,6 +25,7 @@ pub struct Request<'r> {
|
||||||
method: Method,
|
method: Method,
|
||||||
uri: URI<'r>,
|
uri: URI<'r>,
|
||||||
headers: HeaderMap<'r>,
|
headers: HeaderMap<'r>,
|
||||||
|
remote: Option<SocketAddr>,
|
||||||
params: RefCell<Vec<(usize, usize)>>,
|
params: RefCell<Vec<(usize, usize)>>,
|
||||||
cookies: Cookies,
|
cookies: Cookies,
|
||||||
}
|
}
|
||||||
|
@ -46,6 +48,7 @@ impl<'r> Request<'r> {
|
||||||
method: method,
|
method: method,
|
||||||
uri: uri.into(),
|
uri: uri.into(),
|
||||||
headers: HeaderMap::new(),
|
headers: HeaderMap::new(),
|
||||||
|
remote: None,
|
||||||
params: RefCell::new(Vec::new()),
|
params: RefCell::new(Vec::new()),
|
||||||
cookies: Cookies::new(&[]),
|
cookies: Cookies::new(&[]),
|
||||||
}
|
}
|
||||||
|
@ -123,6 +126,49 @@ impl<'r> Request<'r> {
|
||||||
self.params = RefCell::new(Vec::new());
|
self.params = RefCell::new(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the address of the remote connection that initiated this
|
||||||
|
/// request if the address is known. If the address is not known, `None` is
|
||||||
|
/// returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::Request;
|
||||||
|
/// use rocket::http::Method;
|
||||||
|
///
|
||||||
|
/// let request = Request::new(Method::Get, "/uri");
|
||||||
|
/// assert!(request.remote().is_none());
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn remote(&self) -> Option<SocketAddr> {
|
||||||
|
self.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the remote address of `self` to `address`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Set the remote address to be 127.0.0.1:8000:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::Request;
|
||||||
|
/// use rocket::http::Method;
|
||||||
|
/// use std::net::{SocketAddr, IpAddr, Ipv4Addr};
|
||||||
|
///
|
||||||
|
/// let mut request = Request::new(Method::Get, "/uri");
|
||||||
|
///
|
||||||
|
/// let (ip, port) = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000);
|
||||||
|
/// let localhost = SocketAddr::new(ip, port);
|
||||||
|
/// request.set_remote(localhost);
|
||||||
|
///
|
||||||
|
/// assert_eq!(request.remote(), Some(localhost));
|
||||||
|
/// ```
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_remote(&mut self, address: SocketAddr) {
|
||||||
|
self.remote = Some(address);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a `HeaderMap` of all of the headers in `self`.
|
/// Returns a `HeaderMap` of all of the headers in `self`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -185,8 +231,8 @@ impl<'r> Request<'r> {
|
||||||
|
|
||||||
/// Returns a borrow to the cookies in `self`.
|
/// Returns a borrow to the cookies in `self`.
|
||||||
///
|
///
|
||||||
/// Note that `Cookie` implements internal mutability, so this method allows
|
/// Note that `Cookies` implements internal mutability, so this method
|
||||||
/// you to get _and_ set cookies in `self`.
|
/// allows you to get _and_ set cookies in `self`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -274,6 +320,7 @@ impl<'r> Request<'r> {
|
||||||
/// Set `self`'s parameters given that the route used to reach this request
|
/// Set `self`'s parameters given that the route used to reach this request
|
||||||
/// was `route`. This should only be used internally by `Rocket` as improper
|
/// was `route`. This should only be used internally by `Rocket` as improper
|
||||||
/// use may result in out of bounds indexing.
|
/// use may result in out of bounds indexing.
|
||||||
|
/// TODO: Figure out the mount path from here.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_params(&self, route: &Route) {
|
pub fn set_params(&self, route: &Route) {
|
||||||
|
@ -348,8 +395,9 @@ impl<'r> Request<'r> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn from_hyp(h_method: hyper::Method,
|
pub fn from_hyp(h_method: hyper::Method,
|
||||||
h_headers: hyper::header::Headers,
|
h_headers: hyper::header::Headers,
|
||||||
h_uri: hyper::RequestUri)
|
h_uri: hyper::RequestUri,
|
||||||
-> Result<Request<'static>, String> {
|
h_addr: SocketAddr,
|
||||||
|
) -> Result<Request<'static>, String> {
|
||||||
// Get a copy of the URI for later use.
|
// Get a copy of the URI for later use.
|
||||||
let uri = match h_uri {
|
let uri = match h_uri {
|
||||||
hyper::RequestUri::AbsolutePath(s) => s,
|
hyper::RequestUri::AbsolutePath(s) => s,
|
||||||
|
@ -376,6 +424,9 @@ impl<'r> Request<'r> {
|
||||||
request.add_header(header);
|
request.add_header(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the remote address.
|
||||||
|
request.set_remote(h_addr);
|
||||||
|
|
||||||
Ok(request)
|
Ok(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::from_utf8_unchecked;
|
use std::str::from_utf8_unchecked;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use term_painter::Color::*;
|
use term_painter::Color::*;
|
||||||
|
@ -41,11 +42,11 @@ impl hyper::Handler for Rocket {
|
||||||
hyp_req: hyper::Request<'h, 'k>,
|
hyp_req: hyper::Request<'h, 'k>,
|
||||||
res: hyper::FreshResponse<'h>) {
|
res: hyper::FreshResponse<'h>) {
|
||||||
// Get all of the information from Hyper.
|
// Get all of the information from Hyper.
|
||||||
let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
|
let (h_addr, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
|
||||||
|
|
||||||
// Convert the Hyper request into a Rocket request.
|
// Convert the Hyper request into a Rocket request.
|
||||||
let mut request = match Request::from_hyp(h_method, h_headers, h_uri) {
|
let mut req = match Request::from_hyp(h_method, h_headers, h_uri, h_addr) {
|
||||||
Ok(request) => request,
|
Ok(req) => req,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Bad incoming request: {}", e);
|
error!("Bad incoming request: {}", e);
|
||||||
let dummy = Request::new(Method::Get, URI::new("<unknown>"));
|
let dummy = Request::new(Method::Get, URI::new("<unknown>"));
|
||||||
|
@ -59,13 +60,13 @@ impl hyper::Handler for Rocket {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
error_!("Bad data in request: {}", reason);
|
error_!("Bad data in request: {}", reason);
|
||||||
let r = self.handle_error(Status::InternalServerError, &request);
|
let r = self.handle_error(Status::InternalServerError, &req);
|
||||||
return self.issue_response(r, res);
|
return self.issue_response(r, res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dispatch the request to get a response, then write that response out.
|
// Dispatch the request to get a response, then write that response out.
|
||||||
let response = self.dispatch(&mut request, data);
|
let response = self.dispatch(&mut req, data);
|
||||||
self.issue_response(response, res)
|
self.issue_response(response, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,15 +133,33 @@ impl Rocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Preprocess the request for Rocket-specific things. At this time, we're
|
/// Preprocess the request for Rocket things. Currently, this means:
|
||||||
/// only checking for _method in forms. Keep this in-sync with derive_form
|
///
|
||||||
/// when preprocessing form fields.
|
/// * Rewriting the method in the request if _method form field exists.
|
||||||
|
/// * Rewriting the remote IP if the 'X-Real-IP' header is set.
|
||||||
|
///
|
||||||
|
/// Keep this in-sync with derive_form when preprocessing form fields.
|
||||||
fn preprocess_request(&self, req: &mut Request, data: &Data) {
|
fn preprocess_request(&self, req: &mut Request, data: &Data) {
|
||||||
|
// Rewrite the remote IP address. The request must already have an
|
||||||
|
// address associated with it to do this since we need to know the port.
|
||||||
|
if let Some(current) = req.remote() {
|
||||||
|
let ip = req.headers()
|
||||||
|
.get_one("X-Real-IP")
|
||||||
|
.and_then(|ip_str| ip_str.parse().map_err(|_| {
|
||||||
|
warn_!("The 'X-Real-IP' header is malformed: {}", ip_str)
|
||||||
|
}).ok());
|
||||||
|
|
||||||
|
if let Some(ip) = ip {
|
||||||
|
req.set_remote(SocketAddr::new(ip, current.port()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this is a form and if the form contains the special _method
|
// Check if this is a form and if the form contains the special _method
|
||||||
// field which we use to reinterpret the request's method.
|
// field which we use to reinterpret the request's method.
|
||||||
let data_len = data.peek().len();
|
let data_len = data.peek().len();
|
||||||
let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
|
let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
|
||||||
if req.method() == Method::Post && req.content_type().is_form() && data_len >= min_len {
|
let is_form = req.content_type().is_form();
|
||||||
|
if is_form && req.method() == Method::Post && data_len >= min_len {
|
||||||
let form = unsafe {
|
let form = unsafe {
|
||||||
from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
|
from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
|
||||||
};
|
};
|
||||||
|
@ -157,6 +176,8 @@ impl Rocket {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn dispatch<'r>(&self, request: &'r mut Request, data: Data) -> Response<'r> {
|
pub fn dispatch<'r>(&self, request: &'r mut Request, data: Data) -> Response<'r> {
|
||||||
|
info!("{}:", request);
|
||||||
|
|
||||||
// Do a bit of preprocessing before routing.
|
// Do a bit of preprocessing before routing.
|
||||||
self.preprocess_request(request, &data);
|
self.preprocess_request(request, &data);
|
||||||
|
|
||||||
|
@ -207,7 +228,6 @@ impl Rocket {
|
||||||
pub fn route<'r>(&self, request: &'r Request, mut data: Data)
|
pub fn route<'r>(&self, request: &'r Request, mut data: Data)
|
||||||
-> handler::Outcome<'r> {
|
-> handler::Outcome<'r> {
|
||||||
// Go through the list of matching routes until we fail or succeed.
|
// Go through the list of matching routes until we fail or succeed.
|
||||||
info!("{}:", request);
|
|
||||||
let matches = self.router.route(request);
|
let matches = self.router.route(request);
|
||||||
for route in matches {
|
for route in matches {
|
||||||
// Retrieve and set the requests parameters.
|
// Retrieve and set the requests parameters.
|
||||||
|
|
|
@ -108,6 +108,8 @@
|
||||||
use ::{Rocket, Request, Response, Data};
|
use ::{Rocket, Request, Response, Data};
|
||||||
use http::{Method, Header, Cookie};
|
use http::{Method, Header, Cookie};
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
/// A type for mocking requests for testing Rocket applications.
|
/// A type for mocking requests for testing Rocket applications.
|
||||||
pub struct MockRequest {
|
pub struct MockRequest {
|
||||||
request: Request<'static>,
|
request: Request<'static>,
|
||||||
|
@ -143,6 +145,44 @@ impl MockRequest {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the remote address of this request.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Set the remote address to "8.8.8.8:80":
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::Method::*;
|
||||||
|
/// use rocket::testing::MockRequest;
|
||||||
|
///
|
||||||
|
/// let address = "8.8.8.8:80".parse().unwrap();
|
||||||
|
/// let req = MockRequest::new(Get, "/").remote(address);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn remote(mut self, address: SocketAddr) -> Self {
|
||||||
|
self.request.set_remote(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a header to this request. Does not consume `self`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Add the Content-Type header:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::Method::*;
|
||||||
|
/// use rocket::testing::MockRequest;
|
||||||
|
/// use rocket::http::ContentType;
|
||||||
|
///
|
||||||
|
/// let mut req = MockRequest::new(Get, "/");
|
||||||
|
/// req.add_header(ContentType::JSON);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn add_header<'h, H: Into<Header<'static>>>(&mut self, header: H) {
|
||||||
|
self.request.add_header(header.into());
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a cookie to this request.
|
/// Add a cookie to this request.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
#![feature(plugin, custom_derive)]
|
||||||
|
#![plugin(rocket_codegen)]
|
||||||
|
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn get_ip(remote: SocketAddr) -> String {
|
||||||
|
remote.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "testing")]
|
||||||
|
mod remote_rewrite_tests {
|
||||||
|
use super::*;
|
||||||
|
use rocket::testing::MockRequest;
|
||||||
|
use rocket::http::Method::*;
|
||||||
|
use rocket::http::{Header, Status};
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
const KNOWN_IP: &'static str = "127.0.0.1:8000";
|
||||||
|
|
||||||
|
fn check_ip(header: Option<Header<'static>>, ip: Option<String>) {
|
||||||
|
let address: SocketAddr = KNOWN_IP.parse().unwrap();
|
||||||
|
let port = address.port();
|
||||||
|
|
||||||
|
let rocket = rocket::ignite().mount("/", routes![get_ip]);
|
||||||
|
let mut req = MockRequest::new(Get, "/").remote(address);
|
||||||
|
if let Some(header) = header {
|
||||||
|
req.add_header(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut response = req.dispatch_with(&rocket);
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
let body_str = response.body().and_then(|b| b.into_string());
|
||||||
|
match ip {
|
||||||
|
Some(ip) => assert_eq!(body_str, Some(format!("{}:{}", ip, port))),
|
||||||
|
None => assert_eq!(body_str, Some(KNOWN_IP.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x_real_ip_rewrites() {
|
||||||
|
let ip = "8.8.8.8";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), Some(ip.to_string()));
|
||||||
|
|
||||||
|
let ip = "129.120.111.200";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), Some(ip.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x_real_ip_rewrites_ipv6() {
|
||||||
|
let ip = "2001:db8:0:1:1:1:1:1";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), Some(format!("[{}]", ip)));
|
||||||
|
|
||||||
|
let ip = "2001:db8::2:1";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), Some(format!("[{}]", ip)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uncased_header_rewrites() {
|
||||||
|
let ip = "8.8.8.8";
|
||||||
|
check_ip(Some(Header::new("x-REAL-ip", ip)), Some(ip.to_string()));
|
||||||
|
|
||||||
|
let ip = "1.2.3.4";
|
||||||
|
check_ip(Some(Header::new("x-real-ip", ip)), Some(ip.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_header_no_rewrite() {
|
||||||
|
check_ip(Some(Header::new("real-ip", "?")), None);
|
||||||
|
check_ip(None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_header_doesnt_rewrite() {
|
||||||
|
let ip = "092348092348";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), None);
|
||||||
|
|
||||||
|
let ip = "1200:100000:0120129";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), None);
|
||||||
|
|
||||||
|
let ip = "192.168.1.900";
|
||||||
|
check_ip(Some(Header::new("X-Real-IP", ip)), None);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue