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
50 changes: 29 additions & 21 deletions impit-node/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::time::Duration;

use impit::{
impit::{ErrorType, Impit, ImpitBuilder},
errors::ImpitError,
impit::{Impit, ImpitBuilder},
request::RequestOptions,
};
use napi_derive::napi;
Expand Down Expand Up @@ -61,36 +62,43 @@ impl ImpitWrapper {
.unwrap_or_default(),
});

let body = request_init.as_ref().and_then(|init| init.body.as_ref());
let body = request_init
.as_ref()
.and_then(|init| init.body.as_ref())
.map(serialize_body);

let body: Option<Vec<u8>> = body.map(serialize_body);
let method = request_init.unwrap_or_default().method.unwrap_or_default();

let response = match request_init.unwrap_or_default().method.unwrap_or_default() {
HttpMethod::Get => self.inner.get(url, request_options).await,
HttpMethod::Post => self.inner.post(url, body, request_options).await,
HttpMethod::Put => self.inner.put(url, body, request_options).await,
HttpMethod::Delete => self.inner.delete(url, request_options).await,
HttpMethod::Patch => self.inner.patch(url, body, request_options).await,
HttpMethod::Head => self.inner.head(url, request_options).await,
HttpMethod::Options => self.inner.options(url, request_options).await,
let response = if matches!(method, HttpMethod::Get | HttpMethod::Head) && body.is_some() {
Err(ImpitError::BindingPassthroughError(
"GET/HEAD methods don't support passing a request body".to_string(),
))
} else {
// Match the HTTP method and execute the corresponding request
match method {
HttpMethod::Get => self.inner.get(url, request_options).await,
HttpMethod::Head => self.inner.head(url, request_options).await,
HttpMethod::Post => self.inner.post(url, body, request_options).await,
HttpMethod::Put => self.inner.put(url, body, request_options).await,
HttpMethod::Delete => self.inner.delete(url, request_options).await,
HttpMethod::Patch => self.inner.patch(url, body, request_options).await,
HttpMethod::Options => self.inner.options(url, request_options).await,
}
};

match response {
Ok(response) => Ok(ImpitResponse::from(response)),
Err(err) => {
let status = match err {
ErrorType::UrlMissingHostnameError => napi::Status::InvalidArg,
ErrorType::UrlProtocolError(_) => napi::Status::InvalidArg,
ErrorType::UrlParsingError => napi::Status::InvalidArg,
ErrorType::InvalidMethod(_) => napi::Status::InvalidArg,
ErrorType::Http3Disabled => napi::Status::GenericFailure,
ErrorType::RequestError(_) => napi::Status::GenericFailure,
ImpitError::UrlMissingHostnameError(_) => napi::Status::InvalidArg,
ImpitError::UrlProtocolError(_) => napi::Status::InvalidArg,
ImpitError::UrlParsingError => napi::Status::InvalidArg,
ImpitError::InvalidMethod(_) => napi::Status::InvalidArg,
ImpitError::Http3Disabled => napi::Status::GenericFailure,
_ => napi::Status::GenericFailure,
};

let reason = match err {
ErrorType::RequestError(r) => format!("{:#?}", r),
e => format!("{}", e),
};
let reason = format!("impit error: {}", err);

Err(napi::Error::new(status, reason))
}
Expand Down
2 changes: 1 addition & 1 deletion impit-node/test/basics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ describe.each([
getHttpBinUrl('/absolute-redirect/2'),
);

await t.expect(response).rejects.toThrowError('TooManyRedirects');
await t.expect(response).rejects.toThrowError('Too many redirects occurred. Maximum allowed');
});
})
});
56 changes: 56 additions & 0 deletions impit-python/python/impit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,35 @@
from .impit import (
AsyncClient,
Client,
CloseError,
ConnectError,
ConnectTimeout,
CookieConflict,
DecodingError,
HTTPError,
HTTPStatusError,
InvalidURL,
LocalProtocolError,
NetworkError,
PoolTimeout,
ProtocolError,
ProxyError,
ReadError,
ReadTimeout,
RemoteProtocolError,
RequestError,
RequestNotRead,
Response,
ResponseNotRead,
StreamClosed,
StreamConsumed,
StreamError,
TimeoutException,
TooManyRedirects,
TransportError,
UnsupportedProtocol,
WriteError,
WriteTimeout,
delete,
get,
head,
Expand All @@ -18,7 +46,35 @@
'AsyncClient',
'Browser',
'Client',
'CloseError',
'ConnectError',
'ConnectTimeout',
'CookieConflict',
'DecodingError',
'HTTPError',
'HTTPStatusError',
'InvalidURL',
'LocalProtocolError',
'NetworkError',
'PoolTimeout',
'ProtocolError',
'ProxyError',
'ReadError',
'ReadTimeout',
'RemoteProtocolError',
'RequestError',
'RequestNotRead',
'Response',
'ResponseNotRead',
'StreamClosed',
'StreamConsumed',
'StreamError',
'TimeoutException',
'TooManyRedirects',
'TransportError',
'UnsupportedProtocol',
'WriteError',
'WriteTimeout',
'delete',
'get',
'head',
Expand Down
56 changes: 56 additions & 0 deletions impit-python/python/impit/impit.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,62 @@ from typing import Literal

Browser = Literal['chrome', 'firefox']

HTTPError: type
"Represents an HTTP-related error."
RequestError: type
"Represents an error during the request process."
TransportError: type
"Represents a transport-layer error."
TimeoutException: type
"Represents a timeout error."
ConnectTimeout: type
"Represents a connection timeout error."
ReadTimeout: type
"Represents a read timeout error."
WriteTimeout: type
"Represents a write timeout error."
PoolTimeout: type
"Represents a connection pool timeout error."
NetworkError: type
"Represents a network-related error."
ConnectError: type
"Represents a connection error."
ReadError: type
"Represents a read error."
WriteError: type
"Represents a write error."
CloseError: type
"Represents an error when closing a connection."
ProtocolError: type
"Represents a protocol-related error."
LocalProtocolError: type
"Represents a local protocol error."
RemoteProtocolError: type
"Represents a remote protocol error."
ProxyError: type
"Represents a proxy-related error."
UnsupportedProtocol: type
"Represents an unsupported protocol error."
DecodingError: type
"Represents an error during response decoding."
TooManyRedirects: type
"Represents an error due to excessive redirects."
HTTPStatusError: type
"Represents an error related to HTTP status codes."
InvalidURL: type
"Represents an error due to an invalid URL."
CookieConflict: type
"Represents a cookie conflict error."
StreamError: type
"Represents a stream-related error."
StreamConsumed: type
"Represents an error when a stream is already consumed."
ResponseNotRead: type
"Represents an error when a response is not read."
RequestNotRead: type
"Represents an error when a request is not read."
StreamClosed: type
"Represents an error when a stream is closed."

class Response:
"""Response object returned by impit requests."""
Expand Down
22 changes: 5 additions & 17 deletions impit-python/src/async_client.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
use std::{collections::HashMap, time::Duration};

use impit::{
emulation::Browser,
impit::{ErrorType, ImpitBuilder},
request::RequestOptions,
};
use pyo3::{
exceptions::{PyRuntimeError, PyTypeError, PyValueError},
prelude::*,
};
use impit::{emulation::Browser, errors::ImpitError, impit::ImpitBuilder, request::RequestOptions};
use pyo3::{exceptions::PyTypeError, prelude::*};
use tokio::sync::oneshot;

use crate::{request::form_to_bytes, response::ImpitPyResponse, RequestBody};
use crate::{errors::ImpitPyError, request::form_to_bytes, response::ImpitPyResponse, RequestBody};

#[pyclass]
pub(crate) struct AsyncClient {
Expand Down Expand Up @@ -302,7 +295,7 @@ impl AsyncClient {
"trace" => impit.trace(url, Some(options)).await,
"head" => impit.head(url, Some(options)).await,
"delete" => impit.delete(url, Some(options)).await,
_ => Err(ErrorType::InvalidMethod(method.to_string())),
_ => Err(ImpitError::InvalidMethod(method.to_string())),
};

tx.send(response).unwrap();
Expand All @@ -315,12 +308,7 @@ impl AsyncClient {

response
.map(|response| ImpitPyResponse::from(response, default_encoding))
.map_err(|err| match err {
ErrorType::RequestError(r) => {
PyErr::new::<PyRuntimeError, _>(format!("{:#?}", r))
}
e => PyErr::new::<PyValueError, _>(e.to_string()),
})
.map_err(|err| ImpitPyError(err).into())
})
}
}
39 changes: 17 additions & 22 deletions impit-python/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ use std::{collections::HashMap, time::Duration};

