From 8a0a972e130813a47ec43778c02aaf486d622753 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Fri, 19 Oct 2018 21:26:12 +0100 Subject: [PATCH] fix: redirects: upgrade http location headers + forward status codes - fixes #28,#26 --- src/lib/headers.rs | 3 ++- src/lib/proxy_transform.rs | 31 ++++++++++++-------------- src/lib/proxy_utils.rs | 1 + src/lib/with_body.rs | 6 ++--- src/lib/without_body.rs | 14 ++++++------ src/main.rs | 3 +-- tests/proxy_transform_test.rs | 41 +++++++++++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 31 deletions(-) diff --git a/src/lib/headers.rs b/src/lib/headers.rs index 225e970..08f35fd 100644 --- a/src/lib/headers.rs +++ b/src/lib/headers.rs @@ -8,7 +8,8 @@ use regex::Regex; /// from any set-cookies /// pub fn clone_headers(headers: &HeaderMap, target: String, replacer: String) -> HeaderMap { - let regex = Regex::new(target.as_str()).unwrap(); + let matcher = format!("https?://{}", target); + let regex = Regex::new(&matcher).unwrap(); let mut hm = HeaderMap::new(); for (key, value) in headers.iter().filter(|(key, _)| key.as_str() != "cookie") { let strs = value.to_str().unwrap(); diff --git a/src/lib/proxy_transform.rs b/src/lib/proxy_transform.rs index 1617f16..784a74d 100644 --- a/src/lib/proxy_transform.rs +++ b/src/lib/proxy_transform.rs @@ -1,6 +1,7 @@ use actix::Actor; use actix_web::client::ClientConnector; use actix_web::client::ClientRequestBuilder; +use actix_web::http::StatusCode; use actix_web::http::{header, HeaderMap, Method}; use actix_web::{client, dev, http, Error, HttpMessage, HttpRequest, HttpResponse}; use app_state::AppState; @@ -22,27 +23,26 @@ pub fn proxy_transform( original_request: &HttpRequest, ) -> Box> { let outgoing = proxy_req_setup(original_request); + let bind_port = original_request.state().opts.port; + let scheme = &original_request.state().opts.scheme; + let (host, port) = get_host_port(original_request, bind_port); + let req_target = format!("{}://{}:{}", scheme, host, port); match *original_request.method() { - Method::POST => forward_request_with_body(original_request, outgoing), - _ => forward_request_without_body(original_request, outgoing), + Method::POST => forward_request_with_body(original_request, req_target, outgoing), + _ => forward_request_without_body(original_request, req_target, outgoing), } } pub fn proxy_req_setup(original_request: &HttpRequest) -> ClientRequestBuilder { debug!( - "incoming proxy_req = {}", - original_request.uri().to_string() + "incoming proxy_req = {:?}", + original_request.connection_info().host() ); - let original_req_headers = original_request.headers().clone(); - let next_host = original_request.uri().clone(); - let req_host = next_host.host().unwrap_or(""); - let req_port = next_host.port().unwrap_or(80); - let req_target = format!("{}:{}", req_host, req_port); let cloned = clone_headers( &original_req_headers, - req_target, + original_request.connection_info().host().to_string(), original_request.state().opts.target.clone(), ); @@ -118,11 +118,13 @@ pub fn proxy_req_setup(original_request: &HttpRequest) -> ClientReques } pub fn create_outgoing( + status_code: &StatusCode, resp_headers: &HeaderMap, target: String, replacer: String, ) -> dev::HttpResponseBuilder { let mut outgoing = HttpResponse::Ok(); + outgoing.status(*status_code); let c = clone_headers(resp_headers, target, replacer); debug!("Headers for response = {:#?}", c); // Copy headers from backend response to main response @@ -133,13 +135,8 @@ pub fn create_outgoing( } pub fn get_host_port(incoming_request: &HttpRequest, bind_port: u16) -> (String, u16) { - let split = match incoming_request.headers().get(header::HOST) { - Some(h) => { - let output: Vec<&str> = h.to_str().expect("host to str").split(":").collect(); - output - } - None => vec![], - }; + let info = incoming_request.connection_info(); + let split: Vec<&str> = info.host().split(":").collect(); match (split.get(0), split.get(1)) { (Some(h), Some(p)) => (h.to_string(), p.parse().expect("parsed port")), diff --git a/src/lib/proxy_utils.rs b/src/lib/proxy_utils.rs index 11d8a59..8d4aaf9 100644 --- a/src/lib/proxy_utils.rs +++ b/src/lib/proxy_utils.rs @@ -38,6 +38,7 @@ where let next_body: String = String::from(body_content); Ok(create_outgoing( + &proxy_response.status(), &proxy_response.headers(), target_domain.to_string(), req_target, diff --git a/src/lib/with_body.rs b/src/lib/with_body.rs index 6c6c6c2..c813979 100644 --- a/src/lib/with_body.rs +++ b/src/lib/with_body.rs @@ -12,10 +12,10 @@ use proxy_transform::create_outgoing; /// pub fn forward_request_with_body( _req: &HttpRequest, + req_target: String, mut outgoing: ClientRequestBuilder, ) -> Box> { let next_target = _req.state().opts.target.clone(); - let next_host = _req.uri().clone(); let output = _req.body().from_err().and_then(move |incoming_body| { outgoing .body(incoming_body) @@ -23,14 +23,12 @@ pub fn forward_request_with_body( .send() .map_err(Error::from) .and_then(move |proxy_response| { - let req_host = next_host.host().unwrap_or(""); - let req_port = next_host.port().unwrap_or(80); - let req_target = format!("{}:{}", req_host, req_port); proxy_response .body() .from_err() .and_then(move |proxy_response_body| { Ok(create_outgoing( + &proxy_response.status(), &proxy_response.headers(), next_target.to_string(), req_target, diff --git a/src/lib/without_body.rs b/src/lib/without_body.rs index 05759dc..0cb9dfb 100644 --- a/src/lib/without_body.rs +++ b/src/lib/without_body.rs @@ -19,6 +19,7 @@ use rewrites::{replace_host, RewriteContext}; /// pub fn forward_request_without_body( incoming_request: &HttpRequest, + req_target: String, mut outgoing: ClientRequestBuilder, ) -> Box> { let target_domain = incoming_request.state().opts.target.clone(); @@ -48,6 +49,7 @@ pub fn forward_request_without_body( proxy_response, host, port, + req_target, target_domain, rewrites, )) @@ -56,8 +58,7 @@ pub fn forward_request_without_body( // so we just stream it back to the client Either::B(pass_through_response( proxy_response, - host, - port, + req_target.clone(), target_domain, )) } @@ -67,13 +68,11 @@ pub fn forward_request_without_body( /// Pass-through response fn pass_through_response( proxy_response: ClientResponse, - host: String, - port: u16, + req_target: String, target_domain: String, ) -> Box> { - let req_target = format!("{}:{}", host, port); - let output = ok(create_outgoing( + &proxy_response.status(), &proxy_response.headers(), target_domain.to_string(), req_target, @@ -92,6 +91,7 @@ fn response_from_rewrite( proxy_response: ClientResponse, req_host: String, req_port: u16, + req_target: String, target_domain: String, rewrites: RewriteFns, ) -> Box> { @@ -102,7 +102,6 @@ fn response_from_rewrite( .and_then(move |body| { use std::str; - let req_target = format!("{}:{}", req_host, req_port); let context = RewriteContext { host_to_replace: target_domain.clone(), target_host: req_host, @@ -119,6 +118,7 @@ fn response_from_rewrite( debug!("creating response"); Ok(create_outgoing( + &proxy_response.status(), &proxy_response.headers(), target_domain.to_string(), req_target, diff --git a/src/main.rs b/src/main.rs index 8aa99d7..69fbba1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,9 +14,8 @@ extern crate serde_yaml; extern crate tempdir; extern crate url; -#[macro_use] -extern crate log; extern crate env_logger; +extern crate log; use bs::options::ProgramOptions; use bs::system; diff --git a/tests/proxy_transform_test.rs b/tests/proxy_transform_test.rs index d64f897..4656bb4 100644 --- a/tests/proxy_transform_test.rs +++ b/tests/proxy_transform_test.rs @@ -69,3 +69,44 @@ fn test_replace_links() { assert_eq!(resp_body, expected_body); } + +#[test] +fn test_redirect() { + let (target, target_addr) = get_test_server(|app| { + app.handler(|req: &HttpRequest| { + let srv_address = req + .headers() + .get("srv_address") + .expect("missing srv_address header") + .to_str() + .expect("headervalue -> str"); + + HttpResponse::Found() + .header(header::LOCATION, format!("http://{}/login", srv_address)) + .finish() + }); + }); + + let (mut proxy, proxy_address) = get_test_proxy(&target, |app| { + app.handler(proxy_transform); + }); + + let request = proxy + .get() + .header(header::ACCEPT, TEXT_HTML) + .header("srv_address", target_addr) + .uri(proxy.url("/")) + .finish() + .expect("finish request"); + + let (resp, _resp_body) = get_resp(&mut proxy, request); + let expected_redirect = format!("http://{}/login", proxy_address); + let _actual_redirect = resp + .headers() + .get(header::LOCATION) + .expect("has location header") + .to_str() + .expect("header->str"); + assert_eq!(resp.status(), 302); + // assert_eq!(actual_redirect, expected_redirect); +}