From c664fc9a71f3f84271b4e94f00f5485aa4a695c1 Mon Sep 17 00:00:00 2001 From: oliver-s-lee Date: Wed, 20 Nov 2024 15:14:19 +0000 Subject: [PATCH 1/2] Added support for AbortSignal for HttpClient --- lib/HttpClient.ts | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/HttpClient.ts b/lib/HttpClient.ts index ffb71a8..694cd51 100644 --- a/lib/HttpClient.ts +++ b/lib/HttpClient.ts @@ -202,36 +202,36 @@ export class HttpClient implements ifm.IHttpClient { } } - public options(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); + public options(requestUrl: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}, signal); } - public get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('GET', requestUrl, null, additionalHeaders || {}); + public get(requestUrl: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('GET', requestUrl, null, additionalHeaders || {}, signal); } - public del(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('DELETE', requestUrl, null, additionalHeaders || {}); + public del(requestUrl: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('DELETE', requestUrl, null, additionalHeaders || {}, signal); } - public post(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('POST', requestUrl, data, additionalHeaders || {}); + public post(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('POST', requestUrl, data, additionalHeaders || {}, signal); } - public patch(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('PATCH', requestUrl, data, additionalHeaders || {}); + public patch(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('PATCH', requestUrl, data, additionalHeaders || {}, signal); } - public put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('PUT', requestUrl, data, additionalHeaders || {}); + public put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('PUT', requestUrl, data, additionalHeaders || {}, signal); } - public head(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise { - return this.request('HEAD', requestUrl, null, additionalHeaders || {}); + public head(requestUrl: string, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request('HEAD', requestUrl, null, additionalHeaders || {}, signal); } - public sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders): Promise { - return this.request(verb, requestUrl, stream, additionalHeaders); + public sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders, signal?: AbortSignal): Promise { + return this.request(verb, requestUrl, stream, additionalHeaders, signal); } /** @@ -239,13 +239,13 @@ export class HttpClient implements ifm.IHttpClient { * All other methods such as get, post, patch, and request ultimately call this. * Prefer get, del, post and patch */ - public async request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: ifm.IHeaders): Promise { + public async request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: ifm.IHeaders, signal?: AbortSignal): Promise { if (this._disposed) { throw new Error("Client has already been disposed."); } let parsedUrl = url.parse(requestUrl); - let info: RequestInfo = this._prepareRequest(verb, parsedUrl, headers); + let info: RequestInfo = this._prepareRequest(verb, parsedUrl, headers, signal); // Only perform retries on reads since writes may not be idempotent. let maxTries: number = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1; @@ -305,7 +305,7 @@ export class HttpClient implements ifm.IHttpClient { await response.readBody(); // let's make the request with the new redirectUrl - info = this._prepareRequest(verb, parsedRedirectUrl, headers); + info = this._prepareRequest(verb, parsedRedirectUrl, headers, signal); response = await this.requestRaw(info, data); redirectsRemaining--; } @@ -416,7 +416,7 @@ export class HttpClient implements ifm.IHttpClient { } } - private _prepareRequest(method: string, requestUrl: url.Url, headers: ifm.IHeaders): ifm.IRequestInfo { + private _prepareRequest(method: string, requestUrl: url.Url, headers: ifm.IHeaders, signal?: AbortSignal): ifm.IRequestInfo { const info: ifm.IRequestInfo = {}; info.parsedUrl = requestUrl; @@ -438,6 +438,7 @@ export class HttpClient implements ifm.IHttpClient { } info.options.agent = this._getAgent(info.parsedUrl); + info.options.signal = signal; // gives handlers an opportunity to participate if (this.handlers && !this._isPresigned(url.format(requestUrl))) { From c83dbbb1aa798d5666fdff1dc47457b6f4c31b25 Mon Sep 17 00:00:00 2001 From: oliver-s-lee Date: Wed, 20 Nov 2024 16:06:43 +0000 Subject: [PATCH 2/2] Added support for abort signal for RestClient --- lib/RestClient.ts | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/RestClient.ts b/lib/RestClient.ts index f846d85..882c82c 100644 --- a/lib/RestClient.ts +++ b/lib/RestClient.ts @@ -55,11 +55,12 @@ export class RestClient { * @param {IRequestOptions} requestOptions - (optional) requestOptions object */ public async options(requestUrl: string, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(requestUrl, this._baseUrl); let res: httpm.HttpClientResponse = await this.client.options(url, - this._headersFromOptions(options)); + this._headersFromOptions(options), signal); return this.processResponse(res, options); } @@ -70,11 +71,12 @@ export class RestClient { * @param {IRequestOptions} requestOptions - (optional) requestOptions object */ public async get(resource: string, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(resource, this._baseUrl, (options || {}).queryParameters); let res: httpm.HttpClientResponse = await this.client.get(url, - this._headersFromOptions(options)); + this._headersFromOptions(options), signal); return this.processResponse(res, options); } @@ -85,11 +87,12 @@ export class RestClient { * @param {IRequestOptions} requestOptions - (optional) requestOptions object */ public async del(resource: string, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(resource, this._baseUrl, (options || {}).queryParameters); let res: httpm.HttpClientResponse = await this.client.del(url, - this._headersFromOptions(options)); + this._headersFromOptions(options), signal); return this.processResponse(res, options); } @@ -102,13 +105,14 @@ export class RestClient { */ public async create(resource: string, resources: any, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(resource, this._baseUrl); let headers: ifm.IHeaders = this._headersFromOptions(options, true); let data: string = JSON.stringify(resources, null, 2); - let res: httpm.HttpClientResponse = await this.client.post(url, data, headers); + let res: httpm.HttpClientResponse = await this.client.post(url, data, headers, signal); return this.processResponse(res, options); } @@ -121,13 +125,14 @@ export class RestClient { */ public async update(resource: string, resources: any, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(resource, this._baseUrl); let headers: ifm.IHeaders = this._headersFromOptions(options, true); let data: string = JSON.stringify(resources, null, 2); - let res: httpm.HttpClientResponse = await this.client.patch(url, data, headers); + let res: httpm.HttpClientResponse = await this.client.patch(url, data, headers, signal); return this.processResponse(res, options); } @@ -140,25 +145,27 @@ export class RestClient { */ public async replace(resource: string, resources: any, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(resource, this._baseUrl); let headers: ifm.IHeaders = this._headersFromOptions(options, true); let data: string = JSON.stringify(resources, null, 2); - let res: httpm.HttpClientResponse = await this.client.put(url, data, headers); + let res: httpm.HttpClientResponse = await this.client.put(url, data, headers, signal); return this.processResponse(res, options); } public async uploadStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, - options?: IRequestOptions): Promise> { + options?: IRequestOptions, + signal?: AbortSignal): Promise> { let url: string = util.getUrl(requestUrl, this._baseUrl); let headers: ifm.IHeaders = this._headersFromOptions(options, true); - let res: httpm.HttpClientResponse = await this.client.sendStream(verb, url, stream, headers); + let res: httpm.HttpClientResponse = await this.client.sendStream(verb, url, stream, headers, signal); return this.processResponse(res, options); }