Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/cli-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ name: "[impit-cli] Test & Build"
on:
push:
branches:
- "**"
- "master"
tags-ignore:
- "impit-cli-*"
paths:
- "impit-cli/**"
- "impit/**"
pull_request:
workflow_dispatch:

env:
CRATE_NAME: impit-cli
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/format.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Check formatting
on:
push:
branches:
- master
pull_request:

env:
RUSTFLAGS: "--cfg reqwest_unstable"

jobs:
fmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: rustfmt

- name: Rustfmt Check
uses: actions-rust-lang/rustfmt@v1
clippy:
name: clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: clippy

- name: Run clippy
run: cargo clippy --all --manifest-path=./Cargo.toml -- -D warnings
5 changes: 4 additions & 1 deletion impit-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ edition = "2021"
clap = { version = "4.5.21", features = ["derive"] }
impit = { path="../impit" }
tokio = { version="1.41.1", features = ["full"] }
aws-lc-rs = { version = "1.11.1", features = ["bindgen"] }
aws-lc-rs = { version = "1.11.1" }

[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "*", features = ["vendored"] }
aws-lc-rs = { version = "1.11.1", features = ["bindgen"] }

[target.aarch64-unknown-linux-musl.dependencies]
openssl = { version = "*", features = ["vendored"] }
aws-lc-rs = { version = "1.11.1", features = ["bindgen"] }

[target.arm-unknown-linux-musleabi.dependencies]
openssl = { version = "*", features = ["vendored"] }
aws-lc-rs = { version = "1.11.1", features = ["bindgen"] }
2 changes: 1 addition & 1 deletion impit-cli/src/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ pub(crate) fn process_headers(headers: Vec<String>) -> HashMap<String, String> {
}

map
}
}
88 changes: 44 additions & 44 deletions impit-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
use std::ffi::OsString;

use clap::{Parser, ValueEnum};
use impit::{impit::{Impit, RedirectBehavior}, emulation::Browser as ImpitBrowser, request::RequestOptions};
use impit::{
emulation::Browser as ImpitBrowser,
impit::{Impit, RedirectBehavior},
request::RequestOptions,
};

mod headers;

#[derive(Parser, Debug, Clone, Copy, ValueEnum)]
enum Browser {
Chrome,
Firefox,
Impit
Impit,
}

#[derive(Parser, Debug, Clone, Copy, ValueEnum)]
enum Method {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
TRACE
Get,
Post,
Put,
Delete,
Patch,
Head,
Options,
Trace,
}

