diff --git a/impit-node/index.d.ts b/impit-node/index.d.ts index b4ac8a1b..3aa7935c 100644 --- a/impit-node/index.d.ts +++ b/impit-node/index.d.ts @@ -323,8 +323,8 @@ export interface ImpitOptions { * @default `undefined` (no proxy) */ proxyUrl?: string - /** Default timeout for this Impit instance in milliseconds. */ - timeout?: number + /** Default timeout for this Impit instance in milliseconds, or `null` to disable the timeout. */ + timeout?: number | null /** * Enable HTTP/3 support. * @@ -411,8 +411,8 @@ export interface RequestInit { headers?: Headers | Record | [string, string][] /** Request body. Can be a string, Buffer, ArrayBuffer, TypedArray, DataView, Blob, File, URLSearchParams, FormData or ReadableStream. */ body?: string | ArrayBuffer | Uint8Array | DataView | Blob | File | URLSearchParams | FormData | ReadableStream - /** Request timeout in milliseconds. Overrides the Impit-wide timeout option from {@link ImpitOptions.timeout}. */ - timeout?: number + /** Request timeout in milliseconds, or `null` to disable the timeout. Overrides the Impit-wide timeout option from {@link ImpitOptions.timeout}. */ + timeout?: number | null /** Force the request to use HTTP/3. If the server doesn't expect HTTP/3 or the Impit instance doesn't have HTTP/3 enabled (via the {@link ImpitOptions.http3} option), the request will fail. */ forceHttp3?: boolean /** Abort signal to cancel the request. */ diff --git a/impit-node/src/impit_builder.rs b/impit-node/src/impit_builder.rs index 36a80c5e..edf427dc 100644 --- a/impit-node/src/impit_builder.rs +++ b/impit-node/src/impit_builder.rs @@ -5,7 +5,7 @@ use impit::{ impit::{ImpitBuilder, RedirectBehavior}, }; -use napi::bindgen_prelude::Object; +use napi::bindgen_prelude::{Either, Null, Object}; use napi_derive::napi; use crate::cookies::NodeCookieJar; @@ -71,8 +71,8 @@ pub struct ImpitOptions<'a> { /// /// @default `undefined` (no proxy) pub proxy_url: Option, - /// Default timeout for this Impit instance in milliseconds. - pub timeout: Option, + /// Default timeout for this Impit instance in milliseconds, or `null` to disable the timeout. + pub timeout: Option>, /// Enable HTTP/3 support. /// /// **Warning:** Proxies are not supported when HTTP/3 is enabled. @@ -168,7 +168,10 @@ impl ImpitOptions<'_> { config = config.with_proxy(proxy_url); } if let Some(timeout) = self.timeout { - config = config.with_default_timeout(Duration::from_millis(timeout.into())); + config = config.with_default_timeout(match timeout { + Either::A(millis) => Duration::from_millis(millis.into()), + Either::B(_) => Duration::MAX, + }); } if let Some(http3) = self.http3 { if http3 { diff --git a/impit-node/src/lib.rs b/impit-node/src/lib.rs index 57fa4820..c3d91e5e 100644 --- a/impit-node/src/lib.rs +++ b/impit-node/src/lib.rs @@ -5,7 +5,10 @@ use impit::{ impit::Impit, request::{ImpitBody, RequestOptions}, }; -use napi::{bindgen_prelude::ObjectFinalize, Env}; +use napi::{ + bindgen_prelude::{Either, ObjectFinalize}, + Env, +}; use napi_derive::napi; mod abortable_stream; @@ -132,7 +135,10 @@ impl ImpitWrapper { let request_options = Some(RequestOptions { headers: headers.unwrap_or_default(), - timeout: timeout.map(|timeout| Some(Duration::from_millis(timeout.into()))), + timeout: timeout.map(|timeout| match timeout { + Either::A(millis) => Some(Duration::from_millis(millis.into())), + Either::B(_) => None, + }), http3_prior_knowledge: force_http3.unwrap_or_default(), }); diff --git a/impit-node/src/request.rs b/impit-node/src/request.rs index 05f3d244..4990fa00 100644 --- a/impit-node/src/request.rs +++ b/impit-node/src/request.rs @@ -1,6 +1,6 @@ use bytes::Bytes; use futures_util::{Stream, TryStreamExt}; -use napi::bindgen_prelude::{sys, FromNapiValue, ReadableStream, Reader, Uint8Array}; +use napi::bindgen_prelude::{sys, Either, FromNapiValue, Null, ReadableStream, Reader, Uint8Array}; use napi_derive::napi; @@ -68,8 +68,8 @@ pub struct RequestInit { /// Set by the JS wrapper instead of `body` when the body is a stream. Takes precedence over `body`. #[napi(skip_typescript)] pub body_stream: Option, - /// Request timeout in milliseconds. Overrides the Impit-wide timeout option from {@link ImpitOptions.timeout}. - pub timeout: Option, + /// Request timeout in milliseconds, or `null` to disable the timeout. Overrides the Impit-wide timeout option from {@link ImpitOptions.timeout}. + pub timeout: Option>, /// Force the request to use HTTP/3. If the server doesn't expect HTTP/3 or the Impit instance doesn't have HTTP/3 enabled (via the {@link ImpitOptions.http3} option), the request will fail. pub force_http3: Option, /// Abort signal to cancel the request. diff --git a/impit-node/test/basics.test.ts b/impit-node/test/basics.test.ts index c6acbbe6..60bd0a8f 100644 --- a/impit-node/test/basics.test.ts +++ b/impit-node/test/basics.test.ts @@ -669,6 +669,27 @@ describe.each([ }); }); + describe('timeout', () => { + test('`null` disables the client timeout for a request', async () => { + const server = await getServer(); + const url = `http://127.0.0.1:${(server?.address() as AddressInfo).port}/delay/500`; + const shortTimeoutImpit = new Impit({ browser, timeout: 100 }); + + await expect(shortTimeoutImpit.fetch(url).then((x) => x.text())).rejects.toThrow(); + + const response = await shortTimeoutImpit.fetch(url, { timeout: null }); + await expect(response.text()).resolves.toContain('Body sent after delay.'); + }); + + test('`null` disables the client-wide timeout', async () => { + const server = await getServer(); + const url = `http://127.0.0.1:${(server?.address() as AddressInfo).port}/delay/500`; + + const response = await new Impit({ browser, timeout: null }).fetch(url); + await expect(response.text()).resolves.toContain('Body sent after delay.'); + }); + }); + describe('AbortSignal', () => { test('aborts immediately if signal is already aborted', async () => { const controller = new AbortController();