@actions/http-client 1.0.10 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts DELETED
@@ -1,768 +0,0 @@
1
- import http = require('http')
2
- import https = require('https')
3
- import ifm = require('./interfaces')
4
- import pm = require('./proxy')
5
-
6
- let tunnel: any
7
-
8
- export enum HttpCodes {
9
- OK = 200,
10
- MultipleChoices = 300,
11
- MovedPermanently = 301,
12
- ResourceMoved = 302,
13
- SeeOther = 303,
14
- NotModified = 304,
15
- UseProxy = 305,
16
- SwitchProxy = 306,
17
- TemporaryRedirect = 307,
18
- PermanentRedirect = 308,
19
- BadRequest = 400,
20
- Unauthorized = 401,
21
- PaymentRequired = 402,
22
- Forbidden = 403,
23
- NotFound = 404,
24
- MethodNotAllowed = 405,
25
- NotAcceptable = 406,
26
- ProxyAuthenticationRequired = 407,
27
- RequestTimeout = 408,
28
- Conflict = 409,
29
- Gone = 410,
30
- TooManyRequests = 429,
31
- InternalServerError = 500,
32
- NotImplemented = 501,
33
- BadGateway = 502,
34
- ServiceUnavailable = 503,
35
- GatewayTimeout = 504
36
- }
37
-
38
- export enum Headers {
39
- Accept = 'accept',
40
- ContentType = 'content-type'
41
- }
42
-
43
- export enum MediaTypes {
44
- ApplicationJson = 'application/json'
45
- }
46
-
47
- /**
48
- * Returns the proxy URL, depending upon the supplied url and proxy environment variables.
49
- * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
50
- */
51
- export function getProxyUrl(serverUrl: string): string {
52
- let proxyUrl = pm.getProxyUrl(new URL(serverUrl))
53
- return proxyUrl ? proxyUrl.href : ''
54
- }
55
-
56
- const HttpRedirectCodes: number[] = [
57
- HttpCodes.MovedPermanently,
58
- HttpCodes.ResourceMoved,
59
- HttpCodes.SeeOther,
60
- HttpCodes.TemporaryRedirect,
61
- HttpCodes.PermanentRedirect
62
- ]
63
- const HttpResponseRetryCodes: number[] = [
64
- HttpCodes.BadGateway,
65
- HttpCodes.ServiceUnavailable,
66
- HttpCodes.GatewayTimeout
67
- ]
68
- const RetryableHttpVerbs: string[] = ['OPTIONS', 'GET', 'DELETE', 'HEAD']
69
- const ExponentialBackoffCeiling = 10
70
- const ExponentialBackoffTimeSlice = 5
71
-
72
- export class HttpClientError extends Error {
73
- constructor(message: string, statusCode: number) {
74
- super(message)
75
- this.name = 'HttpClientError'
76
- this.statusCode = statusCode
77
- Object.setPrototypeOf(this, HttpClientError.prototype)
78
- }
79
-
80
- public statusCode: number
81
- public result?: any
82
- }
83
-
84
- export class HttpClientResponse implements ifm.IHttpClientResponse {
85
- constructor(message: http.IncomingMessage) {
86
- this.message = message
87
- }
88
-
89
- public message: http.IncomingMessage
90
- readBody(): Promise<string> {
91
- return new Promise<string>(async (resolve, reject) => {
92
- let output = Buffer.alloc(0)
93
-
94
- this.message.on('data', (chunk: Buffer) => {
95
- output = Buffer.concat([output, chunk])
96
- })
97
-
98
- this.message.on('end', () => {
99
- resolve(output.toString())
100
- })
101
- })
102
- }
103
- }
104
-
105
- export function isHttps(requestUrl: string) {
106
- let parsedUrl: URL = new URL(requestUrl)
107
- return parsedUrl.protocol === 'https:'
108
- }
109
-
110
- export class HttpClient {
111
- userAgent: string | undefined
112
- handlers: ifm.IRequestHandler[]
113
- requestOptions: ifm.IRequestOptions
114
-
115
- private _ignoreSslError: boolean = false
116
- private _socketTimeout: number
117
- private _allowRedirects: boolean = true
118
- private _allowRedirectDowngrade: boolean = false
119
- private _maxRedirects: number = 50
120
- private _allowRetries: boolean = false
121
- private _maxRetries: number = 1
122
- private _agent
123
- private _proxyAgent
124
- private _keepAlive: boolean = false
125
- private _disposed: boolean = false
126
-
127
- constructor(
128
- userAgent?: string,
129
- handlers?: ifm.IRequestHandler[],
130
- requestOptions?: ifm.IRequestOptions
131
- ) {
132
- this.userAgent = userAgent
133
- this.handlers = handlers || []
134
- this.requestOptions = requestOptions
135
- if (requestOptions) {
136
- if (requestOptions.ignoreSslError != null) {
137
- this._ignoreSslError = requestOptions.ignoreSslError
138
- }
139
-
140
- this._socketTimeout = requestOptions.socketTimeout
141
-
142
- if (requestOptions.allowRedirects != null) {
143
- this._allowRedirects = requestOptions.allowRedirects
144
- }
145
-
146
- if (requestOptions.allowRedirectDowngrade != null) {
147
- this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade
148
- }
149
-
150
- if (requestOptions.maxRedirects != null) {
151
- this._maxRedirects = Math.max(requestOptions.maxRedirects, 0)
152
- }
153
-
154
- if (requestOptions.keepAlive != null) {
155
- this._keepAlive = requestOptions.keepAlive
156
- }
157
-
158
- if (requestOptions.allowRetries != null) {
159
- this._allowRetries = requestOptions.allowRetries
160
- }
161
-
162
- if (requestOptions.maxRetries != null) {
163
- this._maxRetries = requestOptions.maxRetries
164
- }
165
- }
166
- }
167
-
168
- public options(
169
- requestUrl: string,
170
- additionalHeaders?: ifm.IHeaders
171
- ): Promise<ifm.IHttpClientResponse> {
172
- return this.request('OPTIONS', requestUrl, null, additionalHeaders || {})
173
- }
174
-
175
- public get(
176
- requestUrl: string,
177
- additionalHeaders?: ifm.IHeaders
178
- ): Promise<ifm.IHttpClientResponse> {
179
- return this.request('GET', requestUrl, null, additionalHeaders || {})
180
- }
181
-
182
- public del(
183
- requestUrl: string,
184
- additionalHeaders?: ifm.IHeaders
185
- ): Promise<ifm.IHttpClientResponse> {
186
- return this.request('DELETE', requestUrl, null, additionalHeaders || {})
187
- }
188
-
189
- public post(
190
- requestUrl: string,
191
- data: string,
192
- additionalHeaders?: ifm.IHeaders
193
- ): Promise<ifm.IHttpClientResponse> {
194
- return this.request('POST', requestUrl, data, additionalHeaders || {})
195
- }
196
-
197
- public patch(
198
- requestUrl: string,
199
- data: string,
200
- additionalHeaders?: ifm.IHeaders
201
- ): Promise<ifm.IHttpClientResponse> {
202
- return this.request('PATCH', requestUrl, data, additionalHeaders || {})
203
- }
204
-
205
- public put(
206
- requestUrl: string,
207
- data: string,
208
- additionalHeaders?: ifm.IHeaders
209
- ): Promise<ifm.IHttpClientResponse> {
210
- return this.request('PUT', requestUrl, data, additionalHeaders || {})
211
- }
212
-
213
- public head(
214
- requestUrl: string,
215
- additionalHeaders?: ifm.IHeaders
216
- ): Promise<ifm.IHttpClientResponse> {
217
- return this.request('HEAD', requestUrl, null, additionalHeaders || {})
218
- }
219
-
220
- public sendStream(
221
- verb: string,
222
- requestUrl: string,
223
- stream: NodeJS.ReadableStream,
224
- additionalHeaders?: ifm.IHeaders
225
- ): Promise<ifm.IHttpClientResponse> {
226
- return this.request(verb, requestUrl, stream, additionalHeaders)
227
- }
228
-
229
- /**
230
- * Gets a typed object from an endpoint
231
- * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
232
- */
233
- public async getJson<T>(
234
- requestUrl: string,
235
- additionalHeaders: ifm.IHeaders = {}
236
- ): Promise<ifm.ITypedResponse<T>> {
237
- additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
238
- additionalHeaders,
239
- Headers.Accept,
240
- MediaTypes.ApplicationJson
241
- )
242
- let res: ifm.IHttpClientResponse = await this.get(
243
- requestUrl,
244
- additionalHeaders
245
- )
246
- return this._processResponse<T>(res, this.requestOptions)
247
- }
248
-
249
- public async postJson<T>(
250
- requestUrl: string,
251
- obj: any,
252
- additionalHeaders: ifm.IHeaders = {}
253
- ): Promise<ifm.ITypedResponse<T>> {
254
- let data: string = JSON.stringify(obj, null, 2)
255
- additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
256
- additionalHeaders,
257
- Headers.Accept,
258
- MediaTypes.ApplicationJson
259
- )
260
- additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(
261
- additionalHeaders,
262
- Headers.ContentType,
263
- MediaTypes.ApplicationJson
264
- )
265
- let res: ifm.IHttpClientResponse = await this.post(
266
- requestUrl,
267
- data,
268
- additionalHeaders
269
- )
270
- return this._processResponse<T>(res, this.requestOptions)
271
- }
272
-
273
- public async putJson<T>(
274
- requestUrl: string,
275
- obj: any,
276
- additionalHeaders: ifm.IHeaders = {}
277
- ): Promise<ifm.ITypedResponse<T>> {
278
- let data: string = JSON.stringify(obj, null, 2)
279
- additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
280
- additionalHeaders,
281
- Headers.Accept,
282
- MediaTypes.ApplicationJson
283
- )
284
- additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(
285
- additionalHeaders,
286
- Headers.ContentType,
287
- MediaTypes.ApplicationJson
288
- )
289
- let res: ifm.IHttpClientResponse = await this.put(
290
- requestUrl,
291
- data,
292
- additionalHeaders
293
- )
294
- return this._processResponse<T>(res, this.requestOptions)
295
- }
296
-
297
- public async patchJson<T>(
298
- requestUrl: string,
299
- obj: any,
300
- additionalHeaders: ifm.IHeaders = {}
301
- ): Promise<ifm.ITypedResponse<T>> {
302
- let data: string = JSON.stringify(obj, null, 2)
303
- additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
304
- additionalHeaders,
305
- Headers.Accept,
306
- MediaTypes.ApplicationJson
307
- )
308
- additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(
309
- additionalHeaders,
310
- Headers.ContentType,
311
- MediaTypes.ApplicationJson
312
- )
313
- let res: ifm.IHttpClientResponse = await this.patch(
314
- requestUrl,
315
- data,
316
- additionalHeaders
317
- )
318
- return this._processResponse<T>(res, this.requestOptions)
319
- }
320
-
321
- /**
322
- * Makes a raw http request.
323
- * All other methods such as get, post, patch, and request ultimately call this.
324
- * Prefer get, del, post and patch
325
- */
326
- public async request(
327
- verb: string,
328
- requestUrl: string,
329
- data: string | NodeJS.ReadableStream,
330
- headers: ifm.IHeaders
331
- ): Promise<ifm.IHttpClientResponse> {
332
- if (this._disposed) {
333
- throw new Error('Client has already been disposed.')
334
- }
335
-
336
- let parsedUrl = new URL(requestUrl)
337
- let info: ifm.IRequestInfo = this._prepareRequest(verb, parsedUrl, headers)
338
-
339
- // Only perform retries on reads since writes may not be idempotent.
340
- let maxTries: number =
341
- this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
342
- ? this._maxRetries + 1
343
- : 1
344
- let numTries: number = 0
345
-
346
- let response: HttpClientResponse
347
- while (numTries < maxTries) {
348
- response = await this.requestRaw(info, data)
349
-
350
- // Check if it's an authentication challenge
351
- if (
352
- response &&
353
- response.message &&
354
- response.message.statusCode === HttpCodes.Unauthorized
355
- ) {
356
- let authenticationHandler: ifm.IRequestHandler
357
-
358
- for (let i = 0; i < this.handlers.length; i++) {
359
- if (this.handlers[i].canHandleAuthentication(response)) {
360
- authenticationHandler = this.handlers[i]
361
- break
362
- }
363
- }
364
-
365
- if (authenticationHandler) {
366
- return authenticationHandler.handleAuthentication(this, info, data)
367
- } else {
368
- // We have received an unauthorized response but have no handlers to handle it.
369
- // Let the response return to the caller.
370
- return response
371
- }
372
- }
373
-
374
- let redirectsRemaining: number = this._maxRedirects
375
- while (
376
- HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
377
- this._allowRedirects &&
378
- redirectsRemaining > 0
379
- ) {
380
- const redirectUrl: string | null = response.message.headers['location']
381
- if (!redirectUrl) {
382
- // if there's no location to redirect to, we won't
383
- break
384
- }
385
- let parsedRedirectUrl = new URL(redirectUrl)
386
- if (
387
- parsedUrl.protocol == 'https:' &&
388
- parsedUrl.protocol != parsedRedirectUrl.protocol &&
389
- !this._allowRedirectDowngrade
390
- ) {
391
- throw new Error(
392
- 'Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'
393
- )
394
- }
395
-
396
- // we need to finish reading the response before reassigning response
397
- // which will leak the open socket.
398
- await response.readBody()
399
-
400
- // strip authorization header if redirected to a different hostname
401
- if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
402
- for (let header in headers) {
403
- // header names are case insensitive
404
- if (header.toLowerCase() === 'authorization') {
405
- delete headers[header]
406
- }
407
- }
408
- }
409
-
410
- // let's make the request with the new redirectUrl
411
- info = this._prepareRequest(verb, parsedRedirectUrl, headers)
412
- response = await this.requestRaw(info, data)
413
- redirectsRemaining--
414
- }
415
-
416
- if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
417
- // If not a retry code, return immediately instead of retrying
418
- return response
419
- }
420
-
421
- numTries += 1
422
-
423
- if (numTries < maxTries) {
424
- await response.readBody()
425
- await this._performExponentialBackoff(numTries)
426
- }
427
- }
428
-
429
- return response
430
- }
431
-
432
- /**
433
- * Needs to be called if keepAlive is set to true in request options.
434
- */
435
- public dispose() {
436
- if (this._agent) {
437
- this._agent.destroy()
438
- }
439
-
440
- this._disposed = true
441
- }
442
-
443
- /**
444
- * Raw request.
445
- * @param info
446
- * @param data
447
- */
448
- public requestRaw(
449
- info: ifm.IRequestInfo,
450
- data: string | NodeJS.ReadableStream
451
- ): Promise<ifm.IHttpClientResponse> {
452
- return new Promise<ifm.IHttpClientResponse>((resolve, reject) => {
453
- let callbackForResult = function (
454
- err: any,
455
- res: ifm.IHttpClientResponse
456
- ) {
457
- if (err) {
458
- reject(err)
459
- }
460
-
461
- resolve(res)
462
- }
463
-
464
- this.requestRawWithCallback(info, data, callbackForResult)
465
- })
466
- }
467
-
468
- /**
469
- * Raw request with callback.
470
- * @param info
471
- * @param data
472
- * @param onResult
473
- */
474
- public requestRawWithCallback(
475
- info: ifm.IRequestInfo,
476
- data: string | NodeJS.ReadableStream,
477
- onResult: (err: any, res: ifm.IHttpClientResponse) => void
478
- ): void {
479
- let socket
480
-
481
- if (typeof data === 'string') {
482
- info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8')
483
- }
484
-
485
- let callbackCalled: boolean = false
486
- let handleResult = (err: any, res: HttpClientResponse) => {
487
- if (!callbackCalled) {
488
- callbackCalled = true
489
- onResult(err, res)
490
- }
491
- }
492
-
493
- let req: http.ClientRequest = info.httpModule.request(
494
- info.options,
495
- (msg: http.IncomingMessage) => {
496
- let res: HttpClientResponse = new HttpClientResponse(msg)
497
- handleResult(null, res)
498
- }
499
- )
500
-
501
- req.on('socket', sock => {
502
- socket = sock
503
- })
504
-
505
- // If we ever get disconnected, we want the socket to timeout eventually
506
- req.setTimeout(this._socketTimeout || 3 * 60000, () => {
507
- if (socket) {
508
- socket.end()
509
- }
510
- handleResult(new Error('Request timeout: ' + info.options.path), null)
511
- })
512
-
513
- req.on('error', function (err) {
514
- // err has statusCode property
515
- // res should have headers
516
- handleResult(err, null)
517
- })
518
-
519
- if (data && typeof data === 'string') {
520
- req.write(data, 'utf8')
521
- }
522
-
523
- if (data && typeof data !== 'string') {
524
- data.on('close', function () {
525
- req.end()
526
- })
527
-
528
- data.pipe(req)
529
- } else {
530
- req.end()
531
- }
532
- }
533
-
534
- /**
535
- * Gets an http agent. This function is useful when you need an http agent that handles
536
- * routing through a proxy server - depending upon the url and proxy environment variables.
537
- * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
538
- */
539
- public getAgent(serverUrl: string): http.Agent {
540
- let parsedUrl = new URL(serverUrl)
541
- return this._getAgent(parsedUrl)
542
- }
543
-
544
- private _prepareRequest(
545
- method: string,
546
- requestUrl: URL,
547
- headers: ifm.IHeaders
548
- ): ifm.IRequestInfo {
549
- const info: ifm.IRequestInfo = <ifm.IRequestInfo>{}
550
-
551
- info.parsedUrl = requestUrl
552
- const usingSsl: boolean = info.parsedUrl.protocol === 'https:'
553
- info.httpModule = usingSsl ? https : http
554
- const defaultPort: number = usingSsl ? 443 : 80
555
-
556
- info.options = <http.RequestOptions>{}
557
- info.options.host = info.parsedUrl.hostname
558
- info.options.port = info.parsedUrl.port
559
- ? parseInt(info.parsedUrl.port)
560
- : defaultPort
561
- info.options.path =
562
- (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '')
563
- info.options.method = method
564
- info.options.headers = this._mergeHeaders(headers)
565
- if (this.userAgent != null) {
566
- info.options.headers['user-agent'] = this.userAgent
567
- }
568
-
569
- info.options.agent = this._getAgent(info.parsedUrl)
570
-
571
- // gives handlers an opportunity to participate
572
- if (this.handlers) {
573
- this.handlers.forEach(handler => {
574
- handler.prepareRequest(info.options)
575
- })
576
- }
577
-
578
- return info
579
- }
580
-
581
- private _mergeHeaders(headers: ifm.IHeaders): ifm.IHeaders {
582
- const lowercaseKeys = obj =>
583
- Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
584
-
585
- if (this.requestOptions && this.requestOptions.headers) {
586
- return Object.assign(
587
- {},
588
- lowercaseKeys(this.requestOptions.headers),
589
- lowercaseKeys(headers)
590
- )
591
- }
592
-
593
- return lowercaseKeys(headers || {})
594
- }
595
-
596
- private _getExistingOrDefaultHeader(
597
- additionalHeaders: ifm.IHeaders,
598
- header: string,
599
- _default: string
600
- ) {
601
- const lowercaseKeys = obj =>
602
- Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
603
-
604
- let clientHeader: string
605
- if (this.requestOptions && this.requestOptions.headers) {
606
- clientHeader = lowercaseKeys(this.requestOptions.headers)[header]
607
- }
608
- return additionalHeaders[header] || clientHeader || _default
609
- }
610
-
611
- private _getAgent(parsedUrl: URL): http.Agent {
612
- let agent
613
- let proxyUrl: URL = pm.getProxyUrl(parsedUrl)
614
- let useProxy = proxyUrl && proxyUrl.hostname
615
-
616
- if (this._keepAlive && useProxy) {
617
- agent = this._proxyAgent
618
- }
619
-
620
- if (this._keepAlive && !useProxy) {
621
- agent = this._agent
622
- }
623
-
624
- // if agent is already assigned use that agent.
625
- if (!!agent) {
626
- return agent
627
- }
628
-
629
- const usingSsl = parsedUrl.protocol === 'https:'
630
- let maxSockets = 100
631
- if (!!this.requestOptions) {
632
- maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets
633
- }
634
-
635
- if (useProxy) {
636
- // If using proxy, need tunnel
637
- if (!tunnel) {
638
- tunnel = require('tunnel')
639
- }
640
-
641
- const agentOptions = {
642
- maxSockets: maxSockets,
643
- keepAlive: this._keepAlive,
644
- proxy: {
645
- ...((proxyUrl.username || proxyUrl.password) && {
646
- proxyAuth: `${proxyUrl.username}:${proxyUrl.password}`
647
- }),
648
- host: proxyUrl.hostname,
649
- port: proxyUrl.port
650
- }
651
- }
652
-
653
- let tunnelAgent: Function
654
- const overHttps = proxyUrl.protocol === 'https:'
655
- if (usingSsl) {
656
- tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp
657
- } else {
658
- tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp
659
- }
660
-
661
- agent = tunnelAgent(agentOptions)
662
- this._proxyAgent = agent
663
- }
664
-
665
- // if reusing agent across request and tunneling agent isn't assigned create a new agent
666
- if (this._keepAlive && !agent) {
667
- const options = {keepAlive: this._keepAlive, maxSockets: maxSockets}
668
- agent = usingSsl ? new https.Agent(options) : new http.Agent(options)
669
- this._agent = agent
670
- }
671
-
672
- // if not using private agent and tunnel agent isn't setup then use global agent
673
- if (!agent) {
674
- agent = usingSsl ? https.globalAgent : http.globalAgent
675
- }
676
-
677
- if (usingSsl && this._ignoreSslError) {
678
- // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
679
- // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
680
- // we have to cast it to any and change it directly
681
- agent.options = Object.assign(agent.options || {}, {
682
- rejectUnauthorized: false
683
- })
684
- }
685
-
686
- return agent
687
- }
688
-
689
- private _performExponentialBackoff(retryNumber: number): Promise<void> {
690
- retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
691
- const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)
692
- return new Promise(resolve => setTimeout(() => resolve(), ms))
693
- }
694
-
695
- private static dateTimeDeserializer(key: any, value: any): any {
696
- if (typeof value === 'string') {
697
- let a = new Date(value)
698
- if (!isNaN(a.valueOf())) {
699
- return a
700
- }
701
- }
702
-
703
- return value
704
- }
705
-
706
- private async _processResponse<T>(
707
- res: ifm.IHttpClientResponse,
708
- options: ifm.IRequestOptions
709
- ): Promise<ifm.ITypedResponse<T>> {
710
- return new Promise<ifm.ITypedResponse<T>>(async (resolve, reject) => {
711
- const statusCode: number = res.message.statusCode
712
-
713
- const response: ifm.ITypedResponse<T> = {
714
- statusCode: statusCode,
715
- result: null,
716
- headers: {}
717
- }
718
-
719
- // not found leads to null obj returned
720
- if (statusCode == HttpCodes.NotFound) {
721
- resolve(response)
722
- }
723
-
724
- let obj: any
725
- let contents: string
726
-
727
- // get the result from the body
728
- try {
729
- contents = await res.readBody()
730
- if (contents && contents.length > 0) {
731
- if (options && options.deserializeDates) {
732
- obj = JSON.parse(contents, HttpClient.dateTimeDeserializer)
733
- } else {
734
- obj = JSON.parse(contents)
735
- }
736
-
737
- response.result = obj
738
- }
739
-
740
- response.headers = res.message.headers
741
- } catch (err) {
742
- // Invalid resource (contents not json); leaving result obj null
743
- }
744
-
745
- // note that 3xx redirects are handled by the http layer.
746
- if (statusCode > 299) {
747
- let msg: string
748
-
749
- // if exception/error in body, attempt to get better error
750
- if (obj && obj.message) {
751
- msg = obj.message
752
- } else if (contents && contents.length > 0) {
753
- // it may be the case that the exception is in the body message as string
754
- msg = contents
755
- } else {
756
- msg = 'Failed request: (' + statusCode + ')'
757
- }
758
-
759
- let err = new HttpClientError(msg, statusCode)
760
- err.result = response.result
761
-
762
- reject(err)
763
- } else {
764
- resolve(response)
765
- }
766
- })
767
- }
768
- }