forked from algolia/algoliasearch-client-javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcreateHttpRequester.ts
More file actions
118 lines (99 loc) · 3.67 KB
/
createHttpRequester.ts
File metadata and controls
118 lines (99 loc) · 3.67 KB
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import http from 'http';
import https from 'https';
import { URL } from 'url';
import zlib from 'zlib';
import type { EndRequest, Requester, Response } from '@algolia/client-common';
export type CreateHttpRequesterOptions = Partial<{
agent: http.Agent | https.Agent;
httpAgent: http.Agent;
httpsAgent: https.Agent;
/**
* RequestOptions to be merged with the end request, it will override default options if provided.
*/
requesterOptions: https.RequestOptions;
}>;
// Global agents allow us to reuse the TCP protocol with multiple clients
const agentOptions = { keepAlive: true };
const defaultHttpAgent = new http.Agent(agentOptions);
const defaultHttpsAgent = new https.Agent(agentOptions);
export function createHttpRequester({
agent: userGlobalAgent,
httpAgent: userHttpAgent,
httpsAgent: userHttpsAgent,
requesterOptions = {},
}: CreateHttpRequesterOptions = {}): Requester {
const httpAgent = userHttpAgent || userGlobalAgent || defaultHttpAgent;
const httpsAgent = userHttpsAgent || userGlobalAgent || defaultHttpsAgent;
function send(request: EndRequest): Promise<Response> {
return new Promise((resolve) => {
let responseTimeout: NodeJS.Timeout | undefined;
let connectTimeout: NodeJS.Timeout | undefined;
const url = new URL(request.url);
const path = url.search === null ? url.pathname : `${url.pathname}${url.search}`;
const options: https.RequestOptions = {
agent: url.protocol === 'https:' ? httpsAgent : httpAgent,
hostname: url.hostname,
path,
method: request.method,
...requesterOptions,
headers: {
'accept-encoding': 'gzip',
...request.headers,
...requesterOptions.headers,
},
};
if (url.port && !requesterOptions.port) {
options.port = url.port;
}
const req = (url.protocol === 'https:' ? https : http).request(options, (response) => {
let contentBuffers: Buffer[] = [];
response.on('data', (chunk) => {
contentBuffers = contentBuffers.concat(chunk);
});
response.on('end', () => {
clearTimeout(connectTimeout as NodeJS.Timeout);
clearTimeout(responseTimeout as NodeJS.Timeout);
let buffer = Buffer.concat(contentBuffers);
if (response.headers['content-encoding'] === 'gzip') {
buffer = zlib.gunzipSync(buffer);
}
resolve({
status: response.statusCode || 0,
content: buffer.toString(),
isTimedOut: false,
});
});
response.on('error', (error) => {
clearTimeout(connectTimeout as NodeJS.Timeout);
clearTimeout(responseTimeout as NodeJS.Timeout);
resolve({ status: 0, content: error.message, isTimedOut: false });
});
});
const createTimeout = (timeout: number, content: string): NodeJS.Timeout => {
return setTimeout(() => {
req.destroy();
resolve({
status: 0,
content,
isTimedOut: true,
});
}, timeout);
};
connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
req.on('error', (error) => {
clearTimeout(connectTimeout as NodeJS.Timeout);
clearTimeout(responseTimeout!);
resolve({ status: 0, content: error.message, isTimedOut: false });
});
req.once('response', () => {
clearTimeout(connectTimeout as NodeJS.Timeout);
responseTimeout = createTimeout(request.responseTimeout, 'Socket timeout');
});
if (request.data !== undefined) {
req.write(request.data);
}
req.end();
});
}
return { send };
}