Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions bitreq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,29 @@ This crate is a fork for the very nice
rename it because I wanted to totally gut it and provide a crate with
different goals. Many thanks to the original author.

Simple, minimal-dependency HTTP client. Optional features for http
proxies (`proxy`), async support (`async`, `async-https`), and https
with various TLS implementations (`https-rustls`, `https-rustls-probe`,
and `https` which is an alias for `https-rustls`).
Simple, minimal-dependency HTTP client. Optional features for HTTP
proxies and SOCKS5 proxies (`proxy`), async support (`async`,
`async-https`), and https with various TLS implementations
(`https-rustls`, `https-rustls-probe`, and `https` which is an alias
for `https-rustls`).

### Proxy Support

The `proxy` feature enables both HTTP CONNECT and SOCKS5 proxies:

```rust
// HTTP CONNECT proxy
let proxy = bitreq::Proxy::new_http("http://proxy.example.com:8080").unwrap();
let response = bitreq::get("http://example.com").with_proxy(proxy).send();

// SOCKS5 proxy (e.g., Tor)
let proxy = bitreq::Proxy::new_socks5("127.0.0.1:9050").unwrap();
let response = bitreq::get("http://example.com").with_proxy(proxy).send();
```

SOCKS5 proxies use domain-based addressing (RFC 1928 ATYP 0x03), so
DNS resolution happens at the proxy. This is required for `.onion`
routing through Tor.

Without any optional features, my casual testing indicates about 100
KB additional executable size for stripped release builds using this
Expand Down
61 changes: 2 additions & 59 deletions bitreq/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use std::time::Instant;

#[cfg(all(feature = "async", feature = "proxy"))]
use tokio::io::AsyncReadExt;
#[cfg(feature = "async")]
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, ReadHalf, WriteHalf};
#[cfg(feature = "async")]
Expand Down Expand Up @@ -337,36 +335,8 @@ impl AsyncConnection {
#[cfg(feature = "proxy")]
match &params.proxy {
Some(proxy) => {
// do proxy things
let mut tcp = Self::tcp_connect(&proxy.server, proxy.port).await?;

let proxy_request = proxy.connect(params.host, params.port);
tcp.write_all(proxy_request.as_bytes()).await?;
tcp.flush().await?;

// Max proxy response size to prevent unbounded memory allocation
const MAX_PROXY_RESPONSE_SIZE: usize = 16 * 1024;
let mut proxy_response = Vec::new();
let mut buf = [0; 256];

loop {
let n = tcp.read(&mut buf).await?;
if n == 0 {
// EOF reached
break;
}
proxy_response.extend_from_slice(&buf[..n]);
if proxy_response.len() > MAX_PROXY_RESPONSE_SIZE {
return Err(Error::ProxyConnect);
}
if n < buf.len() {
// Partial read indicates end of response
break;
}
}

crate::Proxy::verify_response(&proxy_response)?;

proxy.handshake_async(&mut tcp, params.host, params.port).await?;
Ok(tcp)
}
None => Self::tcp_connect(params.host, params.port).await,
Expand Down Expand Up @@ -710,35 +680,8 @@ impl Connection {
#[cfg(feature = "proxy")]
match &params.proxy {
Some(proxy) => {
// do proxy things
let mut tcp = Self::tcp_connect(&proxy.server, proxy.port, timeout_at)?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mind refactoring this code so that it just always calls handshake... and doesn't care what type it is and that's abstracted by the proxy?


write!(tcp, "{}", proxy.connect(params.host, params.port))?;
tcp.flush()?;

// Max proxy response size to prevent unbounded memory allocation
const MAX_PROXY_RESPONSE_SIZE: usize = 16 * 1024;
let mut proxy_response = Vec::new();
let mut buf = [0; 256];

loop {
let n = tcp.read(&mut buf)?;
if n == 0 {
// EOF reached
break;
}
proxy_response.extend_from_slice(&buf[..n]);
if proxy_response.len() > MAX_PROXY_RESPONSE_SIZE {
return Err(Error::ProxyConnect);
}
if n < buf.len() {
// Partial read indicates end of response
break;
}
}

crate::Proxy::verify_response(&proxy_response)?;

proxy.handshake_sync(&mut tcp, params.host, params.port)?;
Ok(tcp)
}
None => Self::tcp_connect(params.host, params.port, timeout_at),
Expand Down
Loading
Loading