Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Broker

The Broker is the entry point to the library: it turns a set of providers into a stream of proxies. Two operations sit on top of it:

MethodWhat it doesReturns
grab(GrabQuery)Scrape providers, without checking. Dedup on (host, port), optional country filter, cap at limit.ProxyStream
find(FindQuery)Scrape and check — probe judges, classify anonymity, keep only working proxies.Result<ProxyStream, Error>
check(stream, FindQuery)Check proxies you already have (e.g. from a file) instead of scraping.Result<ProxyStream, Error>

grab returns immediately (the work runs in a spawned task). find and check do their fail-fast setup up front — discovering the host’s external IPs and verifying at least one judge — so [Error::NoTypes], [Error::ExtIpUnknown], and [Error::NoJudges] surface from the await, not as a silently-empty stream.

The crypto provider note

This crate builds reqwest with rustls-no-provider (to keep aws-lc-rs out of the dependency graph, which is the musl cross-compile blocker). The trade-off: reqwest bakes in no crypto provider, so a bare reqwest::Client::new() panics until one is installed.

BrokerBuilder::build and Resolver::new call install_default_crypto_provider() for you, so the normal paths need nothing. You only call it yourself when you build a custom reqwest::Client to pass to BrokerBuilder::client:

#![allow(unused)]
fn main() {
use proxybroker::install_default_crypto_provider;

install_default_crypto_provider(); // idempotent; call before building your own reqwest::Client
let client = reqwest::Client::builder()
    .timeout(std::time::Duration::from_secs(10))
    .build()?;
let broker = proxybroker::Broker::builder().client(client).build();
Ok::<(), Box<dyn std::error::Error>>(())
}

install_default_crypto_provider() is idempotent (a std::sync::Once), so calling it more than once is harmless.

BrokerBuilder

Broker::builder() returns a BrokerBuilder. Every setter is consuming (returns Self); unset fields fall back to defaults. build() is infallible.

SetterPurpose
providers(Vec<ProviderSpec>)Use a specific provider list instead of the bundled registry.
client(reqwest::Client)Supply your own HTTP client (timeouts, proxy, TLS).
resolver(Resolver)Supply a resolver — mainly for tests that stub external-IP discovery and DNS to run offline.
geo(GeoDb)Attach a geo database for country lookup/filtering, overriding the bundled default. Requires the geo feature.
without_geo()Attach no geo database. Country filtering then rejects every proxy, and proxies carry no geo. Skips loading the bundled DB. Requires geo.
asn_db(GeoDb)Attach a separate ASN database (--asn-db) so checked proxies carry proxy.asn. No bundled default. Requires geo.

With default features (geo-bundled), build() auto-attaches the bundled DB-IP Country-Lite database unless you supplied one or called without_geo(). See feature flags for what each feature pulls in.

#![allow(unused)]
fn main() {
use proxybroker::Broker;

let broker = Broker::builder().build(); // bundled providers + bundled geo
}

FindQuery

FindQuery describes what to find and check. types is required — an empty types makes find return [Error::NoTypes]. Every other field has a default. You can construct it as a struct literal (with ..Default::default()) or through FindQueryBuilder.

FieldTypeDefaultMeaning
typesVec<TypeSpec>[] (required)Protocols (and optional anonymity levels) a proxy must support.
countriesOption<Vec<String>>NoneKeep only proxies in these ISO country codes.
limitOption<usize>None (unlimited)Stop after this many working proxies.
judgesVec<String>[] (bundled defaults)Judge URLs to probe.
dnsblVec<String>[]DNS blocklist zones; a listed IP is rejected.
timeoutDuration8sPer-request timeout.
max_connusize200Max concurrent checks in flight.
retryRetryPolicydefaultAttempts per protocol + backoff schedule.
postboolfalseUse POST for the test request.
strictboolfalseRequire the anonymity level to match exactly.
liveness_urlOption<String>NoneFallback liveness URL when no judge verifies.
relaxed_validityboolfalseRelax validity to marker+IP, recording Referer/Cookie as capabilities.
require_cookieboolfalseKeep only proxies that forwarded our Cookie header.
require_refererboolfalseKeep only proxies that forwarded our Referer header.
require_connect25boolfalseKeep only proxies with a confirmed CONNECT:25 (SMTP) tunnel.
trust_checkboolfalseRun honeypot detection and record the verdict.
require_trustedboolfalseKeep only proxies with a clean trust verdict (implies trust_check).

FindQueryBuilder

FindQuery::builder() returns a FindQueryBuilder. build() is infallible — the NoTypes guard lives in find/check, not the builder, so the builder stays composable. The builder covers the common fields; the A4/A6 capability flags (require_cookie, require_connect25, trust_check, …) have no builder setter — set those public fields directly on the struct.

