Skip to content

[WIP] Implement rama-pac#698

Draft
nagxsan wants to merge 3 commits intoplabayo:mainfrom
nagxsan:sanchet/feat/rama-pac
Draft

[WIP] Implement rama-pac#698
nagxsan wants to merge 3 commits intoplabayo:mainfrom
nagxsan:sanchet/feat/rama-pac

Conversation

@nagxsan
Copy link

@nagxsan nagxsan commented Sep 6, 2025

Details

Issue number

#566

Summary

  • Adds a rama-pac crate that will handle logic of creating a PAC Connector, getting and reading the PAC file for the outgoing request URL proxy options and route the request through first successful proxy.

Copy link
Member

@GlenDC GlenDC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some feedback, let me know if something is not clear, as you're dealing with non-trivial concepts here.

) -> Result<Self::Response, Self::Error> {
let pac_file = fetch_pac(&self.web_client, &self.pac_uri)
.await
.unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given it's wip this is ok, but obviously in the final code errors like this should be handled or bubbled up. no unwraps please unless you have a very good reason

*attempt.uri_mut() = proxy_uri;
}
ProxyDirective::Socks5(hp) => {
let proxy_uri: Uri = format!("socks5://{hp}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proxy info will have to be added in the extensions (context for now, but in some days part of request), so that the inner connectors will use it as proxy

*attempt.uri_mut() = proxy_uri;
}
}
match self.service.serve(ctx.clone(), attempt).await {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inner service is a connector service so might as well call it connector. E.g. is going to be something like an AutoConnector wrapped around tcp etc.

@@ -0,0 +1,3 @@
pub mod connector;
pub mod pac_fetcher;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to make all modules public if you only use 1 or 2 types it's better to keep the public module structure flat.

.body(Body::empty())
.map_err(Into::<BoxError>::into)?;

let response = web_client.execute(Context::default(), request).await?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given you already use HttpClientExt you can make this simpler by doing web_client.get(pac_ure).send(Context::default()) or something like it


const PAC_STDLIB: &str = r#"
// spec-ish helpers
function dnsDomainIs(host, domain) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using these js interpreted functions, use rust host functions please, so we can use std rama code and it will be easier to keep it straight

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProxyDirective {
Direct,
Proxy(String),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably do better, type-wise, no? As in a SocketAddress, or something like that?

use rama_core::{Context, error::BoxError};
use rama_http::{Body, Request, Uri, service::client::HttpClientExt};

pub async fn fetch_pac<W>(web_client: &W, pac_uri: &Uri) -> Result<String, BoxError>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this public? This is just a detail of your connector I would think

ctx: Context,
req: Request<Body>,
) -> Result<Self::Response, Self::Error> {
let pac_file = fetch_pac(&self.web_client, &self.pac_uri)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be cached in your connector I think with an ArcSwap together with a duration and only refetch it after some cache duration, default pretty high but can be overwritten.

where
S: Service<Request<Body>, Error: Into<BoxError> + Send + Sync + 'static>,
W: HttpClientExt + Send + Sync + 'static,
Body: Clone + Send + Sync + 'static,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Body is never clone. Neither should it. Your connector is not dealing with the actual Http request anyway, what you need to implement is a service as a connection service. For any request which has a target uri, but that doesn't have to be a http request perse. This way can also work for any other protocol that has uris and again it's; not to already serve the input but to just establish connection. The inner service is the actual connector stack establishing the connection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants