diff --git a/impit-node/yarn.lock b/impit-node/yarn.lock index 808b121c..6ae21d34 100644 --- a/impit-node/yarn.lock +++ b/impit-node/yarn.lock @@ -2500,58 +2500,58 @@ __metadata: languageName: node linkType: hard -"impit-darwin-arm64@npm:0.7.0": - version: 0.7.0 - resolution: "impit-darwin-arm64@npm:0.7.0" +"impit-darwin-arm64@npm:0.7.1": + version: 0.7.1 + resolution: "impit-darwin-arm64@npm:0.7.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"impit-darwin-x64@npm:0.7.0": - version: 0.7.0 - resolution: "impit-darwin-x64@npm:0.7.0" +"impit-darwin-x64@npm:0.7.1": + version: 0.7.1 + resolution: "impit-darwin-x64@npm:0.7.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"impit-linux-arm64-gnu@npm:0.7.0": - version: 0.7.0 - resolution: "impit-linux-arm64-gnu@npm:0.7.0" +"impit-linux-arm64-gnu@npm:0.7.1": + version: 0.7.1 + resolution: "impit-linux-arm64-gnu@npm:0.7.1" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"impit-linux-arm64-musl@npm:0.7.0": - version: 0.7.0 - resolution: "impit-linux-arm64-musl@npm:0.7.0" +"impit-linux-arm64-musl@npm:0.7.1": + version: 0.7.1 + resolution: "impit-linux-arm64-musl@npm:0.7.1" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"impit-linux-x64-gnu@npm:0.7.0": - version: 0.7.0 - resolution: "impit-linux-x64-gnu@npm:0.7.0" +"impit-linux-x64-gnu@npm:0.7.1": + version: 0.7.1 + resolution: "impit-linux-x64-gnu@npm:0.7.1" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"impit-linux-x64-musl@npm:0.7.0": - version: 0.7.0 - resolution: "impit-linux-x64-musl@npm:0.7.0" +"impit-linux-x64-musl@npm:0.7.1": + version: 0.7.1 + resolution: "impit-linux-x64-musl@npm:0.7.1" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"impit-win32-arm64-msvc@npm:0.7.0": - version: 0.7.0 - resolution: "impit-win32-arm64-msvc@npm:0.7.0" +"impit-win32-arm64-msvc@npm:0.7.1": + version: 0.7.1 + resolution: "impit-win32-arm64-msvc@npm:0.7.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"impit-win32-x64-msvc@npm:0.7.0": - version: 0.7.0 - resolution: "impit-win32-x64-msvc@npm:0.7.0" +"impit-win32-x64-msvc@npm:0.7.1": + version: 0.7.1 + resolution: "impit-win32-x64-msvc@npm:0.7.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2564,14 +2564,14 @@ __metadata: "@types/express": "npm:^5.0.0" "@types/node": "npm:^24.0.0" express: "npm:^5.0.0" - impit-darwin-arm64: "npm:0.7.0" - impit-darwin-x64: "npm:0.7.0" - impit-linux-arm64-gnu: "npm:0.7.0" - impit-linux-arm64-musl: "npm:0.7.0" - impit-linux-x64-gnu: "npm:0.7.0" - impit-linux-x64-musl: "npm:0.7.0" - impit-win32-arm64-msvc: "npm:0.7.0" - impit-win32-x64-msvc: "npm:0.7.0" + impit-darwin-arm64: "npm:0.7.1" + impit-darwin-x64: "npm:0.7.1" + impit-linux-arm64-gnu: "npm:0.7.1" + impit-linux-arm64-musl: "npm:0.7.1" + impit-linux-x64-gnu: "npm:0.7.1" + impit-linux-x64-musl: "npm:0.7.1" + impit-win32-arm64-msvc: "npm:0.7.1" + impit-win32-x64-msvc: "npm:0.7.1" proxy-chain: "npm:^2.5.9" socks-server-lib: "npm:^0.0.3" tough-cookie: "npm:^6.0.0" diff --git a/impit-python/Cargo.toml b/impit-python/Cargo.toml index 45201346..446bbdc6 100644 --- a/impit-python/Cargo.toml +++ b/impit-python/Cargo.toml @@ -15,7 +15,7 @@ h2 = "0.4.7" reqwest = "0.12.9" tokio-stream = "0.1.17" bytes = "1.9.0" -pyo3 = { version = "0.27", features = ["extension-module"] } +pyo3 = { version = "0.27", features = ["extension-module", "auto-initialize"] } pyo3-async-runtimes = { version = "0.27", features = ["attributes", "async-std-runtime", "tokio-runtime"] } openssl = { version = "*", features = ["vendored"] } urlencoding = "2.1.3" diff --git a/impit-python/src/async_client.rs b/impit-python/src/async_client.rs index fcd6043e..6e687719 100644 --- a/impit-python/src/async_client.rs +++ b/impit-python/src/async_client.rs @@ -437,9 +437,9 @@ impl AsyncClient { match response { Ok(response) => { - let py_response = - ImpitPyResponse::from_async(response, default_encoding, stream_value).await; - Ok(py_response) + ImpitPyResponse::from_async(response, default_encoding, stream_value) + .await + .map_err(|e| ImpitPyError(e).into()) } Err(err) => Err(ImpitPyError(err).into()), } diff --git a/impit-python/src/client.rs b/impit-python/src/client.rs index 79bd1bd0..bfb099f6 100644 --- a/impit-python/src/client.rs +++ b/impit-python/src/client.rs @@ -425,15 +425,13 @@ impl Client { }; match response { - Ok(response) => { - let py_response = ImpitPyResponse::from_async( - response, - self.default_encoding.clone(), - stream.unwrap_or(false), - ) - .await; - Ok(py_response) - } + Ok(response) => ImpitPyResponse::from_async( + response, + self.default_encoding.clone(), + stream.unwrap_or(false), + ) + .await + .map_err(ImpitPyError), Err(err) => Err(ImpitPyError(err)), } }) diff --git a/impit-python/src/response.rs b/impit-python/src/response.rs index 41807201..5cfc8b07 100644 --- a/impit-python/src/response.rs +++ b/impit-python/src/response.rs @@ -5,7 +5,7 @@ use tokio::sync::Mutex as AsyncMutex; use bytes::Bytes; use encoding::label::encoding_from_whatwg_label; use futures::{Stream, StreamExt}; -use impit::utils::ContentType; +use impit::{errors::ImpitError, utils::ContentType}; use pyo3::prelude::*; use reqwest::{Response, StatusCode, Version}; use std::pin::Pin; @@ -521,7 +521,7 @@ impl ImpitPyResponse { val: Response, preferred_encoding: Option, stream: bool, - ) -> Self { + ) -> Result { let status_code = val.status().as_u16(); let url = val.url().to_string(); let reason_phrase = val @@ -551,7 +551,11 @@ impl ImpitPyResponse { .and_then(|ct| ct.into()); let (content, inner_state, encoding, inner, is_closed, is_stream_consumed) = if !stream { - let content = val.bytes().await.map(|b| b.to_vec()).unwrap_or_default(); + let content = val + .bytes() + .await + .map(|b| b.to_vec()) + .map_err(|e| ImpitError::from(e, None))?; let encoding = preferred_encoding .and_then(|e| encoding_from_whatwg_label(&e)) .or(content_type_charset) @@ -581,7 +585,7 @@ impl ImpitPyResponse { ) }; - ImpitPyResponse { + Ok(ImpitPyResponse { status_code, url, reason_phrase, @@ -595,6 +599,6 @@ impl ImpitPyResponse { is_stream_consumed, inner_state, inner, - } + }) } } diff --git a/impit-python/uv.lock b/impit-python/uv.lock index f3c88b49..70e2866c 100644 --- a/impit-python/uv.lock +++ b/impit-python/uv.lock @@ -311,7 +311,7 @@ wheels = [ [[package]] name = "impit" -version = "0.8.0" +version = "0.9.0" source = { editable = "." } [package.dev-dependencies] diff --git a/impit/src/errors.rs b/impit/src/errors.rs index 45f273cd..d0d8ce64 100644 --- a/impit/src/errors.rs +++ b/impit/src/errors.rs @@ -2,12 +2,13 @@ use std::{error::Error, time::Duration}; use thiserror::Error; +#[derive(Default)] pub struct ErrorContext { - pub timeout: Duration, - pub max_redirects: usize, - pub method: String, - pub protocol: String, - pub url: String, + pub timeout: Option, + pub max_redirects: Option, + pub method: Option, + pub protocol: Option, + pub url: Option, } /// Error types that can be returned by the [`Impit`] struct. @@ -22,8 +23,8 @@ pub enum ImpitError { RequestError, #[error("Transport error occurred.")] TransportError, - #[error("Request timeout ({0} ms) exceeded.")] - TimeoutException(u128), + #[error("Request timeout ({}) exceeded.", .0.map_or("unknown".to_string(), |ms| format!("{ms} ms"))) ] + TimeoutException(Option), #[error("Connection timed out.")] ConnectTimeout, #[error("Read operation timed out.")] @@ -54,8 +55,8 @@ pub enum ImpitError { UnsupportedProtocol, #[error("The response body couldn't be decoded.")] DecodingError, - #[error("Too many redirects occurred. Maximum allowed: {0}")] - TooManyRedirects(usize), + #[error("Too many redirects occurred. Maximum allowed: {}.", .0.map_or("unknown".to_string(), |n| n.to_string())) ] + TooManyRedirects(Option), #[error("HTTP status error occurred with status code {0}.")] HTTPStatusError(u16), #[error("The URL is invalid.")] @@ -99,9 +100,13 @@ impl From for ImpitError { } impl ImpitError { - pub fn from(error: reqwest::Error, context: ErrorContext) -> Self { + pub fn from(error: reqwest::Error, context: Option) -> Self { + let context = context.unwrap_or_default(); if error.is_timeout() { - return ImpitError::TimeoutException(context.timeout.as_millis()); + if (format!("{:?}", error).to_lowercase()).contains("connection timed out") { + return ImpitError::ConnectTimeout; + } + return ImpitError::TimeoutException(context.timeout.map(|x| x.as_millis())); } if error.is_redirect() { diff --git a/impit/src/impit.rs b/impit/src/impit.rs index 029bb59e..da751088 100644 --- a/impit/src/impit.rs +++ b/impit/src/impit.rs @@ -435,13 +435,13 @@ impl Impit { return Err(ImpitError::from( err, - ErrorContext { - timeout: timeout.unwrap_or(self.config.request_timeout), - max_redirects, - method: method.to_string(), - protocol: request.url.scheme().to_string(), - url: url.clone(), - }, + Some(ErrorContext { + timeout: Some(timeout.unwrap_or(self.config.request_timeout)), + max_redirects: Some(max_redirects), + method: Some(method.to_string()), + protocol: Some(request.url.scheme().to_string()), + url: Some(url.clone()), + }), )); } };