SetterNotes
types(Vec<TypeSpec>)Required by find/check.
countries(Vec<String>)
limit(usize)0 maps to unlimited (the CLI’s --limit 0 convention lives here).
judges(Vec<String>)Empty defers to bundled defaults.
dnsbl(Vec<String>)
timeout(Duration)
max_conn(usize)
max_tries(usize)Overrides just the attempt count on the retry policy.
retry(RetryPolicy)The full policy; max_tries, if also set, overrides its count.
post(bool)
strict(bool)
liveness_url(Option<String>)

GrabQuery

GrabQuery is much smaller — grabbing does not check, so there are no judge/timeout/retry knobs.

FieldTypeDefaultMeaning
countriesOption<Vec<String>>NoneKeep only proxies in these ISO country codes.
limitOption<usize>None (unlimited)Stop after this many proxies.

GrabQuery derives Default, so GrabQuery::default() grabs everything the providers return.

ProxyStream

Both grab and find return a ProxyStream, which implements futures_util::Stream<Item = Proxy>. Consume it like any stream:

#![allow(unused)]
fn main() {
use futures_util::StreamExt;
async fn f(stream: &mut proxybroker::ProxyStream) {
while let Some(proxy) = stream.next().await {
    println!("{}", proxy.addr());
}
}
}

The stream ends when the source is exhausted, the limit is reached, or the stream is dropped — dropping fires a cancellation token that aborts in-flight checks, so there is no detached-task leak.

For find (not grab), the stream also carries running statistics over every checked proxy, working or not. Read them after the stream is fully drained for a complete picture:

#![allow(unused)]
fn main() {
fn f(stream: &proxybroker::ProxyStream) {
if let Some(stats) = stream.stats() {
    // aggregate over all checked proxies — see the Stats type
    let _ = stats;
}
}
}

stats() returns Some only for find; it is None for grab (nothing is checked).

A real find example

This mirrors examples/find.rs. It finds up to ten working HTTP or HTTPS proxies and prints each as it is confirmed.

use futures_util::StreamExt;
use proxybroker::{Broker, FindQuery, Proto, TypeSpec};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let broker = Broker::builder().build();

    let mut proxies = broker
        .find(FindQuery {
            types: vec![TypeSpec::any(Proto::Http), TypeSpec::any(Proto::Https)],
            limit: Some(10),
            ..Default::default()
        })
        .await?;

    while let Some(proxy) = proxies.next().await {
        // schemes() is HTTP/HTTPS support; types() has the per-protocol anonymity level.
        println!(
            "Found proxy: {:<21} {:?}  {:.2}s",
            proxy.addr(),
            proxy.schemes(),
            proxy.avg_resp_time(),
        );
    }
    Ok(())
}

The same query via the builder, which normalizes limit(0) to unlimited:

#![allow(unused)]
fn main() {
use proxybroker::{FindQuery, Proto, TypeSpec};

let query = FindQuery::builder()
    .types(vec![TypeSpec::any(Proto::Http), TypeSpec::any(Proto::Https)])
    .limit(10)
    .build();
let _ = query;
}

rotating (feature connector)

Broker::rotating is the one-call convenience for the whole discover → pool → rotate pipeline. It runs find, feeds the resulting stream into a background-warming Pool, and wraps that pool in a ready RotatingProxyConnector:

#![allow(unused)]
fn main() {
pub async fn rotating(
    &self,
    query: FindQuery,
    cfg: RotateConfig,
) -> Result<RotatingProxyConnector, Error>
}

The pool fills as find streams; the connector routes each connection through a healthy proxy (dead ones self-eject via the pool’s health thresholds). It hands hyper a raw tunnel to the target and does not terminate TLS — for an https:// target, layer TLS over the connector (e.g. a hyper-rustls HttpsConnector).

#![allow(unused)]
fn main() {
use proxybroker::{Broker, FindQuery, Proto, TypeSpec};
use proxybroker::connector::RotateConfig;

async fn f() -> Result<(), Box<dyn std::error::Error>> {
let broker = Broker::builder().build();
let connector = broker
    .rotating(
        FindQuery {
            types: vec![TypeSpec::any(Proto::Http)],
            ..Default::default()
        },
        RotateConfig::default(),
    )
    .await?;
// Hand `connector` to a hyper_util Client — see the connector page.
let _ = connector;
Ok(())
}
}

For a pre-seeded pool or a custom PoolConfig, compose the pieces yourself: find → Pool::spawn → RotatingProxyConnector::from_pool.

See also

  • Proxy — the value type each ProxyStream yields.
  • Pool — feed a ProxyStream into a rotating proxy pool.
  • Rotating connector — what rotating returns; a drop-in hyper Service.
  • feature flags — geo, server, and friends.