@algolia/requester-node-http 5.8.1 → 5.9.0
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/dist/requester.http.d.cts +77 -12
- package/dist/requester.http.d.ts +77 -12
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +6 -6
- package/index.ts +0 -1
- package/src/__tests__/node-http-requester.test.ts +0 -281
- package/src/createHttpRequester.ts +0 -106
|
@@ -1,16 +1,81 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
2
|
import https from 'https';
|
|
3
|
-
import {
|
|
3
|
+
import { URL } from 'url';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
5
|
+
// src/createHttpRequester.ts
|
|
6
|
+
var agentOptions = { keepAlive: true };
|
|
7
|
+
var defaultHttpAgent = new http.Agent(agentOptions);
|
|
8
|
+
var defaultHttpsAgent = new https.Agent(agentOptions);
|
|
9
|
+
function createHttpRequester({
|
|
10
|
+
agent: userGlobalAgent,
|
|
11
|
+
httpAgent: userHttpAgent,
|
|
12
|
+
httpsAgent: userHttpsAgent,
|
|
13
|
+
requesterOptions = {}
|
|
14
|
+
} = {}) {
|
|
15
|
+
const httpAgent = userHttpAgent || userGlobalAgent || defaultHttpAgent;
|
|
16
|
+
const httpsAgent = userHttpsAgent || userGlobalAgent || defaultHttpsAgent;
|
|
17
|
+
function send(request) {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
let responseTimeout;
|
|
20
|
+
let connectTimeout;
|
|
21
|
+
const url = new URL(request.url);
|
|
22
|
+
const path = url.search === null ? url.pathname : `${url.pathname}${url.search}`;
|
|
23
|
+
const options = {
|
|
24
|
+
agent: url.protocol === "https:" ? httpsAgent : httpAgent,
|
|
25
|
+
hostname: url.hostname,
|
|
26
|
+
path,
|
|
27
|
+
method: request.method,
|
|
28
|
+
...requesterOptions,
|
|
29
|
+
headers: {
|
|
30
|
+
...request.headers,
|
|
31
|
+
...requesterOptions.headers
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
if (url.port && !requesterOptions.port) {
|
|
35
|
+
options.port = url.port;
|
|
36
|
+
}
|
|
37
|
+
const req = (url.protocol === "https:" ? https : http).request(options, (response) => {
|
|
38
|
+
let contentBuffers = [];
|
|
39
|
+
response.on("data", (chunk) => {
|
|
40
|
+
contentBuffers = contentBuffers.concat(chunk);
|
|
41
|
+
});
|
|
42
|
+
response.on("end", () => {
|
|
43
|
+
clearTimeout(connectTimeout);
|
|
44
|
+
clearTimeout(responseTimeout);
|
|
45
|
+
resolve({
|
|
46
|
+
status: response.statusCode || 0,
|
|
47
|
+
content: Buffer.concat(contentBuffers).toString(),
|
|
48
|
+
isTimedOut: false
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
const createTimeout = (timeout, content) => {
|
|
53
|
+
return setTimeout(() => {
|
|
54
|
+
req.destroy();
|
|
55
|
+
resolve({
|
|
56
|
+
status: 0,
|
|
57
|
+
content,
|
|
58
|
+
isTimedOut: true
|
|
59
|
+
});
|
|
60
|
+
}, timeout);
|
|
61
|
+
};
|
|
62
|
+
connectTimeout = createTimeout(request.connectTimeout, "Connection timeout");
|
|
63
|
+
req.on("error", (error) => {
|
|
64
|
+
clearTimeout(connectTimeout);
|
|
65
|
+
clearTimeout(responseTimeout);
|
|
66
|
+
resolve({ status: 0, content: error.message, isTimedOut: false });
|
|
67
|
+
});
|
|
68
|
+
req.once("response", () => {
|
|
69
|
+
clearTimeout(connectTimeout);
|
|
70
|
+
responseTimeout = createTimeout(request.responseTimeout, "Socket timeout");
|
|
71
|
+
});
|
|
72
|
+
if (request.data !== void 0) {
|
|
73
|
+
req.write(request.data);
|
|
74
|
+
}
|
|
75
|
+
req.end();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return { send };
|
|
79
|
+
}
|
|
15
80
|
|
|
16
|
-
export {
|
|
81
|
+
export { createHttpRequester };
|
package/dist/requester.http.d.ts
CHANGED
|
@@ -1,16 +1,81 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
2
|
import https from 'https';
|
|
3
|
-
import {
|
|
3
|
+
import { URL } from 'url';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
5
|
+
// src/createHttpRequester.ts
|
|
6
|
+
var agentOptions = { keepAlive: true };
|
|
7
|
+
var defaultHttpAgent = new http.Agent(agentOptions);
|
|
8
|
+
var defaultHttpsAgent = new https.Agent(agentOptions);
|
|
9
|
+
function createHttpRequester({
|
|
10
|
+
agent: userGlobalAgent,
|
|
11
|
+
httpAgent: userHttpAgent,
|
|
12
|
+
httpsAgent: userHttpsAgent,
|
|
13
|
+
requesterOptions = {}
|
|
14
|
+
} = {}) {
|
|
15
|
+
const httpAgent = userHttpAgent || userGlobalAgent || defaultHttpAgent;
|
|
16
|
+
const httpsAgent = userHttpsAgent || userGlobalAgent || defaultHttpsAgent;
|
|
17
|
+
function send(request) {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
let responseTimeout;
|
|
20
|
+
let connectTimeout;
|
|
21
|
+
const url = new URL(request.url);
|
|
22
|
+
const path = url.search === null ? url.pathname : `${url.pathname}${url.search}`;
|
|
23
|
+
const options = {
|
|
24
|
+
agent: url.protocol === "https:" ? httpsAgent : httpAgent,
|
|
25
|
+
hostname: url.hostname,
|
|
26
|
+
path,
|
|
27
|
+
method: request.method,
|
|
28
|
+
...requesterOptions,
|
|
29
|
+
headers: {
|
|
30
|
+
...request.headers,
|
|
31
|
+
...requesterOptions.headers
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
if (url.port && !requesterOptions.port) {
|
|
35
|
+
options.port = url.port;
|
|
36
|
+
}
|
|
37
|
+
const req = (url.protocol === "https:" ? https : http).request(options, (response) => {
|
|
38
|
+
let contentBuffers = [];
|
|
39
|
+
response.on("data", (chunk) => {
|
|
40
|
+
contentBuffers = contentBuffers.concat(chunk);
|
|
41
|
+
});
|
|
42
|
+
response.on("end", () => {
|
|
43
|
+
clearTimeout(connectTimeout);
|
|
44
|
+
clearTimeout(responseTimeout);
|
|
45
|
+
resolve({
|
|
46
|
+
status: response.statusCode || 0,
|
|
47
|
+
content: Buffer.concat(contentBuffers).toString(),
|
|
48
|
+
isTimedOut: false
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
const createTimeout = (timeout, content) => {
|
|
53
|
+
return setTimeout(() => {
|
|
54
|
+
req.destroy();
|
|
55
|
+
resolve({
|
|
56
|
+
status: 0,
|
|
57
|
+
content,
|
|
58
|
+
isTimedOut: true
|
|
59
|
+
});
|
|
60
|
+
}, timeout);
|
|
61
|
+
};
|
|
62
|
+
connectTimeout = createTimeout(request.connectTimeout, "Connection timeout");
|
|
63
|
+
req.on("error", (error) => {
|
|
64
|
+
clearTimeout(connectTimeout);
|
|
65
|
+
clearTimeout(responseTimeout);
|
|
66
|
+
resolve({ status: 0, content: error.message, isTimedOut: false });
|
|
67
|
+
});
|
|
68
|
+
req.once("response", () => {
|
|
69
|
+
clearTimeout(connectTimeout);
|
|
70
|
+
responseTimeout = createTimeout(request.responseTimeout, "Socket timeout");
|
|
71
|
+
});
|
|
72
|
+
if (request.data !== void 0) {
|
|
73
|
+
req.write(request.data);
|
|
74
|
+
}
|
|
75
|
+
req.end();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return { send };
|
|
79
|
+
}
|
|
15
80
|
|
|
16
|
-
export {
|
|
81
|
+
export { createHttpRequester };
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/requester.http';
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/requester.http.cjs');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@algolia/requester-node-http",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.9.0",
|
|
4
4
|
"description": "Promise-based request library for node using the native http module.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"type": "module",
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
-
"
|
|
15
|
-
"index.
|
|
14
|
+
"index.d.ts",
|
|
15
|
+
"index.js"
|
|
16
16
|
],
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|
|
@@ -34,15 +34,15 @@
|
|
|
34
34
|
"test:bundle": "publint . && attw --pack ."
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@algolia/client-common": "5.
|
|
37
|
+
"@algolia/client-common": "5.9.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@arethetypeswrong/cli": "0.16.4",
|
|
41
|
-
"@types/node": "22.7.
|
|
41
|
+
"@types/node": "22.7.5",
|
|
42
42
|
"nock": "13.5.5",
|
|
43
43
|
"publint": "0.2.11",
|
|
44
44
|
"tsup": "8.3.0",
|
|
45
|
-
"typescript": "5.6.
|
|
45
|
+
"typescript": "5.6.3",
|
|
46
46
|
"vitest": "2.1.2"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
package/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './src/createHttpRequester';
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import http from 'http';
|
|
2
|
-
import https from 'https';
|
|
3
|
-
import nock from 'nock';
|
|
4
|
-
import { Readable } from 'stream';
|
|
5
|
-
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest';
|
|
6
|
-
|
|
7
|
-
import type { EndRequest } from '@algolia/client-common';
|
|
8
|
-
|
|
9
|
-
import { createHttpRequester } from '../..';
|
|
10
|
-
import {
|
|
11
|
-
createTestServer,
|
|
12
|
-
getStringifiedBody,
|
|
13
|
-
headers,
|
|
14
|
-
requestStub,
|
|
15
|
-
testQueryBaseUrl,
|
|
16
|
-
testQueryHeader,
|
|
17
|
-
timeoutRequest,
|
|
18
|
-
} from '../../../../tests/utils';
|
|
19
|
-
|
|
20
|
-
const requester = createHttpRequester();
|
|
21
|
-
|
|
22
|
-
const httpsBaseRequest = https.request;
|
|
23
|
-
const httpBaseRequest = http.request;
|
|
24
|
-
|
|
25
|
-
describe('api', () => {
|
|
26
|
-
const mockedRequestResponse = {
|
|
27
|
-
destroy: vi.fn(),
|
|
28
|
-
on: vi.fn(),
|
|
29
|
-
once: vi.fn(),
|
|
30
|
-
write: vi.fn(),
|
|
31
|
-
end: vi.fn(),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
beforeAll(() => {
|
|
35
|
-
// @ts-expect-error we don't care about the response for those tests
|
|
36
|
-
https.request = vi.fn(() => mockedRequestResponse);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterAll(() => {
|
|
40
|
-
https.request = httpsBaseRequest;
|
|
41
|
-
http.request = httpBaseRequest;
|
|
42
|
-
vi.resetAllMocks();
|
|
43
|
-
vi.clearAllMocks();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('allow init without parameters', () => {
|
|
47
|
-
expect(() => createHttpRequester()).not.toThrow();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('allow providing custom agent', async () => {
|
|
51
|
-
const agent = new http.Agent();
|
|
52
|
-
// @ts-expect-error we don't care about the response for those tests
|
|
53
|
-
http.request = vi.fn(() => mockedRequestResponse);
|
|
54
|
-
const tmpRequester = createHttpRequester({
|
|
55
|
-
agent,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
await tmpRequester.send({
|
|
59
|
-
...requestStub,
|
|
60
|
-
url: 'http://algolia-dns.net/foo?x-algolia-header=bar',
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
expect(http.request).toHaveBeenCalled();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('allow overriding default options', async () => {
|
|
67
|
-
const tmpRequester = createHttpRequester({
|
|
68
|
-
requesterOptions: {
|
|
69
|
-
headers: {
|
|
70
|
-
'my-extra-header': 'algolia',
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await tmpRequester.send(requestStub);
|
|
76
|
-
|
|
77
|
-
expect(https.request).toHaveBeenCalledWith(
|
|
78
|
-
expect.objectContaining({
|
|
79
|
-
headers: expect.objectContaining({
|
|
80
|
-
'my-extra-header': 'algolia',
|
|
81
|
-
}),
|
|
82
|
-
}),
|
|
83
|
-
expect.any(Function),
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('status code handling', () => {
|
|
89
|
-
test('sends requests', async () => {
|
|
90
|
-
const body = getStringifiedBody();
|
|
91
|
-
|
|
92
|
-
nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(200, body);
|
|
93
|
-
|
|
94
|
-
const response = await requester.send(requestStub);
|
|
95
|
-
|
|
96
|
-
expect(response.content).toEqual(body);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('resolves status 200', async () => {
|
|
100
|
-
const body = getStringifiedBody();
|
|
101
|
-
|
|
102
|
-
nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(200, body);
|
|
103
|
-
|
|
104
|
-
const response = await requester.send(requestStub);
|
|
105
|
-
|
|
106
|
-
expect(response.status).toBe(200);
|
|
107
|
-
expect(response.content).toBe(body);
|
|
108
|
-
expect(response.isTimedOut).toBe(false);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('resolves status 300', async () => {
|
|
112
|
-
const reason = 'Multiple Choices';
|
|
113
|
-
|
|
114
|
-
nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(300, reason);
|
|
115
|
-
|
|
116
|
-
const response = await requester.send(requestStub);
|
|
117
|
-
|
|
118
|
-
expect(response.status).toBe(300);
|
|
119
|
-
expect(response.content).toBe(reason);
|
|
120
|
-
expect(response.isTimedOut).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('resolves status 400', async () => {
|
|
124
|
-
const body = getStringifiedBody({
|
|
125
|
-
message: 'Invalid Application-Id or API-Key',
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(400, body);
|
|
129
|
-
|
|
130
|
-
const response = await requester.send(requestStub);
|
|
131
|
-
|
|
132
|
-
expect(response.status).toBe(400);
|
|
133
|
-
expect(response.content).toBe(body);
|
|
134
|
-
expect(response.isTimedOut).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('handles chunked responses inside unicode character boundaries', async () => {
|
|
138
|
-
const data = Buffer.from('äöü');
|
|
139
|
-
|
|
140
|
-
// create a test response stream that is chunked inside a unicode character
|
|
141
|
-
function* generate() {
|
|
142
|
-
yield data.subarray(0, 3);
|
|
143
|
-
yield data.subarray(3);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const testStream = Readable.from(generate());
|
|
147
|
-
|
|
148
|
-
nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(200, testStream);
|
|
149
|
-
|
|
150
|
-
const response = await requester.send(requestStub);
|
|
151
|
-
|
|
152
|
-
expect(response.content).toEqual(data.toString());
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('timeout handling', () => {
|
|
157
|
-
let server: http.Server;
|
|
158
|
-
// setup http server to test timeout
|
|
159
|
-
beforeAll(() => {
|
|
160
|
-
server = createTestServer();
|
|
161
|
-
|
|
162
|
-
server.listen('1112');
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
afterAll(
|
|
166
|
-
() =>
|
|
167
|
-
new Promise((done) => {
|
|
168
|
-
done();
|
|
169
|
-
}),
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
test('timeouts with the given 1 seconds connection timeout', async () => {
|
|
173
|
-
const before = Date.now();
|
|
174
|
-
const response = await requester.send({
|
|
175
|
-
...timeoutRequest,
|
|
176
|
-
connectTimeout: 1000,
|
|
177
|
-
url: 'http://localhost:1112/connection_timeout',
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const now = Date.now();
|
|
181
|
-
|
|
182
|
-
expect(response.content).toBe('Connection timeout');
|
|
183
|
-
expect(now - before).toBeGreaterThanOrEqual(999);
|
|
184
|
-
expect(now - before).toBeLessThanOrEqual(1200);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('connection timeouts with the given 2 seconds connection timeout', async () => {
|
|
188
|
-
const before = Date.now();
|
|
189
|
-
const response = await requester.send({
|
|
190
|
-
...timeoutRequest,
|
|
191
|
-
connectTimeout: 2000,
|
|
192
|
-
url: 'http://localhost:1112/connection_timeout',
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const now = Date.now();
|
|
196
|
-
|
|
197
|
-
expect(response.content).toBe('Connection timeout');
|
|
198
|
-
expect(now - before).toBeGreaterThanOrEqual(1999);
|
|
199
|
-
expect(now - before).toBeLessThanOrEqual(2200);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("socket timeouts if response don't appears before the timeout with 2 seconds timeout", async () => {
|
|
203
|
-
const before = Date.now();
|
|
204
|
-
|
|
205
|
-
const response = await requester.send({
|
|
206
|
-
...timeoutRequest,
|
|
207
|
-
responseTimeout: 2000,
|
|
208
|
-
url: 'http://localhost:1112',
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
const now = Date.now();
|
|
212
|
-
|
|
213
|
-
expect(response.content).toBe('Socket timeout');
|
|
214
|
-
expect(now - before).toBeGreaterThanOrEqual(1999);
|
|
215
|
-
expect(now - before).toBeLessThanOrEqual(2200);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test("socket timeouts if response don't appears before the timeout with 3 seconds timeout", async () => {
|
|
219
|
-
const before = Date.now();
|
|
220
|
-
const response = await requester.send({
|
|
221
|
-
...timeoutRequest,
|
|
222
|
-
responseTimeout: 3000,
|
|
223
|
-
url: 'http://localhost:1112',
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
const now = Date.now();
|
|
227
|
-
|
|
228
|
-
expect(response.content).toBe('Socket timeout');
|
|
229
|
-
expect(now - before).toBeGreaterThanOrEqual(2999);
|
|
230
|
-
expect(now - before).toBeLessThanOrEqual(3200);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test('do not timeouts if response appears before the timeout', async () => {
|
|
234
|
-
const before = Date.now();
|
|
235
|
-
const response = await requester.send({
|
|
236
|
-
...requestStub,
|
|
237
|
-
url: 'http://localhost:1112',
|
|
238
|
-
responseTimeout: 6000,
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const now = Date.now();
|
|
242
|
-
|
|
243
|
-
expect(response.isTimedOut).toBe(false);
|
|
244
|
-
expect(response.status).toBe(200);
|
|
245
|
-
expect(response.content).toBe('{"foo": "bar"}');
|
|
246
|
-
expect(now - before).toBeGreaterThanOrEqual(4999);
|
|
247
|
-
expect(now - before).toBeLessThanOrEqual(5200);
|
|
248
|
-
}, 10000); // This is a long-running test, default server timeout is set to 5000ms
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe('error handling', (): void => {
|
|
252
|
-
test('resolves dns not found', async () => {
|
|
253
|
-
const request: EndRequest = {
|
|
254
|
-
url: 'https://this-dont-exist.algolia.com',
|
|
255
|
-
method: 'POST',
|
|
256
|
-
headers,
|
|
257
|
-
data: getStringifiedBody(),
|
|
258
|
-
responseTimeout: 2000,
|
|
259
|
-
connectTimeout: 1000,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const response = await requester.send(request);
|
|
263
|
-
|
|
264
|
-
expect(response.status).toBe(0);
|
|
265
|
-
expect(response.content).toContain('');
|
|
266
|
-
expect(response.isTimedOut).toBe(false);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test('resolves general network errors', async () => {
|
|
270
|
-
nock(testQueryBaseUrl, { reqheaders: headers })
|
|
271
|
-
.post('/foo')
|
|
272
|
-
.query(testQueryHeader)
|
|
273
|
-
.replyWithError('This is a general error');
|
|
274
|
-
|
|
275
|
-
const response = await requester.send(requestStub);
|
|
276
|
-
|
|
277
|
-
expect(response.status).toBe(0);
|
|
278
|
-
expect(response.content).toBe('This is a general error');
|
|
279
|
-
expect(response.isTimedOut).toBe(false);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import http from 'http';
|
|
2
|
-
import https from 'https';
|
|
3
|
-
import { URL } from 'url';
|
|
4
|
-
|
|
5
|
-
import type { EndRequest, Requester, Response } from '@algolia/client-common';
|
|
6
|
-
|
|
7
|
-
export type CreateHttpRequesterOptions = Partial<{
|
|
8
|
-
agent: http.Agent | https.Agent;
|
|
9
|
-
httpAgent: http.Agent;
|
|
10
|
-
httpsAgent: https.Agent;
|
|
11
|
-
/**
|
|
12
|
-
* RequestOptions to be merged with the end request, it will override default options if provided.
|
|
13
|
-
*/
|
|
14
|
-
requesterOptions: https.RequestOptions;
|
|
15
|
-
}>;
|
|
16
|
-
|
|
17
|
-
// Global agents allow us to reuse the TCP protocol with multiple clients
|
|
18
|
-
const agentOptions = { keepAlive: true };
|
|
19
|
-
const defaultHttpAgent = new http.Agent(agentOptions);
|
|
20
|
-
const defaultHttpsAgent = new https.Agent(agentOptions);
|
|
21
|
-
|
|
22
|
-
export function createHttpRequester({
|
|
23
|
-
agent: userGlobalAgent,
|
|
24
|
-
httpAgent: userHttpAgent,
|
|
25
|
-
httpsAgent: userHttpsAgent,
|
|
26
|
-
requesterOptions = {},
|
|
27
|
-
}: CreateHttpRequesterOptions = {}): Requester {
|
|
28
|
-
const httpAgent = userHttpAgent || userGlobalAgent || defaultHttpAgent;
|
|
29
|
-
const httpsAgent = userHttpsAgent || userGlobalAgent || defaultHttpsAgent;
|
|
30
|
-
|
|
31
|
-
function send(request: EndRequest): Promise<Response> {
|
|
32
|
-
return new Promise((resolve) => {
|
|
33
|
-
let responseTimeout: NodeJS.Timeout | undefined;
|
|
34
|
-
let connectTimeout: NodeJS.Timeout | undefined;
|
|
35
|
-
const url = new URL(request.url);
|
|
36
|
-
const path = url.search === null ? url.pathname : `${url.pathname}${url.search}`;
|
|
37
|
-
const options: https.RequestOptions = {
|
|
38
|
-
agent: url.protocol === 'https:' ? httpsAgent : httpAgent,
|
|
39
|
-
hostname: url.hostname,
|
|
40
|
-
path,
|
|
41
|
-
method: request.method,
|
|
42
|
-
...requesterOptions,
|
|
43
|
-
headers: {
|
|
44
|
-
...request.headers,
|
|
45
|
-
...requesterOptions.headers,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
if (url.port && !requesterOptions.port) {
|
|
50
|
-
options.port = url.port;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const req = (url.protocol === 'https:' ? https : http).request(options, (response) => {
|
|
54
|
-
let contentBuffers: Buffer[] = [];
|
|
55
|
-
|
|
56
|
-
response.on('data', (chunk) => {
|
|
57
|
-
contentBuffers = contentBuffers.concat(chunk);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
response.on('end', () => {
|
|
61
|
-
clearTimeout(connectTimeout as NodeJS.Timeout);
|
|
62
|
-
clearTimeout(responseTimeout as NodeJS.Timeout);
|
|
63
|
-
|
|
64
|
-
resolve({
|
|
65
|
-
status: response.statusCode || 0,
|
|
66
|
-
content: Buffer.concat(contentBuffers).toString(),
|
|
67
|
-
isTimedOut: false,
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const createTimeout = (timeout: number, content: string): NodeJS.Timeout => {
|
|
73
|
-
return setTimeout(() => {
|
|
74
|
-
req.destroy();
|
|
75
|
-
|
|
76
|
-
resolve({
|
|
77
|
-
status: 0,
|
|
78
|
-
content,
|
|
79
|
-
isTimedOut: true,
|
|
80
|
-
});
|
|
81
|
-
}, timeout);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
|
|
85
|
-
|
|
86
|
-
req.on('error', (error) => {
|
|
87
|
-
clearTimeout(connectTimeout as NodeJS.Timeout);
|
|
88
|
-
clearTimeout(responseTimeout!);
|
|
89
|
-
resolve({ status: 0, content: error.message, isTimedOut: false });
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
req.once('response', () => {
|
|
93
|
-
clearTimeout(connectTimeout as NodeJS.Timeout);
|
|
94
|
-
responseTimeout = createTimeout(request.responseTimeout, 'Socket timeout');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if (request.data !== undefined) {
|
|
98
|
-
req.write(request.data);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
req.end();
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return { send };
|
|
106
|
-
}
|