Skip to content

Commit

Permalink
respect http CONNECT / auth RFC contract
Browse files Browse the repository at this point in the history
closes #198
  • Loading branch information
GlenDC committed May 19, 2024
1 parent 4444653 commit bc761a6
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 16 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ This framework comes with 🔋 batteries included, giving you the full freedome
| 🏗️ [transports](https://ramaproxy.org/docs/rama/stream/index.html) |[tcp](https://ramaproxy.org/docs/rama/tcp/index.html) ⸱ 🏗️ udp <sup>(2)</sup> ⸱ ✅ [middleware](https://ramaproxy.org/docs/rama/stream/layer/index.html) |
| 🏗️ [http](https://ramaproxy.org/docs/rama/http/index.html) |[auto](https://ramaproxy.org/docs/rama/http/server/service/struct.HttpServer.html#method.auto) ⸱ ✅ [http/1.1](https://ramaproxy.org/docs/rama/http/server/service/struct.HttpServer.html#method.http1) ⸱ ✅ [h2](https://ramaproxy.org/docs/rama/http/server/service/struct.HttpServer.html#method.h2) ⸱ 🏗️ h3 <sup>(2)</sup> ⸱ ✅ [middleware](https://ramaproxy.org/docs/rama/http/layer/index.html) |
| ✅ web server |[fs](https://ramaproxy.org/docs/rama/http/service/fs/index.html) ⸱ ✅ [redirect](https://ramaproxy.org/docs/rama/http/service/redirect/struct.Redirect.html) ⸱ ✅ [dyn router](https://ramaproxy.org/docs/rama/http/service/web/struct.WebService.html) ⸱ ✅ [static router](https://ramaproxy.org/docs/rama/http/service/web/macro.match_service.html) ⸱ ✅ [handler extractors](https://ramaproxy.org/docs/rama/http/service/web/extract/index.html) ⸱ ✅ [k8s healthcheck](https://ramaproxy.org/docs/rama/http/service/web/k8s/index.html) |
| ✅ http [client](https://ramaproxy.org/docs/rama/http/client/index.html) |[client](https://ramaproxy.org/docs/rama/http/client/struct.HttpClient.html) ⸱ ✅ [high level API](https://ramaproxy.org/docs/rama/http/client/trait.HttpClientExt.html)🏗️ Proxy Connect <sup>(1)</sup> ⸱ ❌ [Chromium Http](https://github.com/plabayo/rama/issues/189) <sup>(3)</sup> |
| ✅ http [client](https://ramaproxy.org/docs/rama/http/client/index.html) |[client](https://ramaproxy.org/docs/rama/http/client/struct.HttpClient.html) ⸱ ✅ [high level API](https://ramaproxy.org/docs/rama/http/client/trait.HttpClientExt.html)[Proxy Connect](https://ramaproxy.org/docs/rama/proxy/http/client/struct.HttpProxyConnectorService.html) ⸱ ❌ [Chromium Http](https://github.com/plabayo/rama/issues/189) <sup>(3)</sup> |
| 🏗️ [tls](https://ramaproxy.org/docs/rama/tls/index.html) |[Rustls](https://ramaproxy.org/docs/rama/tls/rustls/index.html) ⸱ 🏗️ BoringSSL <sup>(1)</sup> ⸱ ❌ NSS <sup>(3)</sup> |
|[dns](https://ramaproxy.org/docs/rama/dns/index.html) |[DNS Resolver](https://ramaproxy.org/docs/rama/dns/layer/index.html) |
| 🏗️ [proxy protocols](https://ramaproxy.org/docs/rama/proxy/index.html) |[PROXY protocol](https://ramaproxy.org/docs/rama/proxy/pp/index.html)🏗️ http proxy <sup>(1)</sup> ⸱ 🏗️ https proxy <sup>(1)</sup> ⸱ 🏗️ SOCKS5 <sup>(2)</sup> ⸱ 🏗️ SOCKS5H <sup>(2)</sup> |
| 🏗️ [proxy protocols](https://ramaproxy.org/docs/rama/proxy/index.html) |[PROXY protocol](https://ramaproxy.org/docs/rama/proxy/pp/index.html)[http proxy](https://github.com/plabayo/rama/blob/main/examples/http_connect_proxy.rs) ⸱ 🏗️ https proxy <sup>(1)</sup> ⸱ 🏗️ SOCKS5 <sup>(2)</sup> ⸱ 🏗️ SOCKS5H <sup>(2)</sup> |
| 🏗️ web protocols | 🏗️ Web Sockets (WS) <sup>(2)</sup> ⸱ 🏗️ WSS <sup>(2)</sup> ⸱ ❌ Web Transport <sup>(3)</sup> ⸱ ❌ gRPC <sup>(3)</sup> |
|[async-method trait](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) services |[Service](https://ramaproxy.org/docs/rama/service/trait.Service.html) ⸱ ✅ [Layer](https://ramaproxy.org/docs/rama/service/layer/trait.Layer.html) ⸱ ✅ [context](https://ramaproxy.org/docs/rama/service/context/index.html) ⸱ ✅ [dyn dispatch](https://ramaproxy.org/docs/rama/service/struct.BoxService.html) ⸱ ✅ [middleware](https://ramaproxy.org/docs/rama/service/layer/index.html) |
|[telemetry](https://ramaproxy.org/docs/rama/telemetry/index.html) |[tracing](https://tracing.rs/tracing/) ⸱ ✅ [opentelemetry](https://ramaproxy.org/docs/rama/telemetry/opentelemetry/index.html) ⸱ ✅ [http metrics](https://ramaproxy.org/docs/rama/http/layer/opentelemetry/index.html) ⸱ ✅ [transport metrics](https://ramaproxy.org/docs/rama/stream/layer/opentelemetry/index.html) ⸱ ✅ [prometheus exportor](https://ramaproxy.org/docs/rama/http/service/web/struct.PrometheusMetricsHandler.html) |
Expand Down
4 changes: 2 additions & 2 deletions docs/book/src/preface.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ This framework comes with 🔋 batteries included, giving you the full freedome
| 🏗️ [transports](https://ramaproxy.org/docs/rama/stream/index.html) |[tcp](https://ramaproxy.org/docs/rama/tcp/index.html) ⸱ 🏗️ udp <sup>(2)</sup> ⸱ ✅ [middleware](https://ramaproxy.org/docs/rama/stream/layer/index.html) |
| 🏗️ [http](https://ramaproxy.org/docs/rama/http/index.html) |[auto](https://ramaproxy.org/docs/rama/http/server/service/struct.HttpServer.html#method.auto) ⸱ ✅ [http/1.1](https://ramaproxy.org/docs/rama/http/server/service/struct.HttpServer.html#method.http1) ⸱ ✅ [h2](https://ramaproxy.org/docs/rama/http/server/service/struct.HttpServer.html#method.h2) ⸱ 🏗️ h3 <sup>(2)</sup> ⸱ ✅ [middleware](https://ramaproxy.org/docs/rama/http/layer/index.html) |
| ✅ web server |[fs](https://ramaproxy.org/docs/rama/http/service/fs/index.html) ⸱ ✅ [redirect](https://ramaproxy.org/docs/rama/http/service/redirect/struct.Redirect.html) ⸱ ✅ [dyn router](https://ramaproxy.org/docs/rama/http/service/web/struct.WebService.html) ⸱ ✅ [static router](https://ramaproxy.org/docs/rama/http/service/web/macro.match_service.html) ⸱ ✅ [handler extractors](https://ramaproxy.org/docs/rama/http/service/web/extract/index.html) ⸱ ✅ [k8s healthcheck](https://ramaproxy.org/docs/rama/http/service/web/k8s/index.html) |
| ✅ http [client](https://ramaproxy.org/docs/rama/http/client/index.html) |[client](https://ramaproxy.org/docs/rama/http/client/struct.HttpClient.html) ⸱ ✅ [high level API](https://ramaproxy.org/docs/rama/http/client/trait.HttpClientExt.html)🏗️ Proxy Connect <sup>(1)</sup> ⸱ ❌ [Chromium Http](https://github.com/plabayo/rama/issues/189) <sup>(3)</sup> |
| ✅ http [client](https://ramaproxy.org/docs/rama/http/client/index.html) |[client](https://ramaproxy.org/docs/rama/http/client/struct.HttpClient.html) ⸱ ✅ [high level API](https://ramaproxy.org/docs/rama/http/client/trait.HttpClientExt.html)[Proxy Connect](https://ramaproxy.org/docs/rama/proxy/http/client/struct.HttpProxyConnectorService.html) ⸱ ❌ [Chromium Http](https://github.com/plabayo/rama/issues/189) <sup>(3)</sup> |
| 🏗️ [tls](https://ramaproxy.org/docs/rama/tls/index.html) |[Rustls](https://ramaproxy.org/docs/rama/tls/rustls/index.html) ⸱ 🏗️ BoringSSL <sup>(1)</sup> ⸱ ❌ NSS <sup>(3)</sup> |
|[dns](https://ramaproxy.org/docs/rama/dns/index.html) |[DNS Resolver](https://ramaproxy.org/docs/rama/dns/layer/index.html) |
| 🏗️ [proxy protocols](https://ramaproxy.org/docs/rama/proxy/index.html) |[PROXY protocol](https://ramaproxy.org/docs/rama/proxy/pp/index.html)🏗️ http proxy <sup>(1)</sup> ⸱ 🏗️ https proxy <sup>(1)</sup> ⸱ 🏗️ SOCKS5 <sup>(2)</sup> ⸱ 🏗️ SOCKS5H <sup>(2)</sup> |
| 🏗️ [proxy protocols](https://ramaproxy.org/docs/rama/proxy/index.html) |[PROXY protocol](https://ramaproxy.org/docs/rama/proxy/pp/index.html)[http proxy](https://github.com/plabayo/rama/blob/main/examples/http_connect_proxy.rs) ⸱ 🏗️ https proxy <sup>(1)</sup> ⸱ 🏗️ SOCKS5 <sup>(2)</sup> ⸱ 🏗️ SOCKS5H <sup>(2)</sup> |
| 🏗️ web protocols | 🏗️ Web Sockets (WS) <sup>(2)</sup> ⸱ 🏗️ WSS <sup>(2)</sup> ⸱ ❌ Web Transport <sup>(3)</sup> ⸱ ❌ gRPC <sup>(3)</sup> |
|[async-method trait](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) services |[Service](https://ramaproxy.org/docs/rama/service/trait.Service.html) ⸱ ✅ [Layer](https://ramaproxy.org/docs/rama/service/layer/trait.Layer.html) ⸱ ✅ [context](https://ramaproxy.org/docs/rama/service/context/index.html) ⸱ ✅ [dyn dispatch](https://ramaproxy.org/docs/rama/service/struct.BoxService.html) ⸱ ✅ [middleware](https://ramaproxy.org/docs/rama/service/layer/index.html) |
|[telemetry](https://ramaproxy.org/docs/rama/telemetry/index.html) |[tracing](https://tracing.rs/tracing/) ⸱ ✅ [opentelemetry](https://ramaproxy.org/docs/rama/telemetry/opentelemetry/index.html) ⸱ ✅ [http metrics](https://ramaproxy.org/docs/rama/http/layer/opentelemetry/index.html) ⸱ ✅ [transport metrics](https://ramaproxy.org/docs/rama/stream/layer/opentelemetry/index.html) ⸱ ✅ [prometheus exportor](https://ramaproxy.org/docs/rama/http/service/web/struct.PrometheusMetricsHandler.html) |
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
//! | 🏗️ [transports](crate::stream) | ✅ [tcp] ⸱ 🏗️ udp <sup>(1)</sup> ⸱ ✅ [middleware](crate::stream::layer) |
//! | 🏗️ [http] | ✅ [auto](crate::http::server::service::HttpServer::auto) ⸱ ✅ [http/1.1](crate::http::server::service::HttpServer::http1) ⸱ ✅ [h2](crate::http::server::service::HttpServer::h2) ⸱ 🏗️ h3 <sup>(1)</sup> ⸱ ✅ [middleware](crate::http::layer) |
//! | ✅ web server | ✅ [fs](crate::http::service::fs) ⸱ ✅ [redirect](crate::http::service::redirect::Redirect) ⸱ ✅ [dyn router](crate::http::service::web::WebService) ⸱ ✅ [static router](crate::http::service::web::match_service) ⸱ ✅ [handler extractors](crate::http::service::web::extract) ⸱ ✅ [k8s healthcheck](crate::http::service::web::k8s) |
//! | ✅ [http client](crate::http::client) | ✅ [client](crate::http::client::HttpClient) ⸱ ✅ [high level API](crate::http::client::HttpClientExt) ⸱ 🏗️ Proxy Connect <sup>(1)</sup> ⸱ ❌ [Chromium Http](https://github.com/plabayo/rama/issues/189) <sup>(3)</sup> |
//! | ✅ [http client](crate::http::client) | ✅ [client](crate::http::client::HttpClient) ⸱ ✅ [high level API](crate::http::client::HttpClientExt) ⸱ ✅ [Proxy Connect](crate::proxy::http::client::HttpProxyConnectorService) ⸱ ❌ [Chromium Http](https://github.com/plabayo/rama/issues/189) <sup>(3)</sup> |
//! | 🏗️ [tls] | ✅ [Rustls](crate::tls::rustls) ⸱ 🏗️ BoringSSL <sup>(1)</sup> ⸱ ❌ NSS <sup>(3)</sup> |
//! | ✅ [dns] | ✅ [DNS Resolver](crate::dns::layer) |
//! | 🏗️ [proxy] protocols | ✅ [PROXY protocol](crate::proxy::pp) ⸱ 🏗️ http proxy <sup>(1)</sup> ⸱ 🏗️ https proxy <sup>(1)</sup> ⸱ 🏗️ SOCKS5 <sup>(2)</sup> ⸱ 🏗️ SOCKS5H <sup>(2)</sup> |
//! | 🏗️ [proxy] protocols | ✅ [PROXY protocol](crate::proxy::pp) ⸱ ✅ [http proxy](https://github.com/plabayo/rama/blob/main/examples/http_connect_proxy.rs) ⸱ 🏗️ https proxy <sup>(1)</sup> ⸱ 🏗️ SOCKS5 <sup>(2)</sup> ⸱ 🏗️ SOCKS5H <sup>(2)</sup> |
//! | 🏗️ web protocols | 🏗️ Web Sockets (WS) <sup>(2)</sup> ⸱ 🏗️ WSS <sup>(2)</sup> ⸱ ❌ Web Transport <sup>(3)</sup> ⸱ ❌ gRPC <sup>(3)</sup> |
//! | ✅ [async-method trait](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) services | ✅ [Service](crate::service::Service) ⸱ ✅ [Layer](crate::service::layer::Layer) ⸱ ✅ [context](crate::service::context) ⸱ ✅ [dyn dispatch](crate::service::BoxService) ⸱ ✅ [middleware](crate::service::layer) |
//! | ✅ [telemetry][opentelemetry] | ✅ [tracing](https://tracing.rs/tracing/) ⸱ ✅ [opentelemetry] ⸱ ✅ [http metrics](crate::http::layer::opentelemetry) ⸱ ✅ [transport metrics](crate::stream::layer::opentelemetry) ⸱ ✅ [prometheus exportor](crate::http::service::web::PrometheusMetricsHandler) |
Expand Down
38 changes: 37 additions & 1 deletion src/proxy/http/client/layer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::{BoxError, ErrorContext, ErrorExt, OpaqueError};
use crate::http::client::{ClientConnection, EstablishedClientConnection};
use crate::http::headers::HeaderMapExt;
use crate::http::headers::{Authorization, ProxyAuthorization};
use crate::http::Uri;
use crate::http::{Request, RequestContext};
Expand Down Expand Up @@ -255,11 +256,46 @@ where
};
// and do the handshake otherwise...

let EstablishedClientConnection { mut ctx, req, conn } = established_conn;
let EstablishedClientConnection {
mut ctx,
mut req,
conn,
} = established_conn;

let (addr, stream) = conn.into_parts();

let request_context = ctx.get_or_insert_with(|| RequestContext::new(&req));

if !request_context.scheme.secure() {
// unless the scheme is not secure, in such a case no handshake is required...
// we do however need to add authorization headers if credentials are present
if let Some(credentials) = info.credentials.as_ref() {
match credentials {
ProxyCredentials::Basic { username, password } => {
let c = Authorization::basic(
username.as_str(),
password.as_deref().unwrap_or_default(),
)
.0;
req.headers_mut().typed_insert(ProxyAuthorization(c));
}
ProxyCredentials::Bearer(token) => {
let c = Authorization::bearer(token.as_str())
.map_err(|err| {
OpaqueError::from_std(err).context("define http proxy bearer token")
})?
.0;
req.headers_mut().typed_insert(ProxyAuthorization(c));
}
}
}
return Ok(EstablishedClientConnection {
ctx,
req,
conn: ClientConnection::new(addr, stream),
});
}

let authority = match request_context.authority() {
Some(authority) => authority,
None => {
Expand Down
10 changes: 1 addition & 9 deletions tests/example_tests/http_connect_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use rama::{
proxy::http::client::HttpProxyInfo,
rt::Executor,
service::{service_fn, Context},
stream::ServerSocketAddr,
};
use serde_json::{json, Value};

Expand Down Expand Up @@ -42,7 +41,7 @@ async fn test_http_connect_proxy() {
// test regular proxy flow
let result = runner
.get("http://127.0.0.1:63001/foo/bar")
.send(ctx)
.send(ctx.clone())
.await
.unwrap()
.try_into_json::<Value>()
Expand All @@ -51,14 +50,9 @@ async fn test_http_connect_proxy() {
let expected_value = json!({"method":"GET","path":"/foo/bar"});
assert_eq!(expected_value, result);

let mut ctx = Context::default();
// TODO: this should just work correctly over proxy... instead of this hack
ctx.insert(ServerSocketAddr::new("127.0.0.1:62001".parse().unwrap()));
// test proxy pseudo API
let result = runner
.post("http://echo.example.internal/lucky/42")
// TODO: once we go over proxy properly, we should not need this...
.header("Proxy-Authorization", "Basic am9objpzZWNyZXQ=")
.send(ctx)
.await
.unwrap()
Expand All @@ -67,6 +61,4 @@ async fn test_http_connect_proxy() {
.unwrap();
let expected_value = json!({"lucky_number": 42});
assert_eq!(expected_value, result);

// TODO: test https proxy flow
}

0 comments on commit bc761a6

Please sign in to comment.