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
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@ pyo3-async-runtimes = { version = "0.23.0", features = ["tokio-runtime", "unstab
pyo3-log = { version = "0.12.1", optional = true }
pyo3-stub-gen = "0.7.0"
log = { version = "0.4", optional = true }
serde = { version = "1", features = ["derive"] }
serde = { version = "1.0.219", features = ["derive"] }
mime = "0.3.17"
indexmap = { version = "2.7.0", features = ["serde"] }
cookie = "0.18.0"
arc-swap = "1.7.1"
url = "2.5"
rquest = { version = "2.2.1", features = [
rquest = { version = "3.0.5", features = [
"full",
"multipart",
"websocket",
"hickory-dns",
"apple-network-device-binding",
] }
rquest-util = { version = "0.2.5" }
bytes = "1.10.1"
futures-util = { version = "0.3.31", default-features = false }

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ In fact, most device models share the same `TLS`/`HTTP2` configuration, with the

| **Browser** | **Versions** |
|---------------|--------------------------------------------------------------------------------------------------|
| **Chrome** | `Chrome100`, `Chrome101`, `Chrome104`, `Chrome105`, `Chrome106`, `Chrome107`, `Chrome108`, `Chrome109`, `Chrome114`, `Chrome116`, `Chrome117`, `Chrome118`, `Chrome119`, `Chrome120`, `Chrome123`, `Chrome124`, `Chrome126`, `Chrome127`, `Chrome128`, `Chrome129`, `Chrome130`, `Chrome131`, `Chrome133` |
| **Edge** | `Edge101`, `Edge122`, `Edge127`, `Edge131` |
| **Safari** | `SafariIos17_2`, `SafariIos17_4_1`, `SafariIos16_5`, `Safari15_3`, `Safari15_5`, `Safari15_6_1`, `Safari16`, `Safari16_5`, `Safari17_0`, `Safari17_2_1`, `Safari17_4_1`, `Safari17_5`, `Safari18`, `SafariIPad18`, `Safari18_2`, `Safari18_1_1` |
| **OkHttp** | `OkHttp3_9`, `OkHttp3_11`, `OkHttp3_13`, `OkHttp3_14`, `OkHttp4_9`, `OkHttp4_10`, `OkHttp5` |
| **Firefox** | `Firefox109`, `Firefox117`, `Firefox128`, `Firefox133`, `Firefox135`, `FirefoxPrivate135`, `FirefoxAndroid135` |
| **Chrome** | `Chrome100`, `Chrome101`, `Chrome104`, `Chrome105`, `Chrome106`, `Chrome107`, `Chrome108`, `Chrome109`, `Chrome114`, `Chrome116`, `Chrome117`, `Chrome118`, `Chrome119`, `Chrome120`, `Chrome123`, `Chrome124`, `Chrome126`, `Chrome127`, `Chrome128`, `Chrome129`, `Chrome130`, `Chrome131`, `Chrome132`, `Chrome133` |
| **Edge** | `Edge101`, `Edge122`, `Edge127`, `Edge131`, `Edge134` |
| **Safari** | `SafariIos17_2`, `SafariIos17_4_1`, `SafariIos16_5`, `Safari15_3`, `Safari15_5`, `Safari15_6_1`, `Safari16`, `Safari16_5`, `Safari17_0`, `Safari17_2_1`, `Safari17_4_1`, `Safari17_5`, `Safari18`, `SafariIPad18`, `Safari18_2`, `Safari18_1_1`, `Safari18_3` |
| **OkHttp** | `OkHttp3_9`, `OkHttp3_11`, `OkHttp3_13`, `OkHttp3_14`, `OkHttp4_9`, `OkHttp4_10`, `OkHttp4_12`, `OkHttp5` |
| **Firefox** | `Firefox109`, `Firefox117`, `Firefox128`, `Firefox133`, `Firefox135`, `FirefoxPrivate135`, `FirefoxAndroid135`, `Firefox136`, `FirefoxPrivate136`|

## Documentation

Expand Down
26 changes: 0 additions & 26 deletions examples/base_url.py

This file was deleted.

23 changes: 0 additions & 23 deletions examples/blocking/base_url.py

This file was deleted.

140 changes: 67 additions & 73 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{execute_request2, execute_websocket_request2};
use super::request::{execute_request, execute_websocket_request};
use crate::{
apply_option, dns,
error::{wrap_rquest_error, wrap_url_parse_error},
Expand All @@ -8,7 +8,6 @@ use crate::{
Method, TlsVersion,
},
};
use arc_swap::ArcSwap;
use pyo3::{
prelude::*,
pybacked::PyBackedStr,
Expand All @@ -17,16 +16,16 @@ use pyo3::{
use pyo3_async_runtimes::tokio::future_into_py;
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
use rquest::{Url, redirect::Policy};
use std::{net::IpAddr, num::NonZeroUsize, ops::Deref};
use std::{sync::Arc, time::Duration};
use std::time::Duration;
use std::{net::IpAddr, ops::Deref};

/// A client for making HTTP requests.
#[gen_stub_pyclass]
#[pyclass]
pub struct Client(ArcSwap<rquest::Client>);
pub struct Client(rquest::Client);

impl Deref for Client {
type Target = ArcSwap<rquest::Client>;
type Target = rquest::Client;

#[inline(always)]
fn deref(&self) -> &Self::Target {
Expand Down Expand Up @@ -293,8 +292,8 @@ impl Client {
url: PyBackedStr,
kwds: Option<RequestParams>,
) -> PyResult<Bound<'rt, PyAny>> {
let client = self.0.load();
future_into_py(py, execute_request2(client, method, url, kwds))
let client = self.0.clone();
future_into_py(py, execute_request(client, method, url, kwds))
}

/// Sends a WebSocket request.
Expand Down Expand Up @@ -348,8 +347,8 @@ impl Client {
url: PyBackedStr,
kwds: Option<WebSocketParams>,
) -> PyResult<Bound<'rt, PyAny>> {
let client = self.0.load();
future_into_py(py, execute_websocket_request2(client, url, kwds))
let client = self.0.clone();
future_into_py(py, execute_websocket_request(client, url, kwds))
}
}

Expand Down Expand Up @@ -427,10 +426,10 @@ impl Client {

// Impersonation options.
if let Some(impersonate) = params.impersonate.take() {
builder = builder.impersonate(
rquest::Impersonate::builder()
.impersonate(impersonate.into_ffi())
.impersonate_os(
builder = builder.emulation(
rquest_util::EmulationOption::builder()
.emulation(impersonate.into_ffi())
.emulation_os(
params
.impersonate_os
.map(ImpersonateOS::into_ffi)
Expand All @@ -442,15 +441,6 @@ impl Client {
);
}

// Base URL options.
apply_option!(
apply_transformed_option_ref,
builder,
params.base_url,
base_url,
AsRef::<str>::as_ref
);

// User agent options.
apply_option!(
apply_transformed_option_ref,
Expand Down Expand Up @@ -554,13 +544,7 @@ impl Client {
params.pool_max_idle_per_host,
pool_max_idle_per_host
);
apply_option!(
apply_transformed_option,
builder,
params.pool_max_size,
pool_max_size,
NonZeroUsize::new
);
apply_option!(apply_if_some, builder, params.pool_max_size, pool_max_size);

// Protocol options.
apply_option!(
Expand Down Expand Up @@ -629,21 +613,25 @@ impl Client {
local_address,
IpAddr::from
);
rquest::cfg_bindable_device!({
apply_option!(apply_if_some, builder, params.interface, interface);
});
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_os = "linux",
target_os = "ios",
target_os = "visionos",
target_os = "macos",
target_os = "tvos",
target_os = "watchos"
))]
apply_option!(apply_if_some, builder, params.interface, interface);

// Compression options.
apply_option!(apply_if_some, builder, params.gzip, gzip);
apply_option!(apply_if_some, builder, params.brotli, brotli);
apply_option!(apply_if_some, builder, params.deflate, deflate);
apply_option!(apply_if_some, builder, params.zstd, zstd);

builder
.build()
.map(ArcSwap::from_pointee)
.map(Client)
.map_err(wrap_rquest_error)
builder.build().map(Client).map_err(wrap_rquest_error)
})
}

Expand All @@ -666,10 +654,8 @@ impl Client {
pub fn user_agent(&self, py: Python) -> Option<String> {
py.allow_threads(|| {
self.0
.load()
.user_agent()
.and_then(|hv| hv.to_str().ok())
.map(ToString::to_string)
.and_then(|hv| hv.to_str().map(ToString::to_string).ok())
})
}

Expand All @@ -690,9 +676,8 @@ impl Client {
/// ```
#[getter]
pub fn headers<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyDict>> {
let binding = self.0.load();
let headers = binding.headers();
IntoPyHeaderMap(headers).into_pyobject(py).ok()
let headers = self.0.headers();
IntoPyHeaderMap(&headers).into_pyobject(py).ok()
}

/// Returns the cookies for the given URL.
Expand Down Expand Up @@ -722,7 +707,7 @@ impl Client {
) -> PyResult<Bound<'py, PyList>> {
let cookies = py.allow_threads(|| {
let url = Url::parse(url.as_ref()).map_err(wrap_url_parse_error)?;
let cookies = self.0.load().get_cookies(&url);
let cookies = self.0.get_cookies(&url);
Ok::<_, PyErr>(cookies)
})?;

Expand Down Expand Up @@ -757,7 +742,7 @@ impl Client {
) -> PyResult<()> {
py.allow_threads(|| {
let url = Url::parse(url.as_ref()).map_err(wrap_url_parse_error)?;
self.0.load().set_cookies(&url, cookies.0);
self.0.set_cookies(&url, cookies.0);
Ok(())
})
}
Expand Down Expand Up @@ -790,18 +775,17 @@ impl Client {
/// )
/// ```
#[pyo3(signature = (**kwds))]
pub fn update(&self, py: Python, mut kwds: Option<UpdateClientParams>) {
pub fn update(&self, py: Python, mut kwds: Option<UpdateClientParams>) -> PyResult<()> {
py.allow_threads(|| {
let params = kwds.get_or_insert_default();
let mut this = self.0.load_full();
let mut client_mut = Arc::make_mut(&mut this).as_mut();
let mut update = self.0.update();

// Impersonation options.
if let Some(impersonate) = params.impersonate.take() {
client_mut.impersonate(
rquest::Impersonate::builder()
.impersonate(impersonate.into_ffi())
.impersonate_os(
update = update.emulation(
rquest_util::EmulationOption::builder()
.emulation(impersonate.into_ffi())
.emulation_os(
params
.impersonate_os
.map(ImpersonateOS::into_ffi)
Expand All @@ -814,32 +798,42 @@ impl Client {
}

// Default headers options.
params.headers.take().map(|mut default_headers| {
std::mem::swap(client_mut.headers(), &mut default_headers.0)
});
if let Some(mut default_headers) = params.headers.take() {
update = update.headers(|headers| std::mem::swap(headers, &mut default_headers.0));
}

// Headers order options.
params.headers_order.take().map(|value| {
client_mut.headers_order(value.0);
});
apply_option!(
apply_transformed_option,
update,
params.headers_order,
headers_order,
|s: FromPyHeaderOrderList| s.0
);

// Network options.
params.proxies.take().map(|proxies| {
client_mut.proxies(proxies);
});
params
.local_address
.take()
.map(|value| client_mut.local_address::<std::net::IpAddr>(value.into()));
rquest::cfg_bindable_device!({
params
.interface
.take()
.map(|value| client_mut.interface(value));
});
apply_option!(apply_if_some, update, params.proxies, proxies);
apply_option!(
apply_transformed_option,
update,
params.local_address,
local_address,
IpAddr::from
);
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_os = "linux",
target_os = "ios",
target_os = "visionos",
target_os = "macos",
target_os = "tvos",
target_os = "watchos"
))]
apply_option!(apply_if_some, update, params.interface, interface);

// Apply the changes.
self.0.store(this);
update.apply().map_err(wrap_rquest_error)
})
}
}
13 changes: 9 additions & 4 deletions src/async_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ mod http;
mod request;
mod ws;

pub use self::request::{execute_request2, execute_websocket_request2};
pub use self::{
client::Client,
http::{Response, Streamer},
Expand All @@ -12,7 +11,7 @@ pub use self::{
use crate::param::{RequestParams, WebSocketParams};
use crate::typing::{LookupIpStrategy, Method};
use crate::{apply_option, dns};
use request::{execute_request, execute_websocket_request};
pub use request::{execute_request, execute_websocket_request};
use std::sync::LazyLock;

#[macro_export]
Expand Down Expand Up @@ -89,7 +88,13 @@ pub async fn shortcut_request<U>(
where
U: AsRef<str>,
{
execute_request(&*DEFAULT_CLIENT, method, url.as_ref().to_string(), params).await
execute_request(
DEFAULT_CLIENT.clone(),
method,
url.as_ref().to_string(),
params,
)
.await
}

/// Send a shortcut WebSocket request.
Expand All @@ -101,5 +106,5 @@ pub async fn shortcut_websocket_request<U>(
where
U: AsRef<str>,
{
execute_websocket_request(&*DEFAULT_CLIENT, url.as_ref().to_string(), params).await
execute_websocket_request(DEFAULT_CLIENT.clone(), url.as_ref().to_string(), params).await
}
Loading
Loading