Skip to content

Commit

Permalink
Add Normalization middleware for in place (actix#783)
Browse files Browse the repository at this point in the history
  • Loading branch information
DoumanAsh authored and fafhrd91 committed Apr 19, 2019
1 parent bc40f5a commit 1e7f97a
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod cors;
mod defaultheaders;
pub mod errhandlers;
mod logger;
pub mod normalize;

pub use self::defaultheaders::DefaultHeaders;
pub use self::logger::Logger;
Expand Down
109 changes: 109 additions & 0 deletions src/middleware/normalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//! `Middleware` to normalize request's URI
use regex::Regex;
use actix_service::{Service, Transform};
use futures::future::{self, FutureResult};

use crate::service::{ServiceRequest, ServiceResponse};

#[derive(Default, Clone, Copy)]
/// `Middleware` to normalize request's URI in place
///
/// Performs following:
///
/// - Merges multiple slashes into one.
pub struct NormalizePath;

impl<S> Transform<S> for NormalizePath
where
S: Service<Request = ServiceRequest, Response = ServiceResponse>,
{
type Request = ServiceRequest;
type Response = ServiceResponse;
type Error = S::Error;
type InitError = ();
type Transform = NormalizePathNormalization<S>;
type Future = FutureResult<Self::Transform, Self::InitError>;

fn new_transform(&self, service: S) -> Self::Future {
future::ok(NormalizePathNormalization {
service,
merge_slash: Regex::new("//+").unwrap()
})
}
}

pub struct NormalizePathNormalization<S> {
service: S,
merge_slash: Regex,
}

impl<S> Service for NormalizePathNormalization<S>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse>,
{
type Request = ServiceRequest;
type Response = ServiceResponse;
type Error = S::Error;
type Future = S::Future;

fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> {
self.service.poll_ready()
}

fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
let head = req.head_mut();

let path = head.uri.path();
let original_len = path.len();
let path = self.merge_slash.replace_all(path, "/");

if original_len != path.len() {
head.uri = path.parse().unwrap();
}

self.service.call(req)
}
}

#[cfg(test)]
mod tests {
use actix_service::FnService;

use super::*;
use crate::dev::ServiceRequest;
use crate::http::header::CONTENT_TYPE;
use crate::test::{block_on, TestRequest};
use crate::HttpResponse;

#[test]
fn test_in_place_normalization() {
let srv = FnService::new(|req: ServiceRequest| {
assert_eq!("/v1/something/", req.path());
req.into_response(HttpResponse::Ok().finish())
});

let mut normalize = block_on(NormalizePath.new_transform(srv)).unwrap();

let req = TestRequest::with_uri("/v1//something////").to_srv_request();
let res = block_on(normalize.call(req)).unwrap();
assert!(res.status().is_success());
}

#[test]
fn should_normalize_nothing() {
const URI: &str = "/v1/something/";

let srv = FnService::new(|req: ServiceRequest| {
assert_eq!(URI, req.path());
req.into_response(HttpResponse::Ok().finish())
});

let mut normalize = block_on(NormalizePath.new_transform(srv)).unwrap();

let req = TestRequest::with_uri(URI).to_srv_request();
let res = block_on(normalize.call(req)).unwrap();
assert!(res.status().is_success());
}

}

0 comments on commit 1e7f97a

Please sign in to comment.