/// CLI interface for the impit library.
Expand All @@ -30,51 +34,51 @@ enum Method {
#[command(about, long_about = None)]
struct CliArgs {
/// Method to use for the request.
#[arg(short='X', long, default_value = "get")]
#[arg(short = 'X', long, default_value = "get")]
method: Method,

/// HTTP headers to add to the request.
#[arg(short='H', long)]
#[arg(short = 'H', long)]
headers: Vec<String>,

/// What browser to use for the request.
#[arg(short='A', long, default_value = "impit")]
#[arg(short = 'A', long, default_value = "impit")]
impersonate: Browser,

/// If set, impit will ignore TLS errors.
#[arg(short='k', long, action)]
#[arg(short = 'k', long, action)]
ignore_tls_errors: bool,

/// If set, impit will fallback to vanilla HTTP if the impersonated browser fails.
#[arg(short='f', long, action)]
#[arg(short = 'f', long, action)]
fallback: bool,

/// Proxy to use for the request.
#[arg(short='x', long="proxy")]
#[arg(short = 'x', long = "proxy")]
proxy: Option<String>,

/// Maximum time in seconds to wait for the request to complete.
#[arg(short='m', long="max-time")]
#[arg(short = 'm', long = "max-time")]
max_time: Option<u64>,

/// Data to send with the request.
#[arg(short, long)]
data: Option<OsString>,

/// Enforce the use of HTTP/3 for the request. Note that if the server does not support HTTP/3, the request will fail.
#[arg(long="http3-only", action)]
#[arg(long = "http3-only", action)]
http3_prior_knowledge: bool,

/// Enable the use of HTTP/3. This will attempt to use HTTP/3, but fall back to earlier versions of HTTP if the server does not support it.
#[arg(long="http3", action)]
#[arg(long = "http3", action)]
enable_http3: bool,

/// Follow redirects
#[arg(short='L', long="location", action)]
#[arg(short = 'L', long = "location", action)]
follow_redirects: bool,

/// Follow redirects
#[arg(long="max-redirs", default_value = "50")]
#[arg(long = "max-redirs", default_value = "50")]
maximum_redirects: usize,

/// URL of the request to make
Expand All @@ -92,7 +96,7 @@ async fn main() {
client = match args.impersonate {
Browser::Chrome => client.with_browser(ImpitBrowser::Chrome),
Browser::Firefox => client.with_browser(ImpitBrowser::Firefox),
Browser::Impit => client
Browser::Impit => client,
};

if args.proxy.is_some() {
Expand All @@ -109,17 +113,13 @@ async fn main() {
client = client.with_redirect(RedirectBehavior::ManualRedirect);
}

let body: Option<Vec<u8>> = match args.data {
Some(data) => Some(data.into_string().unwrap().into_bytes()),
None => None
};
let body: Option<Vec<u8>> = args
.data
.map(|data| data.into_string().unwrap().into_bytes());

let mut client = client.build();

let timeout = match args.max_time {
Some(time) => Some(std::time::Duration::from_secs(time)),
None => None
};
let timeout = args.max_time.map(std::time::Duration::from_secs);

let options = RequestOptions {
headers: headers::process_headers(args.headers),
Expand All @@ -128,15 +128,15 @@ async fn main() {
};

let response = match args.method {
Method::GET => client.get(args.url, Some(options)).await.unwrap(),
Method::POST => client.post(args.url, body, Some(options)).await.unwrap(),
Method::PUT => client.put(args.url, body, Some(options)).await.unwrap(),
Method::DELETE => client.delete(args.url, Some(options)).await.unwrap(),
Method::PATCH => client.patch(args.url, body, Some(options)).await.unwrap(),
Method::HEAD => client.head(args.url, Some(options)).await.unwrap(),
Method::OPTIONS => client.options(args.url, Some(options)).await.unwrap(),
Method::TRACE => client.trace(args.url, Some(options)).await.unwrap(),
Method::Get => client.get(args.url, Some(options)).await.unwrap(),
Method::Post => client.post(args.url, body, Some(options)).await.unwrap(),
Method::Put => client.put(args.url, body, Some(options)).await.unwrap(),
Method::Delete => client.delete(args.url, Some(options)).await.unwrap(),
Method::Patch => client.patch(args.url, body, Some(options)).await.unwrap(),
Method::Head => client.head(args.url, Some(options)).await.unwrap(),
Method::Options => client.options(args.url, Some(options)).await.unwrap(),
Method::Trace => client.trace(args.url, Some(options)).await.unwrap(),
};

print!("{}", response.text().await.unwrap());
}
}
10 changes: 5 additions & 5 deletions impit/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use impit::impit::Impit;
use impit::emulation::Browser;

#[tokio::main]
async fn main() {
use impit::impit::Impit;

#[tokio::main]
async fn main() {
let mut impit = Impit::builder()
.with_browser(Browser::Firefox)
.with_http3()
Expand All @@ -18,4 +18,4 @@ use impit::emulation::Browser;
println!("{:#?}", e);
}
}
}
}
41 changes: 21 additions & 20 deletions impit/src/http3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use hickory_proto::error::ProtoError;
use hickory_proto::rr::rdata::svcb::SvcParamValue;
use hickory_proto::rr::RData;

use tokio::net::TcpStream as TokioTcpStream;
use hickory_client::client::{AsyncClient, ClientHandle};
use hickory_client::proto::iocompat::AsyncIoTokioAsStd;
use hickory_client::rr::Name;
use hickory_client::tcp::TcpClientStream;
use tokio::net::TcpStream as TokioTcpStream;

/// A struct encapsulating the components required to make HTTP/3 requests.
pub struct H3Engine {
Expand All @@ -17,7 +17,7 @@ pub struct H3Engine {
/// The background task that processes DNS queries.
bg_join_handle: tokio::task::JoinHandle<Result<(), ProtoError>>,
/// A map of hosts that support HTTP/3.
///
///
/// This is populated by the DNS queries and manual calls to `set_h3_support` (based on the `Alt-Svc` header).
/// Implicitly used as a cache for the DNS queries.
h3_alt_svc: HashMap<String, bool>,
Expand All @@ -30,38 +30,39 @@ impl H3Engine {
TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(([8, 8, 8, 8], 53).into());
let (client, bg) = AsyncClient::new(stream, sender, None).await.unwrap();

let bg_join_handle= tokio::spawn(bg);
let bg_join_handle = tokio::spawn(bg);

H3Engine {
client,
H3Engine {
client,
bg_join_handle,
h3_alt_svc: HashMap::new(),
}
}

pub async fn host_supports_h3(self: &mut Self, host: &String) -> bool {
pub async fn host_supports_h3(&mut self, host: &String) -> bool {
if let Some(supports_h3) = self.h3_alt_svc.get(host) {
return supports_h3.to_owned();
}

let domain_name = Name::from_utf8(host).unwrap();

let response = self.client.query(
domain_name,
hickory_proto::rr::DNSClass::IN,
hickory_proto::rr::RecordType::HTTPS
).await;

let dns_h3_support = response.is_ok_and(|response | {

let response = self
.client
.query(
domain_name,
hickory_proto::rr::DNSClass::IN,
hickory_proto::rr::RecordType::HTTPS,
)
.await;

let dns_h3_support = response.is_ok_and(|response| {
response.answers().iter().any(|answer| {
if let RData::HTTPS(data) = answer.data().unwrap() {
return data.svc_params().iter().any(|param| {
if let SvcParamValue::Alpn(alpn_protocols) = param.1.clone() {
return alpn_protocols.0.iter().any(|alpn| {
alpn == "h3"
})
return alpn_protocols.0.iter().any(|alpn| alpn == "h3");
}

false
});
}
Expand All @@ -73,7 +74,7 @@ impl H3Engine {
dns_h3_support
}

pub fn set_h3_support(self: &mut Self, host: &String, supports_h3: bool) {
pub fn set_h3_support(&mut self, host: &String, supports_h3: bool) {
if self.h3_alt_svc.contains_key(host) {
return;
}
Expand All @@ -86,4 +87,4 @@ impl Drop for H3Engine {
fn drop(&mut self) {
self.bg_join_handle.abort();
}
}
}
Loading
Loading