diff options
Diffstat (limited to 'packages/taler-util/src/http-impl.qtart.ts')
-rw-r--r-- | packages/taler-util/src/http-impl.qtart.ts | 80 |
1 files changed, 76 insertions, 4 deletions
diff --git a/packages/taler-util/src/http-impl.qtart.ts b/packages/taler-util/src/http-impl.qtart.ts index a37029d6e..0be9f2c23 100644 --- a/packages/taler-util/src/http-impl.qtart.ts +++ b/packages/taler-util/src/http-impl.qtart.ts @@ -19,9 +19,9 @@ /** * Imports. */ -import { Logger } from "@gnu-taler/taler-util"; +import { Logger, openPromise } from "@gnu-taler/taler-util"; import { TalerError } from "./errors.js"; -import { encodeBody, getDefaultHeaders, HttpLibArgs } from "./http-common.js"; +import { HttpLibArgs, encodeBody, getDefaultHeaders } from "./http-common.js"; import { Headers, HttpRequestLibrary, @@ -29,12 +29,26 @@ import { HttpResponse, } from "./http.js"; import { RequestThrottler, TalerErrorCode, URL } from "./index.js"; -import { qjsOs } from "./qtart.js"; +import { QjsHttpResp, qjsOs } from "./qtart.js"; const logger = new Logger("http-impl.qtart.ts"); const textDecoder = new TextDecoder(); +export class RequestTimeoutError extends Error { + public constructor() { + super("Request timed out"); + Object.setPrototypeOf(this, RequestTimeoutError.prototype); + } +} + +export class RequestCancelledError extends Error { + public constructor() { + super("Request cancelled"); + Object.setPrototypeOf(this, RequestCancelledError.prototype); + } +} + /** * Implementation of the HTTP request library interface for node. */ @@ -92,12 +106,70 @@ export class HttpLibImpl implements HttpRequestLibrary { if (method === "POST") { data = encodeBody(opt?.body); } - const res = await qjsOs.fetchHttp(url, { + + const cancelPromCap = openPromise<QjsHttpResp>(); + + // Just like WHATWG fetch(), the qjs http client doesn't + // really support cancellation, so cancellation here just + // means that the result is ignored! + const fetchProm = qjsOs.fetchHttp(url, { method, data, headers: headersList, }); + let timeoutHandle: any = undefined; + let cancelCancelledHandler: (() => void) | undefined = undefined; + + if (opt?.timeout && opt.timeout.d_ms !== "forever") { + timeoutHandle = setTimeout(() => { + cancelPromCap.reject(new RequestTimeoutError()); + }, opt.timeout.d_ms); + } + + if (opt?.cancellationToken) { + cancelCancelledHandler = opt.cancellationToken.onCancelled(() => { + cancelPromCap.reject(new RequestCancelledError()); + }); + } + + let res: QjsHttpResp; + try { + res = await Promise.race([fetchProm, cancelPromCap.promise]); + } catch (e) { + if (e instanceof RequestCancelledError) { + throw TalerError.fromDetail( + TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, + { + requestUrl: url, + requestMethod: method, + httpStatusCode: 0, + }, + `Request cancelled`, + ); + } + if (e instanceof RequestTimeoutError) { + throw TalerError.fromDetail( + TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, + { + requestUrl: url, + requestMethod: method, + httpStatusCode: 0, + }, + `Request timed out`, + ); + } + throw e; + } + + if (timeoutHandle != null) { + clearTimeout(timeoutHandle); + } + + if (cancelCancelledHandler != null) { + cancelCancelledHandler(); + } + const headers: Headers = new Headers(); if (res.headers) { |