respect max_redirects configuration

This commit is contained in:
Thiago Arrais 2020-12-04 13:57:39 -03:00
parent d10f776837
commit 05527b3e51
4 changed files with 20 additions and 10 deletions

View File

@ -191,6 +191,7 @@ impl ClientBuilder {
let config = ClientConfig { let config = ClientConfig {
headers: self.headers, headers: self.headers,
timeout: self.timeout, timeout: self.timeout,
max_redirects: self.max_redirects,
connector, connector,
}; };
Client(Rc::new(config)) Client(Rc::new(config))

View File

@ -5,6 +5,7 @@ use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, io, mem, net}; use std::{fmt, io, mem, net};
use url::Url;
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::body::Body; use actix_http::body::Body;
@ -12,7 +13,7 @@ use actix_http::client::{
Connect as ClientConnect, ConnectError, Connection, SendRequestError, Connect as ClientConnect, ConnectError, Connection, SendRequestError,
}; };
use actix_http::h1::ClientCodec; use actix_http::h1::ClientCodec;
use actix_http::http::{HeaderMap, Uri}; use actix_http::http::HeaderMap;
use actix_http::Extensions; use actix_http::Extensions;
use actix_http::{RequestHead, RequestHeadType, ResponseHead}; use actix_http::{RequestHead, RequestHeadType, ResponseHead};
use actix_service::Service; use actix_service::Service;
@ -27,6 +28,7 @@ pub(crate) trait Connect {
head: RequestHead, head: RequestHead,
body: Body, body: Body,
addr: Option<net::SocketAddr>, addr: Option<net::SocketAddr>,
max_redirect: usize,
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>>; ) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>>;
fn send_request_extra( fn send_request_extra(
@ -85,12 +87,15 @@ where
head: RequestHead, head: RequestHead,
body: Body, body: Body,
addr: Option<net::SocketAddr>, addr: Option<net::SocketAddr>,
max_redirects: usize,
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>> { ) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>> {
fn deal_with_redirects<S>( fn deal_with_redirects<S>(
backend: Rc<RefCell<S>>, backend: Rc<RefCell<S>>,
head: RequestHead, head: RequestHead,
body: Body, body: Body,
addr: Option<net::SocketAddr>, addr: Option<net::SocketAddr>,
max_redirects: usize,
redirect_count: usize,
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>> ) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>>
where where
S: Service<Request = ClientConnect, Error = ConnectError> + 'static, S: Service<Request = ClientConnect, Error = ConnectError> + 'static,
@ -139,23 +144,25 @@ where
if resphead.status.is_redirection(); if resphead.status.is_redirection();
if let Some(location_value) = resphead.headers.get(actix_http::http::header::LOCATION); if let Some(location_value) = resphead.headers.get(actix_http::http::header::LOCATION);
if let Ok(location_str) = location_value.to_str(); if let Ok(location_str) = location_value.to_str();
if let Ok(location_uri) = location_str.parse::<Uri>();
then { then {
if redirect_count >= max_redirects {
// TODO: need a better error
return Err(SendRequestError::Timeout);
}
if resphead.status == actix_http::http::StatusCode::SEE_OTHER { if resphead.status == actix_http::http::StatusCode::SEE_OTHER {
reqhead.method = actix_http::http::Method::GET; reqhead.method = actix_http::http::Method::GET;
reqbody = Body::None; reqbody = Body::None;
} }
let mut parts = location_uri.clone().into_parts(); let base_url = Url::parse(&uri.to_string()).unwrap();
if location_uri.authority().is_none() { let url_parser = Url::options().base_url(Some(&base_url));
parts.scheme = Some(uri.scheme().unwrap().clone()); reqhead.uri = url_parser.parse(location_str).unwrap().as_str().parse().unwrap();
parts.authority = Some(uri.authority().unwrap().clone());
}
reqhead.uri = Uri::from_parts(parts).unwrap();
return deal_with_redirects( return deal_with_redirects(
backend.clone(), backend.clone(),
reqhead, reqhead,
reqbody, reqbody,
addr, addr,
max_redirects,
redirect_count + 1,
) )
.await; .await;
} }
@ -167,7 +174,7 @@ where
}) })
} }
deal_with_redirects(self.0.clone(), head, body, addr) deal_with_redirects(self.0.clone(), head, body, addr, max_redirects, 0)
} }
fn send_request_extra( fn send_request_extra(

View File

@ -148,6 +148,7 @@ pub(crate) struct ClientConfig {
pub(crate) connector: RefCell<Box<dyn Connect>>, pub(crate) connector: RefCell<Box<dyn Connect>>,
pub(crate) headers: HeaderMap, pub(crate) headers: HeaderMap,
pub(crate) timeout: Option<Duration>, pub(crate) timeout: Option<Duration>,
pub(crate) max_redirects: usize,
} }
impl Default for Client { impl Default for Client {
@ -158,6 +159,7 @@ impl Default for Client {
))))), ))))),
headers: HeaderMap::new(), headers: HeaderMap::new(),
timeout: Some(Duration::from_secs(5)), timeout: Some(Duration::from_secs(5)),
max_redirects: 10,
})) }))
} }
} }

View File

@ -186,7 +186,7 @@ impl RequestSender {
let fut = match self { let fut = match self {
RequestSender::Owned(head) => { RequestSender::Owned(head) => {
connector.send_request(head, body.into(), addr) connector.send_request(head, body.into(), addr, config.max_redirects)
} }
RequestSender::Rc(head, extra_headers) => { RequestSender::Rc(head, extra_headers) => {
connector.send_request_extra(head, extra_headers, body.into(), addr) connector.send_request_extra(head, extra_headers, body.into(), addr)