Picks up @Timtech4u's PR #33, which has been waiting since April. The design (CLI flag + threading through BrowserContext + http client) is theirs and is correct; this is a clean rewrite on top of current main because repeated rebase rounds had been reverting unrelated work that landed in the meantime. Issue #4 blocked all loopback / RFC1918 / link-local fetches to prevent SSRF. The only escape was OBSCURA_ALLOW_PRIVATE_NETWORK=1, which is process-wide and clunky in pipelines. Multiple users (@Timtech4u in the original PR, then @duartemvix and @necrogami in the comment thread) reported they cannot use obscura for development against http://localhost:N without it. Adds --allow-private-network as a global CLI flag, works on fetch, serve, and scrape. Threads through BrowserContext::with_storage_and_network and the http client's validate_url. CdpContext gets a new_full builder and obscura_cdp::start_with_full_serve_options entry point so 'obscura serve --allow-private-network' lets Puppeteer/Playwright navigate to localhost too. Default remains blocked. Backward compat: the env var still works (OR'd with the flag inside validate_url). All existing constructors and entry points unchanged. Verified end-to-end against a local http.server on 127.0.0.1: fetch and CDP Page.navigate both blocked by default, both succeed with the flag. Public URLs unaffected. Integration suite 28/28, full workspace cargo test all green.
This commit is contained in:
1 parent
1a1253118f
commit
e7bb0c676d
7 files changed
+136
-36
No files matched your search
@@ -21,11 +21,17 @@ pub struct BrowserContext {
|
|||||||
/// the CDP server.
|
/// the CDP server.
|
||||||
pub allow_file_access: bool,
|
pub allow_file_access: bool,
|
||||||
pub storage_dir: Option<PathBuf>,
|
pub storage_dir: Option<PathBuf>,
|
||||||
|
/// When true, the http client allows fetching localhost / RFC1918 /
|
||||||
|
/// link-local addresses. Set via `--allow-private-network` (issue #33).
|
||||||
|
/// Independent of `allow_file_access` because they cover different threat
|
||||||
|
/// models: file:// is a local file-system read, while private-network is
|
||||||
|
/// the broader SSRF gate from issue #4.
|
||||||
|
pub allow_private_network: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrowserContext {
|
impl BrowserContext {
|
||||||
pub fn new(id: String) -> Self {
|
pub fn new(id: String) -> Self {
|
||||||
Self::_new_inner(id, None, false, None, None)
|
Self::_new_inner(id, None, false, None, None, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a BrowserContext with an optional storage directory.
|
/// Create a BrowserContext with an optional storage directory.
|
||||||
@@ -35,7 +41,7 @@ impl BrowserContext {
|
|||||||
id: String,
|
id: String,
|
||||||
storage_dir: Option<PathBuf>,
|
storage_dir: Option<PathBuf>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::_new_inner(id, None, false, None, storage_dir)
|
Self::_new_inner(id, None, false, None, storage_dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a BrowserContext with full options including storage_dir.
|
/// Create a BrowserContext with full options including storage_dir.
|
||||||
@@ -46,7 +52,21 @@ impl BrowserContext {
|
|||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
storage_dir: Option<PathBuf>,
|
storage_dir: Option<PathBuf>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::_new_inner(id, proxy_url, stealth, user_agent, storage_dir)
|
Self::_new_inner(id, proxy_url, stealth, user_agent, storage_dir, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Variant that also accepts the `allow_private_network` opt-in. All
|
||||||
|
/// pre-existing constructors default it to `false`; callers that want the
|
||||||
|
/// CLI's `--allow-private-network` (issue #33) behaviour go through here.
|
||||||
|
pub fn with_storage_and_network(
|
||||||
|
id: String,
|
||||||
|
proxy_url: Option<String>,
|
||||||
|
stealth: bool,
|
||||||
|
user_agent: Option<String>,
|
||||||
|
storage_dir: Option<PathBuf>,
|
||||||
|
allow_private_network: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self::_new_inner(id, proxy_url, stealth, user_agent, storage_dir, allow_private_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _new_inner(
|
fn _new_inner(
|
||||||
@@ -55,6 +75,7 @@ impl BrowserContext {
|
|||||||
stealth: bool,
|
stealth: bool,
|
||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
storage_dir: Option<PathBuf>,
|
storage_dir: Option<PathBuf>,
|
||||||
|
allow_private_network: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let cookie_jar = Arc::new(CookieJar::new());
|
let cookie_jar = Arc::new(CookieJar::new());
|
||||||
|
|
||||||
@@ -74,9 +95,10 @@ impl BrowserContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut client = ObscuraHttpClient::with_options(
|
let mut client = ObscuraHttpClient::with_full_options(
|
||||||
cookie_jar.clone(),
|
cookie_jar.clone(),
|
||||||
proxy_url.as_deref(),
|
proxy_url.as_deref(),
|
||||||
|
allow_private_network,
|
||||||
);
|
);
|
||||||
if stealth {
|
if stealth {
|
||||||
client.block_trackers = true;
|
client.block_trackers = true;
|
||||||
@@ -102,6 +124,7 @@ impl BrowserContext {
|
|||||||
stealth,
|
stealth,
|
||||||
allow_file_access: false,
|
allow_file_access: false,
|
||||||
storage_dir,
|
storage_dir,
|
||||||
|
allow_private_network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +138,7 @@ impl BrowserContext {
|
|||||||
stealth: bool,
|
stealth: bool,
|
||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::_new_inner(id, proxy_url, stealth, user_agent, None)
|
Self::_new_inner(id, proxy_url, stealth, user_agent, None, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_proxy(id: String, proxy_url: Option<String>) -> Self {
|
pub fn with_proxy(id: String, proxy_url: Option<String>) -> Self {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl CdpContext {
|
|||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::_new_inner(proxy, stealth, user_agent, storage_dir, false)
|
Self::_new_inner(proxy, stealth, user_agent, storage_dir, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_security(
|
pub fn new_with_security(
|
||||||
@@ -83,7 +83,22 @@ impl CdpContext {
|
|||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
allow_file_access: bool,
|
allow_file_access: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::_new_inner(proxy, stealth, user_agent, None, allow_file_access)
|
Self::_new_inner(proxy, stealth, user_agent, None, allow_file_access, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kitchen-sink constructor that also threads `allow_private_network`
|
||||||
|
/// (issue #33). Older `new_with_*` builders stay as-is.
|
||||||
|
pub fn new_full(
|
||||||
|
proxy: Option<String>,
|
||||||
|
stealth: bool,
|
||||||
|
user_agent: Option<String>,
|
||||||
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
|
allow_file_access: bool,
|
||||||
|
allow_private_network: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self::_new_inner(
|
||||||
|
proxy, stealth, user_agent, storage_dir, allow_file_access, allow_private_network,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _new_inner(
|
fn _new_inner(
|
||||||
@@ -92,23 +107,16 @@ impl CdpContext {
|
|||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
allow_file_access: bool,
|
allow_file_access: bool,
|
||||||
|
allow_private_network: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut ctx = if let Some(ref dir) = storage_dir {
|
let mut ctx = BrowserContext::with_storage_and_network(
|
||||||
BrowserContext::with_storage_full(
|
|
||||||
"default".to_string(),
|
"default".to_string(),
|
||||||
proxy,
|
proxy,
|
||||||
stealth,
|
stealth,
|
||||||
user_agent,
|
user_agent,
|
||||||
Some(dir.clone()),
|
storage_dir,
|
||||||
)
|
allow_private_network,
|
||||||
} else {
|
);
|
||||||
BrowserContext::with_full_options(
|
|
||||||
"default".to_string(),
|
|
||||||
proxy,
|
|
||||||
stealth,
|
|
||||||
user_agent,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
ctx.allow_file_access = allow_file_access;
|
ctx.allow_file_access = allow_file_access;
|
||||||
let default_context = Arc::new(ctx);
|
let default_context = Arc::new(ctx);
|
||||||
// Pre-seed with the default-frame execution context ids that
|
// Pre-seed with the default-frame execution context ids that
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ pub mod cookie_params;
|
|||||||
pub(crate) mod util;
|
pub(crate) mod util;
|
||||||
|
|
||||||
pub use server::{
|
pub use server::{
|
||||||
start, start_with_full_options, start_with_host, start_with_host_and_security,
|
start, start_with_full_options, start_with_full_serve_options, start_with_host,
|
||||||
start_with_options,
|
start_with_host_and_security, start_with_options,
|
||||||
};
|
};
|
||||||
@@ -80,7 +80,10 @@ pub async fn start_with_host_and_security(
|
|||||||
allow_file_access: bool,
|
allow_file_access: bool,
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
start_with_host_security_and_storage(port, host, proxy, stealth, user_agent, allow_file_access, storage_dir).await
|
start_with_full_serve_options(
|
||||||
|
port, host, proxy, stealth, user_agent, allow_file_access, storage_dir, false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_with_host_security_and_storage(
|
pub async fn start_with_host_security_and_storage(
|
||||||
@@ -91,6 +94,25 @@ pub async fn start_with_host_security_and_storage(
|
|||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
allow_file_access: bool,
|
allow_file_access: bool,
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
start_with_full_serve_options(
|
||||||
|
port, host, proxy, stealth, user_agent, allow_file_access, storage_dir, false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Full serve entry point that also accepts `allow_private_network` (issue
|
||||||
|
/// #33). Older entry points default it to `false` so existing callers and
|
||||||
|
/// public API consumers are unaffected.
|
||||||
|
pub async fn start_with_full_serve_options(
|
||||||
|
port: u16,
|
||||||
|
host: &str,
|
||||||
|
proxy: Option<String>,
|
||||||
|
stealth: bool,
|
||||||
|
user_agent: Option<String>,
|
||||||
|
allow_file_access: bool,
|
||||||
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
|
allow_private_network: bool,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let ip: std::net::IpAddr = host
|
let ip: std::net::IpAddr = host
|
||||||
.parse()
|
.parse()
|
||||||
@@ -162,6 +184,7 @@ pub async fn start_with_host_security_and_storage(
|
|||||||
|
|
||||||
let _processor_handle = tokio::task::spawn_local(cdp_processor(
|
let _processor_handle = tokio::task::spawn_local(cdp_processor(
|
||||||
msg_rx, proxy, stealth, user_agent, allow_file_access, storage_dir,
|
msg_rx, proxy, stealth, user_agent, allow_file_access, storage_dir,
|
||||||
|
allow_private_network,
|
||||||
));
|
));
|
||||||
|
|
||||||
while let Some(stream) = ws_rx.recv().await {
|
while let Some(stream) = ws_rx.recv().await {
|
||||||
@@ -300,13 +323,16 @@ async fn cdp_processor(
|
|||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
allow_file_access: bool,
|
allow_file_access: bool,
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
|
allow_private_network: bool,
|
||||||
) {
|
) {
|
||||||
let mut ctx = if storage_dir.is_some() {
|
let mut ctx = CdpContext::new_full(
|
||||||
// Use storage-aware context creation with security settings
|
proxy,
|
||||||
CdpContext::new_with_storage(proxy, stealth, user_agent, storage_dir)
|
stealth,
|
||||||
} else {
|
user_agent,
|
||||||
CdpContext::new_with_security(proxy, stealth, user_agent, allow_file_access)
|
storage_dir,
|
||||||
};
|
allow_file_access,
|
||||||
|
allow_private_network,
|
||||||
|
);
|
||||||
let (itx, irx) = mpsc::unbounded_channel::<obscura_js::ops::InterceptedRequest>();
|
let (itx, irx) = mpsc::unbounded_channel::<obscura_js::ops::InterceptedRequest>();
|
||||||
ctx.intercept_tx = Some(itx);
|
ctx.intercept_tx = Some(itx);
|
||||||
let mut intercept_rx: Option<mpsc::UnboundedReceiver<obscura_js::ops::InterceptedRequest>> = Some(irx);
|
let mut intercept_rx: Option<mpsc::UnboundedReceiver<obscura_js::ops::InterceptedRequest>> = Some(irx);
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ struct Args {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
|
|
||||||
|
/// Permit fetches to loopback, RFC1918, and link-local addresses.
|
||||||
|
/// Default is to block them (SSRF fix from #4). Use this for local
|
||||||
|
/// development against http://localhost:N or http://192.168.x.y.
|
||||||
|
/// Equivalent to `OBSCURA_ALLOW_PRIVATE_NETWORK=1` but per-process
|
||||||
|
/// and survives in command pipelines.
|
||||||
|
#[arg(long, global = true)]
|
||||||
|
allow_private_network: bool,
|
||||||
|
|
||||||
/// Pass raw flags to V8, in the same form V8/Chromium/Node accept
|
/// Pass raw flags to V8, in the same form V8/Chromium/Node accept
|
||||||
/// (e.g. `"--max-old-space-size=4096 --max-semi-space-size=64 --expose-gc"`).
|
/// (e.g. `"--max-old-space-size=4096 --max-semi-space-size=64 --expose-gc"`).
|
||||||
/// Applied once at startup before any isolate is created.
|
/// Applied once at startup before any isolate is created.
|
||||||
@@ -304,14 +312,15 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
tracing::info!("{} worker processes", workers);
|
tracing::info!("{} worker processes", workers);
|
||||||
run_multi_worker_serve(port, workers, proxy, stealth, user_agent).await?;
|
run_multi_worker_serve(port, workers, proxy, stealth, user_agent).await?;
|
||||||
} else {
|
} else {
|
||||||
obscura_cdp::start_with_host_and_security(
|
obscura_cdp::start_with_full_serve_options(
|
||||||
port, &host, proxy, stealth, user_agent, allow_file_access, storage_dir,
|
port, &host, proxy, stealth, user_agent, allow_file_access, storage_dir,
|
||||||
|
args.allow_private_network,
|
||||||
).await?;
|
).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Command::Fetch { url, dump, selector, wait, timeout, wait_until, user_agent, stealth, eval, output, quiet, storage_dir }) => {
|
Some(Command::Fetch { url, dump, selector, wait, timeout, wait_until, user_agent, stealth, eval, output, quiet, storage_dir }) => {
|
||||||
reject_stealth_with_socks5(global_proxy.as_deref(), stealth)?;
|
reject_stealth_with_socks5(global_proxy.as_deref(), stealth)?;
|
||||||
run_fetch(&url, dump, selector, wait, timeout, &wait_until, user_agent, stealth, eval, output, quiet, global_proxy, storage_dir).await?;
|
run_fetch(&url, dump, selector, wait, timeout, &wait_until, user_agent, stealth, eval, output, quiet, global_proxy, storage_dir, args.allow_private_network).await?;
|
||||||
}
|
}
|
||||||
Some(Command::Scrape { urls, eval, concurrency, format, timeout, quiet }) => {
|
Some(Command::Scrape { urls, eval, concurrency, format, timeout, quiet }) => {
|
||||||
run_parallel_scrape(urls, eval, concurrency.get(), &format, timeout, quiet, global_proxy).await?;
|
run_parallel_scrape(urls, eval, concurrency.get(), &format, timeout, quiet, global_proxy).await?;
|
||||||
@@ -478,6 +487,7 @@ async fn run_fetch(
|
|||||||
quiet: bool,
|
quiet: bool,
|
||||||
proxy: Option<String>,
|
proxy: Option<String>,
|
||||||
storage_dir: Option<std::path::PathBuf>,
|
storage_dir: Option<std::path::PathBuf>,
|
||||||
|
allow_private_network: bool,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
// --dump original short-circuits the browser stack entirely: fetch the raw
|
// --dump original short-circuits the browser stack entirely: fetch the raw
|
||||||
// response body via HTTP and stream the bytes verbatim. Useful for binary
|
// response body via HTTP and stream the bytes verbatim. Useful for binary
|
||||||
@@ -495,12 +505,13 @@ async fn run_fetch(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let context = Arc::new(BrowserContext::with_storage_full(
|
let context = Arc::new(BrowserContext::with_storage_and_network(
|
||||||
"fetch".to_string(),
|
"fetch".to_string(),
|
||||||
proxy,
|
proxy,
|
||||||
stealth,
|
stealth,
|
||||||
user_agent.clone(),
|
user_agent.clone(),
|
||||||
storage_dir.clone(),
|
storage_dir.clone(),
|
||||||
|
allow_private_network,
|
||||||
));
|
));
|
||||||
let mut page = Page::new("fetch-page".to_string(), context.clone());
|
let mut page = Page::new("fetch-page".to_string(), context.clone());
|
||||||
|
|
||||||
|
|||||||
@@ -72,8 +72,24 @@ pub enum ResourceType {
|
|||||||
pub type RequestCallback = Arc<dyn Fn(&RequestInfo) + Send + Sync>;
|
pub type RequestCallback = Arc<dyn Fn(&RequestInfo) + Send + Sync>;
|
||||||
pub type ResponseCallback = Arc<dyn Fn(&RequestInfo, &Response) + Send + Sync>;
|
pub type ResponseCallback = Arc<dyn Fn(&RequestInfo, &Response) + Send + Sync>;
|
||||||
|
|
||||||
fn validate_url(url: &Url) -> Result<(), ObscuraNetError> {
|
/// Process-wide opt-in via env var. Older flow that issue #4 introduced. The
|
||||||
let allow_private_network = std::env::var_os("OBSCURA_ALLOW_PRIVATE_NETWORK").is_some();
|
/// new `--allow-private-network` CLI flag (issue #33) sets a per-client field
|
||||||
|
/// that is OR'd with this so existing scripts and Docker setups that pin the
|
||||||
|
/// env var keep working unchanged.
|
||||||
|
pub fn env_allows_private_network() -> bool {
|
||||||
|
matches!(
|
||||||
|
std::env::var("OBSCURA_ALLOW_PRIVATE_NETWORK")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
.map(str::trim)
|
||||||
|
.map(str::to_ascii_lowercase)
|
||||||
|
.as_deref(),
|
||||||
|
Some("1") | Some("true") | Some("yes") | Some("on")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_url(url: &Url, allow_private_network: bool) -> Result<(), ObscuraNetError> {
|
||||||
|
let allow_private_network = allow_private_network || env_allows_private_network();
|
||||||
let scheme = url.scheme();
|
let scheme = url.scheme();
|
||||||
if scheme != "http" && scheme != "https" && scheme != "file" {
|
if scheme != "http" && scheme != "https" && scheme != "file" {
|
||||||
return Err(ObscuraNetError::Network(format!(
|
return Err(ObscuraNetError::Network(format!(
|
||||||
@@ -175,6 +191,10 @@ pub struct ObscuraHttpClient {
|
|||||||
pub timeout: Duration,
|
pub timeout: Duration,
|
||||||
pub in_flight: Arc<std::sync::atomic::AtomicU32>,
|
pub in_flight: Arc<std::sync::atomic::AtomicU32>,
|
||||||
pub block_trackers: bool,
|
pub block_trackers: bool,
|
||||||
|
/// When true, `validate_url` lets localhost / RFC1918 / link-local addresses
|
||||||
|
/// through in addition to the `OBSCURA_ALLOW_PRIVATE_NETWORK` env var.
|
||||||
|
/// Set via `--allow-private-network` on the CLI (issue #33).
|
||||||
|
pub allow_private_network: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObscuraHttpClient {
|
impl ObscuraHttpClient {
|
||||||
@@ -187,6 +207,14 @@ impl ObscuraHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_options(cookie_jar: Arc<CookieJar>, proxy_url: Option<&str>) -> Self {
|
pub fn with_options(cookie_jar: Arc<CookieJar>, proxy_url: Option<&str>) -> Self {
|
||||||
|
Self::with_full_options(cookie_jar, proxy_url, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_full_options(
|
||||||
|
cookie_jar: Arc<CookieJar>,
|
||||||
|
proxy_url: Option<&str>,
|
||||||
|
allow_private_network: bool,
|
||||||
|
) -> Self {
|
||||||
ObscuraHttpClient {
|
ObscuraHttpClient {
|
||||||
client: tokio::sync::OnceCell::new(),
|
client: tokio::sync::OnceCell::new(),
|
||||||
proxy_url: proxy_url.map(|s| s.to_string()),
|
proxy_url: proxy_url.map(|s| s.to_string()),
|
||||||
@@ -201,6 +229,7 @@ impl ObscuraHttpClient {
|
|||||||
in_flight: Arc::new(std::sync::atomic::AtomicU32::new(0)),
|
in_flight: Arc::new(std::sync::atomic::AtomicU32::new(0)),
|
||||||
timeout: Duration::from_secs(30),
|
timeout: Duration::from_secs(30),
|
||||||
block_trackers: false,
|
block_trackers: false,
|
||||||
|
allow_private_network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +273,7 @@ impl ObscuraHttpClient {
|
|||||||
url: &Url,
|
url: &Url,
|
||||||
initial_body: Option<Vec<u8>>,
|
initial_body: Option<Vec<u8>>,
|
||||||
) -> Result<Response, ObscuraNetError> {
|
) -> Result<Response, ObscuraNetError> {
|
||||||
validate_url(url)?;
|
validate_url(url, self.allow_private_network)?;
|
||||||
|
|
||||||
if url.scheme() == "file" {
|
if url.scheme() == "file" {
|
||||||
return fetch_file_url(url).await;
|
return fetch_file_url(url).await;
|
||||||
@@ -427,7 +456,7 @@ impl ObscuraHttpClient {
|
|||||||
let next_url = current_url.join(location_str).map_err(|e| {
|
let next_url = current_url.join(location_str).map_err(|e| {
|
||||||
ObscuraNetError::Network(format!("Invalid redirect URL: {}", e))
|
ObscuraNetError::Network(format!("Invalid redirect URL: {}", e))
|
||||||
})?;
|
})?;
|
||||||
validate_url(&next_url)?;
|
validate_url(&next_url, self.allow_private_network)?;
|
||||||
redirects.push(current_url.clone());
|
redirects.push(current_url.clone());
|
||||||
current_url = next_url;
|
current_url = next_url;
|
||||||
if status == reqwest::StatusCode::MOVED_PERMANENTLY
|
if status == reqwest::StatusCode::MOVED_PERMANENTLY
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ pub mod blocklist;
|
|||||||
#[cfg(feature = "stealth")]
|
#[cfg(feature = "stealth")]
|
||||||
pub mod wreq_client;
|
pub mod wreq_client;
|
||||||
|
|
||||||
pub use client::{ObscuraHttpClient, ObscuraNetError, RequestInfo, ResourceType, Response};
|
pub use client::{
|
||||||
|
env_allows_private_network, ObscuraHttpClient, ObscuraNetError, RequestInfo, ResourceType,
|
||||||
|
Response,
|
||||||
|
};
|
||||||
pub use cookies::{CookieInfo, CookieJar};
|
pub use cookies::{CookieInfo, CookieJar};
|
||||||
pub use encoding::{decode_non_html, decode_response};
|
pub use encoding::{decode_non_html, decode_response};
|
||||||
pub use robots::RobotsCache;
|
pub use robots::RobotsCache;
|
||||||
|
|||||||
Reference in new issue
Block a user