-
Notifications
You must be signed in to change notification settings - Fork 3
/
http.requestHandler.ts
103 lines (91 loc) · 3.09 KB
/
http.requestHandler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* eslint-disable @typescript-eslint/no-var-requires */
import { HttpRequestHandler, NormalizedHttpOptions, RawHttpResponse } from '../httpClient.types';
import { encodeText, getEncodingFromHeaders } from '../helpers/encoder.helper';
import { ClientRequestArgs, IncomingMessage } from 'http';
import { HttpResponseHeaders } from '../httpResponseHeaders';
import { HttpStatusText } from '../httpCodes';
import { HttpRequestError } from '../httpRequestError';
export interface HttpRequestHandlerOptions {
defaultRequestOptions?: ClientRequestArgs;
}
/**
* Creates a new {@link HttpRequestHandler} that uses native Node.js [HTTP]{@link https://nodejs.org/api/http.html} & [HTTPS]{@link https://nodejs.org/api/https.html} modules underneath.
* Does not support *mode* property.
* @param requestHandlerOptions - default native options attached to all requests
*/
export const httpRequestHandler = (
requestHandlerOptions: HttpRequestHandlerOptions = {},
): HttpRequestHandler => {
const url = require('url');
const http = require('http');
const https = require('https');
return (requestOptions: NormalizedHttpOptions): Promise<RawHttpResponse> => new Promise((resolve, reject) => {
const {
url: urlString,
method,
headers,
body,
timeout,
} = requestOptions;
const {
protocol,
auth,
hostname,
port,
path,
} = url.parse(urlString);
const requestModule = protocol === 'https:' ? https : http;
const request = requestModule.request({
...requestHandlerOptions.defaultRequestOptions,
path,
method,
port,
hostname,
protocol,
auth,
timeout,
}, (response: IncomingMessage) => {
const status = response.statusCode || -1;
const statusText = response.statusMessage || HttpStatusText[status] || '';
const ok = status > 0 && status < 400;
const headers = new HttpResponseHeaders(response.headers);
const bodyPromise = new Promise<ArrayBuffer>((resolve, reject) => {
const chunks: any[] = [];
response.on('data', data => chunks.push(data));
response.on('error', error => reject(error));
response.on('end', () => {
const body = Buffer.concat(chunks);
resolve(body);
});
});
resolve({
arrayBuffer: () => bodyPromise,
text: () => bodyPromise.then(buffer => encodeText(buffer, getEncodingFromHeaders(headers))),
ok,
headers,
redirected: false,
status,
statusText,
url: response.url || urlString,
abort: () => request.abort(),
});
}).once('error', (error: Error) => {
reject(error);
}).once('timeout', () => {
reject(new HttpRequestError(requestOptions, `Request timed out after ${timeout}ms.`));
});
for (const key in headers) {
if (headers.hasOwnProperty(key)) {
request.setHeader(key, headers[key]);
}
}
if (body) {
if (typeof body === 'string') {
request.write(body);
} else {
request.write(Buffer.from(body));
}
}
request.end();
});
};