Skip to content

Commit

Permalink
feat(timeouts): allow changing http timeouts
Browse files Browse the repository at this point in the history
  • Loading branch information
awlayton committed Apr 26, 2024
1 parent a2df56e commit dff86e7
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 18 deletions.
7 changes: 5 additions & 2 deletions lib/auto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import resolveALPN from 'resolve-alpn';

import { HttpClient } from './http.js';
import { WebSocketClient } from './websocket.js';
import { HTTPTimeouts } from './http.js';

const error = debug('@oada/client:auto:error');

Expand Down Expand Up @@ -70,11 +71,13 @@ export async function autoConnection({
token,
concurrency,
userAgent,
timeouts,
}: {
domain: string;
token: string;
concurrency: number;
userAgent: string;
timeouts: HTTPTimeouts;
}) {
try {
const { hostname, port, protocols } = parseDomain(domain);
Expand All @@ -89,7 +92,7 @@ export async function autoConnection({
switch (alpnProtocol) {
// Prefer HTTP/2
case 'h2': {
return new HttpClient(domain, token, { concurrency, userAgent });
return new HttpClient(domain, token, { concurrency, userAgent, timeouts });
}

// If no HTTP/2, use a WebSocket
Expand All @@ -105,6 +108,6 @@ export async function autoConnection({
} catch (cError: unknown) {
// Fallback to HTTP on error
error(cError, 'Failed to auto pick connection type, falling back to HTTP');
return new HttpClient(domain, token, { concurrency, userAgent });
return new HttpClient(domain, token, { concurrency, userAgent, timeouts });
}
}
17 changes: 10 additions & 7 deletions lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
toStringPath,
} from './utils.js';
import { AbortController } from '#fetch';
import { HttpClient } from './http.js';
import { HttpClient, type HTTPTimeouts } from './http.js';
import { WebSocketClient } from './websocket.js';

import type { Change, Json, JsonObject } from './index.js';
Expand Down Expand Up @@ -66,9 +66,9 @@ export type ConnectionRequest = {
headers: Record<string, string>;
data?: Body;
} & (
| { watch?: false }
| { watch: true; method: 'head' | 'get' | 'put' | 'post' | 'delete' }
);
| { watch?: false }
| { watch: true; method: 'head' | 'get' | 'put' | 'post' | 'delete' }
);

export interface ConnectionResponse {
requestId: string | string[];
Expand Down Expand Up @@ -106,6 +106,7 @@ export interface Config {
/** @default 'auto' */
connection?: 'auto' | 'ws' | 'http' | Connection;
userAgent?: string;
timeouts?: number | Partial<HTTPTimeouts>
}

export type Response = ConnectionResponse;
Expand Down Expand Up @@ -271,7 +272,8 @@ export class OADAClient {
concurrency = 1,
userAgent = `${process.env.npm_package_name}/${process.env.npm_package_version}`,
connection = 'http',
}: Config) {
timeouts = {},
}: Config & { timeouts?: HTTPTimeouts }) {
// Help for those who can't remember if https should be there
this.#domain = domain;
this.#token = token;
Expand All @@ -294,6 +296,7 @@ export class OADAClient {
this.#connection = new HttpClient(this.#domain, this.#token, {
concurrency: this.#concurrency,
userAgent,
timeouts,
});
break;
}
Expand Down Expand Up @@ -434,8 +437,8 @@ export class OADAClient {
});
const rev =
typeof data === 'object' &&
!(data instanceof Uint8Array) &&
!Array.isArray(data)
!(data instanceof Uint8Array) &&
!Array.isArray(data)
? Number(data?._rev)
: undefined;

Expand Down
14 changes: 13 additions & 1 deletion lib/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ const enum ConnectionStatus {
Connected,
}

export interface HTTPTimeouts {
/** @default 60e3 */
connect?: number
body?: number;
headers?: number;
keepAlive?: number;
}

function isJson(contentType: string) {
const media = fromString(contentType);
return [media.subtype, media.suffix].includes('json');
Expand Down Expand Up @@ -75,7 +83,7 @@ export class HttpClient extends EventEmitter implements Connection {
constructor(
domain: string,
token: string,
{ concurrency = 10, userAgent }: { concurrency: number; userAgent: string },
{ concurrency = 10, userAgent, timeouts }: { concurrency: number; userAgent: string; timeouts: HTTPTimeouts },
) {
super();

Expand All @@ -94,7 +102,11 @@ export class HttpClient extends EventEmitter implements Connection {
this.#agent =
Agent &&
new Agent({
keepAliveTimeout: timeouts.keepAlive,
bodyTimeout: timeouts.body,
headersTimeout: timeouts.headers,
connect: {
timeout: timeouts.connect,
rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0',
},
});
Expand Down
21 changes: 14 additions & 7 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,18 @@ export async function connect({
connection: proto = 'auto',
concurrency = 1,
userAgent = `${process.env.npm_package_name}/${process.env.npm_package_version}`,
timeouts: t = {},
...config
}: Config & { token: string }): Promise<OADAClient> {
const timeouts = typeof t === 'number' ? {
connect: t,
keepAlive: t,
headers: t,
body: t,
} : t;
const connection =
proto === 'auto'
? await autoConnection({ concurrency, userAgent, ...config })
? await autoConnection({ concurrency, userAgent, timeouts, ...config })
: proto;
// Create an instance of client and start connection
const client = new OADAClient({
Expand Down Expand Up @@ -78,12 +85,12 @@ export type Json = JsonPrimitive | JsonObject | JsonArray;

export type JsonCompatible<T> = {
[P in keyof T]: T[P] extends Json
? T[P]
: Pick<T, P> extends Required<Pick<T, P>>
? never
: T[P] extends (() => unknown) | undefined
? never
: JsonCompatible<T[P]>;
? T[P]
: Pick<T, P> extends Required<Pick<T, P>>
? never
: T[P] extends (() => unknown) | undefined
? never
: JsonCompatible<T[P]>;
};

declare global {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oada/client",
"version": "5.0.1",
"version": "5.1.0",
"description": "A lightweight client tool to interact with an OADA-compliant server",
"repository": "https://github.com/OADA/client",
"main": "./dist/index.js",
Expand Down

0 comments on commit dff86e7

Please sign in to comment.