Skip to content

Commit

Permalink
classify known http proxy connect errors
Browse files Browse the repository at this point in the history
makes it easier for downstream users
to classify these into something specific
  • Loading branch information
GlenDC committed Oct 23, 2024
1 parent 1fbb3fa commit ead32fa
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 19 deletions.
2 changes: 1 addition & 1 deletion rama-http-backend/src/client/proxy/layer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ pub use proxy_auth_header::{SetProxyAuthHttpHeaderLayer, SetProxyAuthHttpHeaderS

mod proxy_connector;
#[doc(inline)]
pub use proxy_connector::{HttpProxyConnector, HttpProxyConnectorLayer};
pub use proxy_connector::{HttpProxyConnector, HttpProxyConnectorLayer, HttpProxyError};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use rama_http_types::{
use rama_net::{address::Authority, stream::Stream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

use super::HttpProxyError;

#[derive(Debug, Clone)]
/// Connector for HTTP proxies.
///
Expand Down Expand Up @@ -64,7 +66,7 @@ impl InnerHttpProxyConnector {
pub(super) async fn handshake<S: Stream + Unpin>(
&self,
mut stream: S,
) -> Result<S, std::io::Error> {
) -> Result<S, HttpProxyError> {
// TODO: handle user-agent and host better
// TODO: use h1 protocol from embedded hyper directly here!
let mut request = format!(
Expand Down Expand Up @@ -100,7 +102,8 @@ impl InnerHttpProxyConnector {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"http conn handshake read incomplete",
));
)
.into());
}
pos += n;

Expand All @@ -113,27 +116,24 @@ impl InnerHttpProxyConnector {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"http conn handshake response too large",
));
)
.into());
}
// else read more
} else if recvd.starts_with(b"HTTP/1.1 407") {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"http conn handshake proxy auth required",
));
return Err(HttpProxyError::AuthRequired);
} else if recvd.starts_with(b"HTTP/1.1 503") {
return Err(HttpProxyError::Unavailable);
} else {
let input = String::from_utf8_lossy(recvd);
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"invalid http conn handshake start: [{}]",
if let Some((line, _)) = input.split_once("\r\n") {
Cow::Borrowed(line)
} else {
input
}
),
));
return Err(HttpProxyError::Other(format!(
"invalid http conn handshake start: [{}]",
if let Some((line, _)) = input.split_once("\r\n") {
Cow::Borrowed(line)
} else {
input
}
)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ mod connector;
// internal usage only
use connector::InnerHttpProxyConnector;

mod proxy_error;
#[doc(inline)]
pub use proxy_error::HttpProxyError;

mod layer;
#[doc(inline)]
pub use layer::HttpProxyConnectorLayer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::fmt;

#[derive(Debug)]
/// error that can be returned in case a http proxy
/// did not manage to establish a connection
pub enum HttpProxyError {
/// Proxy Authentication Required
///
/// (Proxy returned HTTP 407)
AuthRequired,
/// Proxy is Unavailable
///
/// (Proxy returned HTTP 503)
Unavailable,
/// I/O error happened as part of HTTP Proxy Connection Establishment
///
/// (e.g. some kind of TCP error)
Transport(std::io::Error),
/// Something went wrong, but classification did not happen.
///
/// (First header line of http response is included in error)
Other(String),
}

impl fmt::Display for HttpProxyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HttpProxyError::AuthRequired => {
write!(f, "http proxy error: proxy auth required (http 407)")
}
HttpProxyError::Unavailable => {
write!(f, "http proxy error: proxy unavailable (http 503)")
}
HttpProxyError::Transport(error) => {
write!(f, "http proxy error: transport error: I/O [{}]", error)
}
HttpProxyError::Other(header) => {
write!(f, "http proxy error: first line of header = [{}]", header)
}
}
}
}

impl From<std::io::Error> for HttpProxyError {
fn from(value: std::io::Error) -> Self {
Self::Transport(value)
}
}

impl std::error::Error for HttpProxyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
HttpProxyError::AuthRequired => None,
HttpProxyError::Unavailable => None,
HttpProxyError::Transport(err) => Some(err),
HttpProxyError::Other(_) => None,
}
}
}

0 comments on commit ead32fa

Please sign in to comment.