diff --git a/Cargo.toml b/Cargo.toml index f88fc243..bdd7b81f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/README.md b/README.md index ef41f255..2366025c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/examples/base_url.py b/examples/base_url.py deleted file mode 100644 index 75db9df6..00000000 --- a/examples/base_url.py +++ /dev/null @@ -1,26 +0,0 @@ -import asyncio -from rnet import Impersonate, Client - - -async def main(): - client = Client( - base_url="https://httpbin.org", - impersonate=Impersonate.Firefox135, - user_agent="rnet", - ) - resp = await client.get("/stream/20") - print("Status Code: ", resp.status_code) - print("Version: ", resp.version) - print("Response URL: ", resp.url) - print("Headers: ", resp.headers) - print("Content-Length: ", resp.content_length) - print("Encoding: ", resp.encoding) - print("Remote Address: ", resp.remote_addr) - streamer = resp.stream() - async for chunk in streamer: - print("Chunk: ", chunk) - await asyncio.sleep(0.1) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/blocking/base_url.py b/examples/blocking/base_url.py deleted file mode 100644 index bd4bcbeb..00000000 --- a/examples/blocking/base_url.py +++ /dev/null @@ -1,23 +0,0 @@ -from rnet import BlockingClient - - -def main(): - client = BlockingClient( - base_url="https://httpbin.org", - user_agent="rnet", - ) - resp = client.get("/stream/20") - print("Status Code: ", resp.status_code) - print("Version: ", resp.version) - print("Response URL: ", resp.url) - print("Headers: ", resp.headers) - print("Content-Length: ", resp.content_length) - print("Encoding: ", resp.encoding) - print("Remote Address: ", resp.remote_addr) - with resp.stream() as streamer: - for chunk in streamer: - print("Chunk: ", chunk) - - -if __name__ == "__main__": - main() diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 082adfb6..94e1face 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -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}, @@ -8,7 +8,6 @@ use crate::{ Method, TlsVersion, }, }; -use arc_swap::ArcSwap; use pyo3::{ prelude::*, pybacked::PyBackedStr, @@ -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); +pub struct Client(rquest::Client); impl Deref for Client { - type Target = ArcSwap; + type Target = rquest::Client; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -293,8 +292,8 @@ impl Client { url: PyBackedStr, kwds: Option, ) -> PyResult> { - 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. @@ -348,8 +347,8 @@ impl Client { url: PyBackedStr, kwds: Option, ) -> PyResult> { - 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)) } } @@ -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) @@ -442,15 +441,6 @@ impl Client { ); } - // Base URL options. - apply_option!( - apply_transformed_option_ref, - builder, - params.base_url, - base_url, - AsRef::::as_ref - ); - // User agent options. apply_option!( apply_transformed_option_ref, @@ -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!( @@ -629,9 +613,17 @@ 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); @@ -639,11 +631,7 @@ impl Client { 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) }) } @@ -666,10 +654,8 @@ impl Client { pub fn user_agent(&self, py: Python) -> Option { 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()) }) } @@ -690,9 +676,8 @@ impl Client { /// ``` #[getter] pub fn headers<'py>(&self, py: Python<'py>) -> Option> { - 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. @@ -722,7 +707,7 @@ impl Client { ) -> PyResult> { 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) })?; @@ -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(()) }) } @@ -790,18 +775,17 @@ impl Client { /// ) /// ``` #[pyo3(signature = (**kwds))] - pub fn update(&self, py: Python, mut kwds: Option) { + pub fn update(&self, py: Python, mut kwds: Option) -> 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) @@ -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::(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) }) } } diff --git a/src/async_impl/mod.rs b/src/async_impl/mod.rs index 21d6ba39..3857b26f 100644 --- a/src/async_impl/mod.rs +++ b/src/async_impl/mod.rs @@ -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}, @@ -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] @@ -89,7 +88,13 @@ pub async fn shortcut_request( where U: AsRef, { - 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. @@ -101,5 +106,5 @@ pub async fn shortcut_websocket_request( where U: AsRef, { - 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 } diff --git a/src/async_impl/request.rs b/src/async_impl/request.rs index 7aff606e..d9742781 100644 --- a/src/async_impl/request.rs +++ b/src/async_impl/request.rs @@ -7,21 +7,19 @@ use crate::{ typing::{Method, Version}, }; -use rquest::header; use rquest::redirect::Policy; +use rquest::{Client, header}; use std::net::IpAddr; -use std::ops::Deref; -use std::{sync::Arc, time::Duration}; +use std::time::Duration; /// Executes an HTTP request. -pub async fn execute_request( - client: C, +pub async fn execute_request( + client: Client, method: Method, url: U, mut params: Option, ) -> Result where - C: Deref, U: AsRef, { let params = params.get_or_insert_default(); @@ -75,9 +73,17 @@ where 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); // Headers options. apply_option!(apply_if_some_inner, builder, params.headers, headers); @@ -130,13 +136,12 @@ where } /// Executes a WebSocket request. -pub async fn execute_websocket_request( - client: C, +pub async fn execute_websocket_request( + client: Client, url: U, mut params: Option, ) -> Result where - C: Deref, U: AsRef, { let params = params.get_or_insert_default(); @@ -177,86 +182,60 @@ where accept_unmasked_frames ); - // The origin to use for the request. - builder = builder.with_builder(|mut builder| { - // Network options. - apply_option!( - apply_transformed_option_ref, - builder, - params.proxy, - proxy, - AsRef::::as_ref - ); - apply_option!( - apply_transformed_option, - builder, - params.local_address, - local_address, - IpAddr::from - ); - rquest::cfg_bindable_device!( - apply_option!(apply_if_some, builder, params.interface, interface); - ); - - // Authentication options. - apply_option!( - apply_transformed_option_ref, - builder, - params.auth, - auth, - AsRef::::as_ref - ); + // Network options. + apply_option!( + apply_transformed_option_ref, + builder, + params.proxy, + proxy, + AsRef::::as_ref + ); + apply_option!( + apply_transformed_option, + builder, + 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, builder, params.interface, interface); - // Bearer authentication options. - apply_option!(apply_if_some, builder, params.bearer_auth, bearer_auth); + // Authentication options. + apply_option!( + apply_transformed_option_ref, + builder, + params.auth, + auth, + AsRef::::as_ref + ); - // Basic authentication options. - if let Some(basic_auth) = params.basic_auth.take() { - builder = builder.basic_auth(basic_auth.0, basic_auth.1); - } + // Bearer authentication options. + apply_option!(apply_if_some, builder, params.bearer_auth, bearer_auth); - // Headers options. - apply_option!(apply_if_some_inner, builder, params.headers, headers); + // Basic authentication options. + if let Some(basic_auth) = params.basic_auth.take() { + builder = builder.basic_auth(basic_auth.0, basic_auth.1); + } - // Cookies options. - if let Some(cookies) = params.cookies.take() { - builder = builder.header(header::COOKIE, cookies.0); - } + // Headers options. + apply_option!(apply_if_some_inner, builder, params.headers, headers); - // Query options. - apply_option!(apply_if_some_ref, builder, params.query, query); + // Cookies options. + if let Some(cookies) = params.cookies.take() { + builder = builder.header(header::COOKIE, cookies.0); + } - builder - }); + // Query options. + apply_option!(apply_if_some_ref, builder, params.query, query); WebSocket::new(builder).await } - -/// Executes an HTTP request. -#[inline(always)] -pub async fn execute_request2( - client: C, - method: Method, - url: U, - params: Option, -) -> Result -where - C: Deref>, - U: AsRef, -{ - execute_request(client.as_ref(), method, url, params).await -} - -/// Executes a WebSocket request. -#[inline(always)] -pub async fn execute_websocket_request2( - client: C, - url: U, - params: Option, -) -> Result -where - C: Deref>, - U: AsRef, -{ - execute_websocket_request(client.as_ref(), url, params).await -} diff --git a/src/async_impl/ws/message.rs b/src/async_impl/ws/message.rs index fafda44b..904bae32 100644 --- a/src/async_impl/ws/message.rs +++ b/src/async_impl/ws/message.rs @@ -1,8 +1,13 @@ -use pyo3::{prelude::*, types::PyBytes}; +use bytes::Bytes; +use pyo3::{ + prelude::*, + pybacked::{PyBackedBytes, PyBackedStr}, +}; use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods}; +use rquest::Utf8Bytes; use crate::{ - buffer::{Buffer, PyBufferProtocol}, + buffer::{Buffer, BytesBuffer, PyBufferProtocol}, error::wrap_rquest_error, typing::Json, }; @@ -64,7 +69,7 @@ impl Message { #[getter] pub fn binary<'py>(&self, py: Python<'py>) -> Option> { if let rquest::Message::Binary(data) = &self.0 { - Buffer::new(data.to_owned()).into_bytes_ref(py).ok() + BytesBuffer::new(data.clone()).into_bytes_ref(py).ok() } else { None } @@ -78,7 +83,7 @@ impl Message { #[getter] pub fn ping<'py>(&self, py: Python<'py>) -> Option> { if let rquest::Message::Ping(data) = &self.0 { - Buffer::new(data.to_owned()).into_bytes_ref(py).ok() + BytesBuffer::new(data.clone()).into_bytes_ref(py).ok() } else { None } @@ -92,7 +97,7 @@ impl Message { #[getter] pub fn pong<'py>(&self, py: Python<'py>) -> Option> { if let rquest::Message::Pong(data) = &self.0 { - Buffer::new(data.to_owned()).into_bytes_ref(py).ok() + BytesBuffer::new(data.clone()).into_bytes_ref(py).ok() } else { None } @@ -105,8 +110,8 @@ impl Message { /// An optional tuple containing the close code and reason. #[getter] pub fn close(&self) -> Option<(u16, Option<&str>)> { - if let rquest::Message::Close { code, reason } = &self.0 { - Some((u16::from(*code), reason.as_deref())) + if let rquest::Message::Close(Some(s)) = &self.0 { + Some((s.code.0, Some(s.reason.as_str()))) } else { None } @@ -166,8 +171,9 @@ impl Message { #[staticmethod] #[pyo3(signature = (text))] #[inline(always)] - pub fn from_text(text: &str) -> Self { - Message(rquest::Message::Text(text.to_owned())) + pub fn from_text(text: PyBackedStr) -> Self { + let msg = rquest::Message::Text(rquest::Utf8Bytes::from(text.as_ref() as &str)); + Message(msg) } /// Creates a new binary message. @@ -182,8 +188,9 @@ impl Message { #[staticmethod] #[pyo3(signature = (data))] #[inline(always)] - pub fn from_binary(data: &Bound) -> Self { - Message(rquest::Message::Binary(data.as_bytes().to_vec())) + pub fn from_binary(data: PyBackedBytes) -> Self { + let msg = rquest::Message::binary(data.as_ref().to_owned()); + Message(msg) } /// Creates a new ping message. @@ -198,8 +205,9 @@ impl Message { #[staticmethod] #[pyo3(signature = (data))] #[inline(always)] - pub fn from_ping(data: &Bound) -> Self { - Message(rquest::Message::Ping(data.as_bytes().to_vec())) + pub fn from_ping(data: PyBackedBytes) -> Self { + let msg = rquest::Message::Ping(Bytes::from(data.as_ref().to_owned())); + Message(msg) } /// Creates a new pong message. @@ -214,8 +222,9 @@ impl Message { #[staticmethod] #[pyo3(signature = (data))] #[inline(always)] - pub fn from_pong(data: &Bound) -> Self { - Message(rquest::Message::Pong(data.as_bytes().to_vec())) + pub fn from_pong(data: PyBackedBytes) -> Self { + let msg = rquest::Message::Pong(Bytes::from(data.as_ref().to_owned())); + Message(msg) } /// Creates a new close message. @@ -232,10 +241,11 @@ impl Message { #[pyo3(signature = (code, reason=None))] #[inline(always)] pub fn from_close(code: u16, reason: Option) -> Self { - Message(rquest::Message::Close { - code: rquest::CloseCode::from(code), - reason, - }) + let msg = rquest::Message::Close(Some(rquest::CloseFrame { + code: rquest::CloseCode(code), + reason: Utf8Bytes::from(reason.as_deref().unwrap_or("Goodbye")), + })); + Message(msg) } } diff --git a/src/async_impl/ws/mod.rs b/src/async_impl/ws/mod.rs index 04863447..dc959a6f 100644 --- a/src/async_impl/ws/mod.rs +++ b/src/async_impl/ws/mod.rs @@ -12,7 +12,7 @@ pub use message::Message; use pyo3::{IntoPyObjectExt, prelude::*, types::PyDict}; use pyo3_async_runtimes::tokio::future_into_py; use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods}; -use rquest::header::HeaderMap; +use rquest::header::{HeaderMap, HeaderValue}; use std::sync::Arc; use tokio::sync::Mutex; @@ -27,7 +27,7 @@ pub struct WebSocket { status_code: StatusCode, remote_addr: Option, headers: HeaderMap, - protocol: Option, + protocol: Option, sender: Sender, receiver: Receiver, } @@ -41,7 +41,7 @@ impl WebSocket { let remote_addr = response.remote_addr().map(SocketAddr::from); let headers = response.headers().clone(); let websocket = response.into_websocket().await.map_err(wrap_rquest_error)?; - let protocol = websocket.protocol().map(ToOwned::to_owned); + let protocol = websocket.protocol().cloned(); let (sender, receiver) = websocket.split(); Ok(WebSocket { @@ -104,12 +104,13 @@ impl WebSocket { if let Some(mut sender) = sender { sender - .send(rquest::Message::Close { + .send(rquest::Message::Close(Some(rquest::CloseFrame { code: code - .map(rquest::CloseCode::from) - .unwrap_or(rquest::CloseCode::Normal), - reason, - }) + .map(rquest::CloseCode) + .unwrap_or(rquest::CloseCode::NORMAL), + + reason: rquest::Utf8Bytes::from(reason.as_deref().unwrap_or("Goodbye")), + }))) .await .map_err(wrap_rquest_error)?; sender.flush().await.map_err(wrap_rquest_error)?; @@ -217,7 +218,12 @@ impl WebSocket { /// An optional string representing the WebSocket protocol. #[inline(always)] pub fn protocol(&self) -> Option<&str> { - self.protocol.as_deref() + self.protocol + .as_ref() + .map(HeaderValue::to_str) + .transpose() + .ok() + .flatten() } /// Receives a message from the WebSocket. diff --git a/src/blocking/client.rs b/src/blocking/client.rs index dc00a554..3b7e4149 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -1,6 +1,6 @@ use super::{http::BlockingResponse, ws::BlockingWebSocket}; use crate::{ - async_impl::{self, execute_request2, execute_websocket_request2}, + async_impl::{self, execute_request, execute_websocket_request}, param::{ClientParams, RequestParams, UpdateClientParams, WebSocketParams}, typing::{FromPyCookieList, Method}, }; @@ -276,9 +276,9 @@ impl BlockingClient { kwds: Option, ) -> PyResult { py.allow_threads(|| { - let client = self.0.load(); + let client = self.0.clone(); pyo3_async_runtimes::tokio::get_runtime() - .block_on(execute_request2(client, method, url, kwds)) + .block_on(execute_request(client, method, url, kwds)) .map(Into::into) }) } @@ -335,9 +335,9 @@ impl BlockingClient { kwds: Option, ) -> PyResult { py.allow_threads(|| { - let client = self.0.load(); + let client = self.0.clone(); pyo3_async_runtimes::tokio::get_runtime() - .block_on(execute_websocket_request2(client, url, kwds)) + .block_on(execute_websocket_request(client, url, kwds)) .map(Into::into) }) } @@ -356,7 +356,6 @@ impl BlockingClient { /// impersonate_os: typing.Optional[ImpersonateOS] /// impersonate_skip_http2: typing.Optional[builtins.bool] /// impersonate_skip_headers: typing.Optional[builtins.bool] - /// base_url: typing.Optional[str] /// user_agent: typing.Optional[str] /// default_headers: typing.Optional[typing.Dict[str, bytes]] /// headers_order: typing.Optional[typing.List[str]] @@ -501,7 +500,7 @@ impl BlockingClient { /// ``` #[pyo3(signature = (**kwds))] #[inline(always)] - fn update(&self, py: Python, kwds: Option) { - self.0.update(py, kwds); + fn update(&self, py: Python, kwds: Option) -> PyResult<()> { + self.0.update(py, kwds) } } diff --git a/src/param/client.rs b/src/param/client.rs index 014fad6b..7989c94b 100644 --- a/src/param/client.rs +++ b/src/param/client.rs @@ -20,9 +20,6 @@ pub struct ClientParams { /// Whether to skip impersonate headers. pub impersonate_skip_headers: Option, - /// The base URL to use for the request. - pub base_url: Option, - /// The user agent to use for the request. pub user_agent: Option, @@ -195,7 +192,6 @@ impl<'py> FromPyObject<'py> for ClientParams { extract_option!(ob, params, impersonate_skip_http2); extract_option!(ob, params, impersonate_skip_headers); - extract_option!(ob, params, base_url); extract_option!(ob, params, user_agent); extract_option!(ob, params, default_headers); extract_option!(ob, params, headers_order); diff --git a/src/typing/enums.rs b/src/typing/enums.rs index 269a9d57..687f96f1 100644 --- a/src/typing/enums.rs +++ b/src/typing/enums.rs @@ -88,7 +88,7 @@ define_enum_with_conversion!( /// An impersonate. const, Impersonate, - rquest::Impersonate, + rquest_util::Emulation, Chrome100, Chrome101, Chrome104, @@ -111,6 +111,7 @@ define_enum_with_conversion!( Chrome129, Chrome130, Chrome131, + Chrome132, Chrome133, Edge101, Edge122, @@ -123,6 +124,8 @@ define_enum_with_conversion!( Firefox135, FirefoxPrivate135, FirefoxAndroid135, + Firefox136, + FirefoxPrivate136, SafariIos17_2, SafariIos17_4_1, SafariIos16_5, @@ -138,6 +141,7 @@ define_enum_with_conversion!( Safari18, SafariIPad18, Safari18_2, + Safari18_3, SafariIos18_1_1, OkHttp3_9, OkHttp3_11, @@ -145,6 +149,7 @@ define_enum_with_conversion!( OkHttp3_14, OkHttp4_9, OkHttp4_10, + OkHttp4_12, OkHttp5 ); @@ -152,7 +157,7 @@ define_enum_with_conversion!( /// An impersonate operating system. const, ImpersonateOS, - rquest::ImpersonateOS, + rquest_util::EmulationOS, Windows, MacOS, Linux, diff --git a/tests/client_test.py b/tests/client_test.py index a468f16c..5f1accb8 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -43,17 +43,6 @@ async def test_update_impersonate(): == "Mozilla/5.0 (Windows NT 10.0; rv:135.0) Gecko/20100101 Firefox/135.0" ) - -@pytest.mark.asyncio -@pytest.mark.flaky(reruns=3, reruns_delay=2) -async def test_base_url(): - base_url = "https://httpbin.org" - client = rnet.Client(tls_info=True, base_url=base_url) - url = "/anything" - response = await client.get(url) - assert response.url == base_url + url - - @pytest.mark.asyncio @pytest.mark.flaky(reruns=3, reruns_delay=2) async def test_alps_new_endpoint():