use impit::{
emulation::Browser,
impit::{ErrorType, Impit, ImpitBuilder},
errors::ImpitError,
impit::{Impit, ImpitBuilder},
request::RequestOptions,
};
use pyo3::{
exceptions::{PyRuntimeError, PyTypeError, PyValueError},
prelude::*,
};
use pyo3::prelude::*;

use crate::{
errors::ImpitPyError,
request::{form_to_bytes, RequestBody},
response::{self, ImpitPyResponse},
};
Expand Down Expand Up @@ -90,7 +89,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("get", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -103,7 +102,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("head", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -116,7 +115,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("post", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -129,7 +128,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("patch", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -142,7 +141,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("put", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -155,7 +154,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("delete", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -168,7 +167,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("options", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -181,7 +180,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<response::ImpitPyResponse, PyErr> {
) -> Result<response::ImpitPyResponse, ImpitPyError> {
self.request("trace", url, content, data, headers, timeout, force_http3)
}

Expand All @@ -195,7 +194,7 @@ impl Client {
headers: Option<HashMap<String, String>>,
timeout: Option<f64>,
force_http3: Option<bool>,
) -> Result<ImpitPyResponse, PyErr> {
) -> Result<ImpitPyResponse, ImpitPyError> {
let mut headers = headers.clone();

if let Some(content) = content {
Expand All @@ -212,9 +211,8 @@ impl Client {
);
Ok(form_to_bytes(form))
}
RequestBody::CatchAll(e) => Err(PyErr::new::<PyTypeError, _>(format!(
"Unsupported data type in request body: {:#?}",
e
RequestBody::CatchAll(e) => Err(ImpitPyError(ImpitError::BindingPassthroughError(
format!("Unsupported data type: {:?}", e).to_string(),
))),
},
None => Ok(Vec::new()),
Expand All @@ -237,13 +235,10 @@ impl Client {
"trace" => self.impit.trace(url, Some(options)).await,
"head" => self.impit.head(url, Some(options)).await,
"delete" => self.impit.delete(url, Some(options)).await,
_ => Err(ErrorType::InvalidMethod(method.to_string())),
_ => Err(ImpitError::InvalidMethod(method.to_string())),
}
})
.map(|response| ImpitPyResponse::from(response, self.default_encoding.clone()))
.map_err(|err| match err {
ErrorType::RequestError(r) => PyErr::new::<PyRuntimeError, _>(format!("{:#?}", r)),
e => PyErr::new::<PyValueError, _>(e.to_string()),
})
.map_err(|err| err.into())
}
}
Loading
Loading