@superhero/http-request 4.0.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/README.md +351 -0
- package/index.js +704 -0
- package/index.test.js +555 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# HTTP-Request
|
|
2
|
+
|
|
3
|
+
An HTTP client for Node.js supporting HTTP/1.1 and HTTP/2.0, with support for retries, timeouts, and streaming.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Protocol Support**: HTTP/1.1 and HTTP/2.0.
|
|
8
|
+
- **HTTP Methods**: Supports all standard methods—GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE.
|
|
9
|
+
- **Retries**: Configurable retry mechanism for requests.
|
|
10
|
+
- **Timeouts**: Set request timeouts to prevent hanging requests.
|
|
11
|
+
- **Streaming**: Stream request bodies and responses (upstream and downstream).
|
|
12
|
+
- **Data Handling**: Automatically handles JSON and URL-encoded form data.
|
|
13
|
+
- **Customizable**: Configure headers, body, query parameters, and more.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Install via npm:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @superhero/http-request
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Importing the Module
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import Request from '@superhero/http-request'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Creating a Request Instance
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
const request = new Request({
|
|
35
|
+
base : 'https://example.com',
|
|
36
|
+
headers : { 'Content-Type': 'application/json' },
|
|
37
|
+
timeout : 5e3, // 5 seconds
|
|
38
|
+
retry : 3, // Retry up to 3 times
|
|
39
|
+
retryDelay : 200 // 200ms delay between retries
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Making Requests
|
|
44
|
+
|
|
45
|
+
Use the methods corresponding to HTTP verbs:
|
|
46
|
+
|
|
47
|
+
- `get(options)`
|
|
48
|
+
- `post(options)`
|
|
49
|
+
- `put(options)`
|
|
50
|
+
- `patch(options)`
|
|
51
|
+
- `delete(options)`
|
|
52
|
+
- `head(options)`
|
|
53
|
+
- `options(options)`
|
|
54
|
+
- `trace(options)`
|
|
55
|
+
|
|
56
|
+
Each method accepts an `options` object.
|
|
57
|
+
|
|
58
|
+
#### Example: GET Request
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
const response = await request.get('/users')
|
|
62
|
+
|
|
63
|
+
console.log(response.status) // HTTP status code
|
|
64
|
+
console.log(response.headers) // Response headers
|
|
65
|
+
console.log(response.body) // Parsed response body
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Example: POST Request with JSON Body
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
const response = await request.post({
|
|
72
|
+
url : '/users',
|
|
73
|
+
body : { name: 'John Doe', email: 'john@example.com' }
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
console.log(response.status)
|
|
77
|
+
console.log(response.body)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Example: PUT Request with Form Data
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
const response = await request.put({
|
|
84
|
+
url : '/users/123',
|
|
85
|
+
headers : { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
86
|
+
body : { name: 'Jane Doe', email: 'jane@example.com' }
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
console.log(response.status)
|
|
90
|
+
console.log(response.body)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Handling Errors
|
|
94
|
+
|
|
95
|
+
Errors are thrown with specific error codes for easier handling:
|
|
96
|
+
|
|
97
|
+
- `E_HTTP_REQUEST_CLIENT_ERROR`
|
|
98
|
+
- `E_HTTP_REQUEST_CLIENT_TIMEOUT`
|
|
99
|
+
- `E_HTTP_REQUEST_DOWNSTREAM_ERROR`
|
|
100
|
+
- `E_HTTP_REQUEST_INVALID_RESPONSE_BODY_FORMAT`
|
|
101
|
+
- `E_HTTP_REQUEST_INVALID_RESPONSE_STATUS`
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
try {
|
|
107
|
+
const response = await request.get({ url: '/data' })
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error('Request failed:', error.message, error.code)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Configuring Retries and Timeouts
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
const response = await request.get({
|
|
118
|
+
url: '/unstable-endpoint',
|
|
119
|
+
timeout: 3000, // 3 seconds timeout
|
|
120
|
+
retry: 5, // Retry up to 5 times
|
|
121
|
+
retryDelay: 500, // 500ms delay between retries
|
|
122
|
+
retryOnClientTimeout: true // Retry if client times out
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Streaming Data
|
|
127
|
+
|
|
128
|
+
#### Streaming Request Body (Upstream)
|
|
129
|
+
|
|
130
|
+
Stream data to the server using the `upstream` option with a readable stream.
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
import { Readable } from 'node:stream'
|
|
134
|
+
|
|
135
|
+
const upstream = Readable.from(['streaming ', 'data ', 'to ', 'server'])
|
|
136
|
+
|
|
137
|
+
const response = await request.post({
|
|
138
|
+
url: '/upload',
|
|
139
|
+
upstream
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Streaming Response Body (Downstream)
|
|
144
|
+
|
|
145
|
+
Stream the response data from the server using the `downstream` option with a writable stream.
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
import { createWriteStream } from 'node:fs'
|
|
149
|
+
|
|
150
|
+
const downstream = createWriteStream('output.txt')
|
|
151
|
+
|
|
152
|
+
const response = await request.get({
|
|
153
|
+
url: '/download',
|
|
154
|
+
downstream
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
downstream.on('finish', () => {
|
|
158
|
+
console.log('Download completed.')
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Using HTTP/2
|
|
163
|
+
|
|
164
|
+
Establish a connection for HTTP/2 requests.
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
await request.connect('http://api.example.com')
|
|
168
|
+
|
|
169
|
+
// Now you can make HTTP/2 requests
|
|
170
|
+
const response = await request.get({ url: '/users' })
|
|
171
|
+
|
|
172
|
+
// Close the connection when done
|
|
173
|
+
await request.close()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## API Reference
|
|
177
|
+
|
|
178
|
+
### Class: Request
|
|
179
|
+
|
|
180
|
+
#### Constructor
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
new Request(config)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- `config`: An object containing default request configurations.
|
|
187
|
+
|
|
188
|
+
#### Methods
|
|
189
|
+
|
|
190
|
+
- **connect(authority, [options])**
|
|
191
|
+
- Connects to an HTTP/2 server.
|
|
192
|
+
- `authority`: The URL to connect to.
|
|
193
|
+
- `options`: Optional connection options.
|
|
194
|
+
|
|
195
|
+
- **close()**
|
|
196
|
+
- Closes the HTTP/2 client connection.
|
|
197
|
+
|
|
198
|
+
- **get(options)**
|
|
199
|
+
- Makes a GET request.
|
|
200
|
+
|
|
201
|
+
- **post(options)**
|
|
202
|
+
- Makes a POST request.
|
|
203
|
+
|
|
204
|
+
- **put(options)**
|
|
205
|
+
- Makes a PUT request.
|
|
206
|
+
|
|
207
|
+
- **patch(options)**
|
|
208
|
+
- Makes a PATCH request.
|
|
209
|
+
|
|
210
|
+
- **delete(options)**
|
|
211
|
+
- Makes a DELETE request.
|
|
212
|
+
|
|
213
|
+
- **head(options)**
|
|
214
|
+
- Makes a HEAD request.
|
|
215
|
+
|
|
216
|
+
- **options(options)**
|
|
217
|
+
- Makes an OPTIONS request.
|
|
218
|
+
|
|
219
|
+
- **trace(options)**
|
|
220
|
+
- Makes a TRACE request.
|
|
221
|
+
|
|
222
|
+
#### Request Options
|
|
223
|
+
|
|
224
|
+
- **url**: The endpoint URL (relative to `base` if provided).
|
|
225
|
+
- **base**: The base URL for resolving relative URLs.
|
|
226
|
+
- **headers**: An object containing request headers.
|
|
227
|
+
- **body** or **data**: The request payload (object or string).
|
|
228
|
+
- **method**: HTTP method (overrides method implied by the function used).
|
|
229
|
+
- **timeout**: Request timeout in milliseconds.
|
|
230
|
+
- **retry**: Number of retry attempts on failure.
|
|
231
|
+
- **retryDelay**: Delay between retries in milliseconds.
|
|
232
|
+
- **retryOnStatus**: Array of HTTP status codes to trigger a retry.
|
|
233
|
+
- **retryOnClientTimeout**: Retry on client timeout (`true` or `false`).
|
|
234
|
+
- **retryOnDownstreamError**: Retry on downstream errors (`true` or `false`).
|
|
235
|
+
- **retryOnInvalidResponseBodyFormat**: Retry if response body format is invalid (`true` or `false`).
|
|
236
|
+
- **retryOnErrorResponseStatus**: Retry on error HTTP statuses (`true` or `false`).
|
|
237
|
+
- **doNotThrowOnErrorStatus**: Do not throw on error statuses (`true` or `false`).
|
|
238
|
+
- **doNotThrowOnRedirectStatus**: Do not throw on redirect statuses (`true` or `false`).
|
|
239
|
+
- **upstream**: A `Readable` stream to pipe the request body from.
|
|
240
|
+
- **downstream**: A `Writable` stream to pipe the response body to.
|
|
241
|
+
|
|
242
|
+
#### Response Object
|
|
243
|
+
|
|
244
|
+
- **status**: HTTP status code of the response.
|
|
245
|
+
- **headers**: Response headers as an object.
|
|
246
|
+
- **body**: Parsed response body. If the response is JSON, it's parsed automatically. If `downstream` is used, `body` is omitted.
|
|
247
|
+
|
|
248
|
+
## Testing
|
|
249
|
+
|
|
250
|
+
The test suite uses Node.js's built-in `node:test` module.
|
|
251
|
+
|
|
252
|
+
### Running the Tests
|
|
253
|
+
|
|
254
|
+
To run the tests, execute:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
node test.js
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Test Coverage
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
▶ @superhero/http-request
|
|
264
|
+
▶ HTTP 1.1
|
|
265
|
+
▶ Request using method
|
|
266
|
+
✔ POST (29.927617ms)
|
|
267
|
+
✔ PUT (6.608225ms)
|
|
268
|
+
✔ PATCH (4.07807ms)
|
|
269
|
+
✔ GET (4.255941ms)
|
|
270
|
+
✔ DELETE (5.305515ms)
|
|
271
|
+
✔ HEAD (4.096928ms)
|
|
272
|
+
✔ OPTIONS (4.635634ms)
|
|
273
|
+
✔ TRACE (4.071535ms)
|
|
274
|
+
✔ Request using method (64.83487ms)
|
|
275
|
+
|
|
276
|
+
✔ Request using the URL as the option parameter (8.577185ms)
|
|
277
|
+
✔ Request using a string body (5.060509ms)
|
|
278
|
+
✔ Request using a custom header (3.345092ms)
|
|
279
|
+
✔ Normalizes headers and body (2.763181ms)
|
|
280
|
+
✔ Can pipe upstream body through the request from a readable stream (6.058912ms)
|
|
281
|
+
✔ Can pipe downstream response from the request to a writable stream (5.560773ms)
|
|
282
|
+
|
|
283
|
+
▶ Tests that require an altered server response
|
|
284
|
+
✔ Supports request timeout (106.042009ms)
|
|
285
|
+
✔ Rejects invalid JSON response accurately (4.167691ms)
|
|
286
|
+
✔ Retry on client error (214.183201ms)
|
|
287
|
+
✔ Retry on client timeout (311.841398ms)
|
|
288
|
+
✔ Retry on invalid response body format (208.378113ms)
|
|
289
|
+
|
|
290
|
+
▶ Retry on response status
|
|
291
|
+
✔ Retry on status: 503 - should succeed after re-attempts (209.880754ms)
|
|
292
|
+
✔ Retry on status: 503 - should reject after to few re-attempts (108.7312ms)
|
|
293
|
+
✔ Retry on status: 500 - should reject on first attempt (4.106091ms)
|
|
294
|
+
|
|
295
|
+
▶ Retry on error response status
|
|
296
|
+
✔ Should succeed after re-attempts (212.3066ms)
|
|
297
|
+
✔ Should succeed after re-attempts when "doNotThrowOnErrorStatus" (8.122609ms)
|
|
298
|
+
✔ Retry on error response status (222.080598ms)
|
|
299
|
+
✔ Retry on response status (547.205892ms)
|
|
300
|
+
✔ Tests that require an altered server response (1392.469482ms)
|
|
301
|
+
✔ HTTP 1.1 (1489.71029ms)
|
|
302
|
+
|
|
303
|
+
▶ HTTP 2.0
|
|
304
|
+
▶ Request using method
|
|
305
|
+
✔ POST (24.172115ms)
|
|
306
|
+
✔ PUT (4.569734ms)
|
|
307
|
+
✔ PATCH (5.478934ms)
|
|
308
|
+
✔ GET (3.347633ms)
|
|
309
|
+
✔ DELETE (4.18065ms)
|
|
310
|
+
✔ HEAD (2.557823ms)
|
|
311
|
+
✔ OPTIONS (3.031275ms)
|
|
312
|
+
✔ TRACE (4.541577ms)
|
|
313
|
+
✔ Request using method (52.666782ms)
|
|
314
|
+
|
|
315
|
+
✔ Request using the URL as the option parameter (3.139841ms)
|
|
316
|
+
✔ Request using a string body (3.152696ms)
|
|
317
|
+
✔ Request using a custom header (2.364444ms)
|
|
318
|
+
✔ Normalizes headers and body (3.156592ms)
|
|
319
|
+
✔ Can pipe upstream body through the request from a readable stream (4.870264ms)
|
|
320
|
+
✔ Can pipe downstream response from the request to a writable stream (2.96681ms)
|
|
321
|
+
|
|
322
|
+
▶ Tests that require an altered server response
|
|
323
|
+
✔ Retry on connection error (212.816865ms)
|
|
324
|
+
✔ Supports request timeout (108.664584ms)
|
|
325
|
+
✔ Tests that require an altered server response (322.005974ms)
|
|
326
|
+
✔ HTTP 2.0 (394.95823ms)
|
|
327
|
+
✔ @superhero/http-request (1885.245212ms)
|
|
328
|
+
|
|
329
|
+
tests 42
|
|
330
|
+
suites 7
|
|
331
|
+
pass 42
|
|
332
|
+
|
|
333
|
+
------------------------------------------------------------------------
|
|
334
|
+
file | line % | branch % | funcs % | uncovered lines
|
|
335
|
+
------------------------------------------------------------------------
|
|
336
|
+
index.js | 97.16 | 92.45 | 95.12 | 487-496 524-526 662-668
|
|
337
|
+
index.test.js | 100.00 | 97.67 | 100.00 |
|
|
338
|
+
------------------------------------------------------------------------
|
|
339
|
+
all files | 98.41 | 94.79 | 98.10 |
|
|
340
|
+
------------------------------------------------------------------------
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
This project is licensed under the MIT License.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Contributing
|
|
351
|
+
Feel free to submit issues or pull requests for improvements or additional features.
|