1 | <div align="center">
|
2 | <img src="banner.jpeg" alt="Adobe Fetch"/>
|
3 | <br>
|
4 | <p>Light-weight <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch API</a> implementation transparently supporting both <b>HTTP/1(.1)</b> and <b>HTTP/2</b></p>
|
5 | <a href="https://codecov.io/gh/adobe/fetch"><img src="https://img.shields.io/codecov/c/github/adobe/fetch.svg" alt="codecov"></a>
|
6 | <a href="https://circleci.com/gh/adobe/fetch"><img src="https://img.shields.io/circleci/project/github/adobe/fetch.svg" alt="CircleCI"></a>
|
7 | <a href="https://github.com/adobe/fetch/blob/main/LICENSE.txt"><img src="https://img.shields.io/github/license/adobe/fetch.svg" alt="GitHub license"></a>
|
8 | <a href="https://github.com/adobe/fetch/issues"><img src="https://img.shields.io/github/issues/adobe/fetch.svg" alt="GitHub issues"></a>
|
9 | <a href="https://lgtm.com/projects/g/adobe/fetch"><img src="https://img.shields.io/lgtm/grade/javascript/g/adobe/fetch.svg?logo=lgtm&logoWidth=18" alt="LGTM Code Quality Grade: JavaScript"></a>
|
10 | <a href="https://renovatebot.com/"><img src="https://img.shields.io/badge/renovate-enabled-brightgreen.svg" alt="Renovate enabled"></a>
|
11 | <a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="semantic-release"></a>
|
12 | <a href="https://packagephobia.now.sh/result?p=@adobe/fetch"><img src="https://badgen.net/packagephobia/install/@adobe/fetch" alt="Install size"></a>
|
13 | <a href="https://www.npmjs.com/package/@adobe/fetch"><img src="https://img.shields.io/npm/v/@adobe/fetch" alt="Current version"></a>
|
14 | </div>
|
15 |
|
16 | ---
|
17 |
|
18 |
|
19 | - [About](#about)
|
20 | - [Features](#features)
|
21 | - [Installation](#installation)
|
22 | - [API](#api)
|
23 | - [Context](#context)
|
24 | - [Common Usage Examples](#common-usage-examples)
|
25 | - [Access Response Headers and other Meta data](#access-response-headers-and-other-meta-data)
|
26 | - [Fetch JSON](#fetch-json)
|
27 | - [Fetch text data](#fetch-text-data)
|
28 | - [Fetch binary data](#fetch-binary-data)
|
29 | - [Specify a timeout for a `fetch` operation](#specify-a-timeout-for-a-fetch-operation)
|
30 | - [Stream an image](#stream-an-image)
|
31 | - [Post JSON](#post-json)
|
32 | - [Post JPEG image](#post-jpeg-image)
|
33 | - [Post form data](#post-form-data)
|
34 | - [GET with query parameters object](#get-with-query-parameters-object)
|
35 | - [Cache](#cache)
|
36 | - [Advanced Usage Examples](#advanced-usage-examples)
|
37 | - [HTTP/2 Server Push](#http2-server-push)
|
38 | - [Force HTTP/1(.1) protocol](#force-http11-protocol)
|
39 | - [HTTP/1.1 Keep-Alive](#http11-keep-alive)
|
40 | - [Extract Set-Cookie Header](#extract-set-cookie-header)
|
41 | - [Self-signed Certificates](#self-signed-certificates)
|
42 | - [Set cache size limit](#set-cache-size-limit)
|
43 | - [Disable caching](#disable-caching)
|
44 | - [Set a custom user agent](#set-a-custom-user-agent)
|
45 | - [More examples](#more-examples)
|
46 | - [Development](#development)
|
47 | - [Build](#build)
|
48 | - [Test](#test)
|
49 | - [Lint](#lint)
|
50 | - [Troubleshooting](#troubleshooting)
|
51 | - [Acknowledgement](#acknowledgement)
|
52 | - [License](#license)
|
53 |
|
54 |
|
55 | ---
|
56 |
|
57 | ## About
|
58 |
|
59 | `@adobe/fetch` in general adheres to the [Fetch API Specification](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), implementing a subset of the API. However, there are some notable deviations:
|
60 |
|
61 | * `Response.body` returns a Node.js [Readable stream](https://nodejs.org/api/stream.html#stream_readable_streams).
|
62 | * `Response.blob()` is not implemented. Use `Response.buffer()` instead.
|
63 | * `Response.formData()` is not implemented.
|
64 | * Cookies are not stored by default. However, cookies can be extracted and passed by manipulating request and response headers.
|
65 | * The following values of the `fetch()` option `cache` are supported: `'default'` (the implicit default) and `'no-store'`. All other values are currently ignored.
|
66 | * The following `fetch()` options are ignored due to the nature of Node.js and since `@adobe/fetch` doesn't have the concept of web pages: `mode`, `referrer`, `referrerPolicy`, `integrity` and `credentials`.
|
67 | * The `fetch()` option `keepalive` is not supported. But you can use the `h1.keepAlive` context option, as demonstrated [here](#http11-keep-alive).
|
68 |
|
69 | `@adobe/fetch` also supports the following non-spec extensions:
|
70 |
|
71 | * `Response.buffer()` returns a Node.js `Buffer`.
|
72 | * `Response.url` contains the final url when following redirects.
|
73 | * The `body` that can be sent in a `Request` can also be a `Readable` Node.js stream, a `Buffer`, a string or a plain object.
|
74 | * There are no forbidden header names.
|
75 | * The `Response` object has an extra property `httpVersion` which is one of `'1.0'`, `'1.1'` or `'2.0'`, depending on what was negotiated with the server.
|
76 | * The `Response` object has an extra property `fromCache` which determines whether the response was retrieved from cache.
|
77 | * The `Response` object has an extra property `decoded` which determines whether the response body was automatically decoded (see Fetch option `decode` below).
|
78 | * `Response.headers.plain()` returns the headers as a plain object.
|
79 | * `Response.headers.raw()` returns the internal/raw representation of the headers where e.g. the `Set-Cokkie` header is represented with an array of strings value.
|
80 | * The Fetch option `follow` allows to limit the number of redirects to follow (default: `20`).
|
81 | * The Fetch option `compress` enables transparent gzip/deflate/br content encoding (default: `true`).
|
82 | * The Fetch option `decode` enables transparent gzip/deflate/br content decoding (default: `true`).
|
83 |
|
84 | Note that non-standard Fetch options have been aligned with [node-fetch](https://github.com/node-fetch/node-fetch) where appropriate.
|
85 |
|
86 | ## Features
|
87 |
|
88 | * [x] supports reasonable subset of the standard [Fetch specification](https://fetch.spec.whatwg.org/)
|
89 | * [x] Transparent handling of HTTP/1(.1) and HTTP/2 connections
|
90 | * [x] [RFC 7234](https://httpwg.org/specs/rfc7234.html) compliant cache
|
91 | * [x] Support `gzip/deflate/br` content encoding
|
92 | * [x] HTTP/2 request and response multiplexing support
|
93 | * [x] HTTP/2 Server Push support (transparent caching and explicit listener support)
|
94 | * [x] overridable User-Agent
|
95 | * [x] low-level HTTP/1.* agent/connect options support (e.g. `keepAlive`, `rejectUnauthorized`)
|
96 |
|
97 | ## Installation
|
98 |
|
99 | > **Note**:
|
100 | >
|
101 | > As of v2 Node version >= 12 is required.
|
102 |
|
103 | ```bash
|
104 | $ npm install @adobe/fetch
|
105 | ```
|
106 |
|
107 | ## API
|
108 |
|
109 | Apart from the standard Fetch API
|
110 |
|
111 | * `fetch()`
|
112 | * `Request`
|
113 | * `Response`
|
114 | * `Headers`
|
115 | * `Body`
|
116 |
|
117 | `@adobe/fetch` exposes the following non-spec extensions:
|
118 |
|
119 | * `context()` - creates a new customized API context
|
120 | * `reset()` - resets the current API context, i.e. closes pending sessions/sockets, clears internal caches, etc ...
|
121 | * `onPush()` - registers an HTTP/2 Server Push listener
|
122 | * `offPush()`- deregisters a listener previously registered with `onPush()`
|
123 | * `clearCache()` - clears the HTTP cache (cached responses)
|
124 | * `cacheStats()` - returns cache statistics
|
125 | * `noCache()` - creates a customized API context with disabled caching (_convenience_)
|
126 | * `h1()` - creates a customized API context with enforced HTTP/1.1 protocol (_convenience_)
|
127 | * `keepAlive()` - creates a customized API context with enforced HTTP/1.1 protocol and persistent connections (_convenience_)
|
128 | * `h1NoCache()` - creates a customized API context with disabled caching and enforced HTTP/1.1 protocol (_convenience_)
|
129 | * `keepAliveNoCache()` - creates a customized API context with disabled caching and enforced HTTP/1.1 protocol with persistent connections (_convenience_)
|
130 | * `createUrl()` - creates a URL with query parameters (_convenience_)
|
131 | * `timeoutSignal()` - ceates a timeout signal (_convenience_)
|
132 |
|
133 | ### Context
|
134 |
|
135 | An API context allows to customize certain aspects of the implementation and provides isolation of internal structures (session caches, HTTP cache, etc.) per API context.
|
136 |
|
137 | The following options are supported:
|
138 |
|
139 | ```ts
|
140 | interface ContextOptions {
|
141 | /**
|
142 | * Value of `user-agent` request header
|
143 | * @default 'adobe-fetch/<version>'
|
144 | */
|
145 | userAgent?: string;
|
146 | /**
|
147 | * The maximum total size of the cached entries (in bytes). 0 disables caching.
|
148 | * @default 100 * 1024 * 1024
|
149 | */
|
150 | maxCacheSize?: number;
|
151 | /**
|
152 | * The protocols to be negotiated, in order of preference
|
153 | * @default [ALPN_HTTP2, ALPN_HTTP1_1, ALPN_HTTP1_0]
|
154 | */
|
155 | alpnProtocols?: ReadonlyArray< ALPNProtocol >;
|
156 | /**
|
157 | * How long (in milliseconds) should ALPN information be cached for a given host?
|
158 | * @default 60 * 60 * 1000
|
159 | */
|
160 | alpnCacheTTL?: number;
|
161 | /**
|
162 | * (HTTPS only, applies to HTTP/1.x and HTTP/2)
|
163 | * If not false, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails; err.code contains the OpenSSL error code.
|
164 | * @default true
|
165 | */
|
166 | rejectUnauthorized?: boolean;
|
167 | /**
|
168 | * Maximum number of ALPN cache entries
|
169 | * @default 100
|
170 | */
|
171 | alpnCacheSize?: number;
|
172 | h1?: Http1Options;
|
173 | h2?: Http2Options;
|
174 | };
|
175 |
|
176 | interface Http1Options {
|
177 | /**
|
178 | * Keep sockets around in a pool to be used by other requests in the future.
|
179 | * @default false
|
180 | */
|
181 | keepAlive?: boolean;
|
182 | /**
|
183 | * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive.
|
184 | * Only relevant if keepAlive is set to true.
|
185 | * @default 1000
|
186 | */
|
187 | keepAliveMsecs?: number;
|
188 | /**
|
189 | * (HTTPS only)
|
190 | * If not false, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails; err.code contains the OpenSSL error code.
|
191 | * @default true
|
192 | */
|
193 | rejectUnauthorized?: boolean;
|
194 | /**
|
195 | * (HTTPS only)
|
196 | * Maximum number of TLS cached sessions. Use 0 to disable TLS session caching.
|
197 | * @default 100
|
198 | */
|
199 | maxCachedSessions?: number;
|
200 | }
|
201 |
|
202 | interface Http2Options {
|
203 | /**
|
204 | * Max idle time in milliseconds after which a session will be automatically closed.
|
205 | * @default 5 * 60 * 1000
|
206 | */
|
207 | idleSessionTimeout?: number;
|
208 | /**
|
209 | * Enable HTTP/2 Server Push?
|
210 | * @default true
|
211 | */
|
212 | enablePush?: boolean;
|
213 | /**
|
214 | * Max idle time in milliseconds after which a pushed stream will be automatically closed.
|
215 | * @default 5000
|
216 | */
|
217 | pushedStreamIdleTimeout?: number;
|
218 | /**
|
219 | * (HTTPS only)
|
220 | * If not false, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails; err.code contains the OpenSSL error code.
|
221 | * @default true
|
222 | */
|
223 | rejectUnauthorized?: boolean;
|
224 | };
|
225 | ```
|
226 |
|
227 | ## Common Usage Examples
|
228 |
|
229 | ### Access Response Headers and other Meta data
|
230 |
|
231 | ```javascript
|
232 | const { fetch } = require('@adobe/fetch');
|
233 |
|
234 | const resp = await fetch('https://httpbin.org/get');
|
235 | console.log(resp.ok);
|
236 | console.log(resp.status);
|
237 | console.log(resp.statusText);
|
238 | console.log(resp.httpVersion);
|
239 | console.log(resp.headers.plain());
|
240 | console.log(resp.headers.get('content-type'));
|
241 | ```
|
242 |
|
243 | ### Fetch JSON
|
244 |
|
245 | ```javascript
|
246 | const { fetch } = require('@adobe/fetch');
|
247 |
|
248 | const resp = await fetch('https://httpbin.org/json');
|
249 | const jsonData = await resp.json();
|
250 | ```
|
251 |
|
252 | ### Fetch text data
|
253 |
|
254 | ```javascript
|
255 | const { fetch } = require('@adobe/fetch');
|
256 |
|
257 | const resp = await fetch('https://httpbin.org/');
|
258 | const textData = await resp.text();
|
259 | ```
|
260 |
|
261 | ### Fetch binary data
|
262 |
|
263 | ```javascript
|
264 | const { fetch } = require('@adobe/fetch');
|
265 |
|
266 | const resp = await fetch('https://httpbin.org//stream-bytes/65535');
|
267 | const imageData = await resp.buffer();
|
268 | ```
|
269 |
|
270 | ### Specify a timeout for a `fetch` operation
|
271 |
|
272 | Using `timeoutSignal(ms)` non-spec extension:
|
273 |
|
274 | ```javascript
|
275 | const { fetch, timeoutSignal, AbortError } = require('@adobe/fetch');
|
276 |
|
277 | const signal = timeoutSignal(1000);
|
278 | try {
|
279 | const resp = await fetch('https://httpbin.org/json', { signal });
|
280 | const jsonData = await resp.json();
|
281 | } catch (err) {
|
282 | if (err instanceof AbortError) {
|
283 | console.log('fetch timed out after 1s');
|
284 | }
|
285 | } finally {
|
286 | // avoid pending timers which prevent node process from exiting
|
287 | signal.clear();
|
288 | }
|
289 | ```
|
290 |
|
291 | Using `AbortController`:
|
292 |
|
293 | ```javascript
|
294 | const { fetch, AbortController, AbortError } = require('@adobe/fetch');
|
295 |
|
296 | const controller = new AbortController();
|
297 | const timerId = setTimeout(() => controller.abort(), 1000);
|
298 | const { signal } = controller;
|
299 |
|
300 | try {
|
301 | const resp = await fetch('https://httpbin.org/json', { signal });
|
302 | const jsonData = await resp.json();
|
303 | } catch (err) {
|
304 | if (err instanceof AbortError) {
|
305 | console.log('fetch timed out after 1s');
|
306 | }
|
307 | } finally {
|
308 | // avoid pending timers which prevent node process from exiting
|
309 | clearTimeout(timerId);
|
310 | }
|
311 | ```
|
312 |
|
313 | ### Stream an image
|
314 |
|
315 | ```javascript
|
316 | const fs = require('fs');
|
317 | const { fetch } = require('@adobe/fetch');
|
318 |
|
319 | const resp = await fetch('https://httpbin.org/image/jpeg');
|
320 | resp.body.pipe(fs.createWriteStream('saved-image.jpg'));
|
321 | ```
|
322 |
|
323 | ### Post JSON
|
324 |
|
325 | ```javascript
|
326 | const { fetch } = require('@adobe/fetch');
|
327 |
|
328 | const method = 'POST';
|
329 | const body = { foo: 'bar' };
|
330 | const resp = await fetch('https://httpbin.org/post', { method, body });
|
331 | ```
|
332 |
|
333 | ### Post JPEG image
|
334 |
|
335 | ```javascript
|
336 | const fs = require('fs');
|
337 | const { fetch } = require('@adobe/fetch');
|
338 |
|
339 | const method = 'POST';
|
340 | const body = fs.createReadStream('some-image.jpg');
|
341 | const headers = { 'content-type': 'image/jpeg' };
|
342 | const resp = await fetch('https://httpbin.org/post', { method, body, headers });
|
343 | ```
|
344 |
|
345 | ### Post form data
|
346 |
|
347 | ```javascript
|
348 | const { FormData, Blob, File } = require('formdata-node'); // spec-compliant implementations
|
349 | const { fileFromPath } = require('formdata-node/file-from-path'); // helper for creating File instance from disk file
|
350 |
|
351 | const { fetch } = require('@adobe/fetch');
|
352 |
|
353 | const method = 'POST';
|
354 | const fd = new FormData();
|
355 | fd.set('field1', 'foo');
|
356 | fd.set('field2', 'bar');
|
357 | fd.set('blob', new Blob([0x68, 0x65, 0x6c, 0x69, 0x78, 0x2d, 0x66, 0x65, 0x74, 0x63, 0x68]));
|
358 | fd.set('file', new File(['File content goes here'], 'file.txt'));
|
359 | fd.set('other_file', await fileFromPath('/foo/bar.jpg', 'bar.jpg', { type: 'image/jpeg' }));
|
360 | const resp = await fetch('https://httpbin.org/post', { method, body: fd });
|
361 | ```
|
362 |
|
363 | ### GET with query parameters object
|
364 |
|
365 | ```javascript
|
366 | const { createUrl, fetch } = require('@adobe/fetch');
|
367 |
|
368 | const qs = {
|
369 | fake: 'dummy',
|
370 | foo: 'bar',
|
371 | rumple: "stiltskin",
|
372 | };
|
373 |
|
374 | const resp = await fetch(createUrl('https://httpbin.org/json', qs));
|
375 | ```
|
376 |
|
377 | or using `URLSearchParams`:
|
378 |
|
379 | ```javascript
|
380 | const { fetch } = require('@adobe/fetch');
|
381 |
|
382 | const body = new URLSearchParams({
|
383 | fake: 'dummy',
|
384 | foo: 'bar',
|
385 | rumple: "stiltskin",
|
386 | });
|
387 |
|
388 | const resp = await fetch('https://httpbin.org/json', { body });
|
389 | ```
|
390 |
|
391 | ### Cache
|
392 |
|
393 | Responses of `GET` and `HEAD` requests are by default cached, according to the rules of [RFC 7234](https://httpwg.org/specs/rfc7234.html):
|
394 |
|
395 | ```javascript
|
396 | const { fetch } = require('@adobe/fetch');
|
397 |
|
398 | const url = 'https://httpbin.org/cache/60'; // -> max-age=60 (seconds)
|
399 | // send initial request, priming cache
|
400 | let resp = await fetch(url);
|
401 | assert(resp.ok);
|
402 | assert(!resp.fromCache);
|
403 |
|
404 | // re-send request and verify it's served from cache
|
405 | resp = await fetch(url);
|
406 | assert(resp.ok);
|
407 | assert(resp.fromCache);
|
408 | ```
|
409 |
|
410 | You can disable caching per request with the `cache: 'no-store'` option:
|
411 |
|
412 | ```javascript
|
413 | const { fetch } = require('@adobe/fetch');
|
414 |
|
415 | const resp = await fetch('https://httbin.org/', { cache: 'no-store' });
|
416 | assert(resp.ok);
|
417 | assert(!resp.fromCache);
|
418 | ```
|
419 |
|
420 | You can disable caching entirely:
|
421 |
|
422 | ```javascript
|
423 | const { fetch } = require('@adobe/fetch').noCache();
|
424 | ```
|
425 |
|
426 | ## Advanced Usage Examples
|
427 |
|
428 | ### HTTP/2 Server Push
|
429 |
|
430 | Note that pushed resources will be automatically and transparently added to the cache.
|
431 | You can however add a listener which will be notified on every pushed (and cached) resource.
|
432 |
|
433 | ```javascript
|
434 | const { fetch, onPush } = require('@adobe/fetch');
|
435 |
|
436 | onPush((url, response) => console.log(`received server push: ${url} status ${response.status}`));
|
437 |
|
438 | const resp = await fetch('https://nghttp2.org');
|
439 | console.log(`Http version: ${resp.httpVersion}`);
|
440 | ```
|
441 |
|
442 | ### Force HTTP/1(.1) protocol
|
443 |
|
444 | ```javascript
|
445 | const { fetch } = require('@adobe/fetch').h1();
|
446 |
|
447 | const resp = await fetch('https://nghttp2.org');
|
448 | console.log(`Http version: ${resp.httpVersion}`);
|
449 | ```
|
450 |
|
451 | ### HTTP/1.1 Keep-Alive
|
452 |
|
453 | ```javascript
|
454 | const { fetch } = require('@adobe/fetch').keepAlive();
|
455 |
|
456 | const resp = await fetch('https://httpbin.org/status/200');
|
457 | console.log(`Connection: ${resp.headers.get('connection')}`); // -> keep-alive
|
458 | ```
|
459 |
|
460 | ### Extract Set-Cookie Header
|
461 |
|
462 | Unlike browsers, you can access raw `Set-Cookie` headers manually using `Headers.raw()`. This is an `@adobe/fetch` only API.
|
463 |
|
464 | ```javascript
|
465 | const { fetch } = require('@adobe/fetch');
|
466 |
|
467 | const resp = await fetch('https://httpbin.org/cookies/set?a=1&b=2');
|
468 | // returns an array of values, instead of a string of comma-separated values
|
469 | console.log(resp.headers.raw()['set-cookie']);
|
470 | ```
|
471 |
|
472 | ### Self-signed Certificates
|
473 |
|
474 | ```javascript
|
475 | const { fetch } = require('@adobe/fetch').context({ rejectUnauthorized: false });
|
476 |
|
477 | const resp = await fetch('https://localhost:8443/'); // a server using a self-signed certificate
|
478 | ```
|
479 |
|
480 | ### Set cache size limit
|
481 |
|
482 | ```javascript
|
483 | const { fetch, cacheStats } = require('@adobe/fetch').context({
|
484 | maxCacheSize: 100 * 1024, // 100kb (Default: 100mb)
|
485 | });
|
486 |
|
487 | let resp = await fetch('https://httpbin.org/bytes/60000'); // ~60kb response
|
488 | resp = await fetch('https://httpbin.org/bytes/50000'); // ~50kb response
|
489 | console.log(cacheStats());
|
490 | ```
|
491 |
|
492 | ### Disable caching
|
493 |
|
494 | ```javascript
|
495 | const { fetch } = require('@adobe/fetch').noCache();
|
496 |
|
497 | let resp = await fetch('https://httpbin.org/cache/60'); // -> max-age=60 (seconds)
|
498 | // re-fetch
|
499 | resp = await fetch('https://httpbin.org/cache/60');
|
500 | assert(!resp.fromCache);
|
501 | ```
|
502 |
|
503 | ### Set a custom user agent
|
504 |
|
505 | ```javascript
|
506 | const { fetch } = require('@adobe/fetch').context({
|
507 | userAgent: 'custom-fetch'
|
508 | });
|
509 |
|
510 | const resp = await fetch('https://httpbin.org//user-agent');
|
511 | const json = await resp.json();
|
512 | console.log(json['user-agent']);
|
513 | ```
|
514 |
|
515 | ## More examples
|
516 |
|
517 | More example code can be found in the [test source files](/test/).
|
518 |
|
519 | ## Development
|
520 |
|
521 | ### Build
|
522 |
|
523 | ```bash
|
524 | $ npm install
|
525 | ```
|
526 |
|
527 | ### Test
|
528 |
|
529 | ```bash
|
530 | $ npm test
|
531 | ```
|
532 |
|
533 | ### Lint
|
534 |
|
535 | ```bash
|
536 | $ npm run lint
|
537 | ```
|
538 |
|
539 | ### Troubleshooting
|
540 |
|
541 | You can enable `@adobe/fetch` low-level debug console output by setting the `DEBUG` environment variable to `adobe/fetch*`, e.g.:
|
542 |
|
543 | ```bash
|
544 | $ DEBUG=adobe/fetch* node test.js
|
545 | ```
|
546 |
|
547 | This will produce console outout similar to:
|
548 |
|
549 | ```bash
|
550 | ...
|
551 | adobe/fetch:core established TLS connection: #48 (www.nghttp2.org) +2s
|
552 | adobe/fetch:core www.nghttp2.org -> h2 +0ms
|
553 | adobe/fetch:h2 reusing socket #48 (www.nghttp2.org) +2s
|
554 | adobe/fetch:h2 GET www.nghttp2.org/httpbin/user-agent +0ms
|
555 | adobe/fetch:h2 session https://www.nghttp2.org established +1ms
|
556 | adobe/fetch:h2 caching session https://www.nghttp2.org +0ms
|
557 | adobe/fetch:h2 session https://www.nghttp2.org remoteSettings: {"headerTableSize":8192,"enablePush":true,"initialWindowSize":1048576,"maxFrameSize":16384,"maxConcurrentStreams":100,"maxHeaderListSize":4294967295,"maxHeaderSize":4294967295,"enableConnectProtocol":true} +263ms
|
558 | adobe/fetch:h2 session https://www.nghttp2.org localSettings: {"headerTableSize":4096,"enablePush":true,"initialWindowSize":65535,"maxFrameSize":16384,"maxConcurrentStreams":4294967295,"maxHeaderListSize":4294967295,"maxHeaderSize":4294967295,"enableConnectProtocol":false} +0ms
|
559 | adobe/fetch:h2 session https://www.nghttp2.org closed +6ms
|
560 | adobe/fetch:h2 discarding cached session https://www.nghttp2.org +0ms
|
561 | ...
|
562 | ```
|
563 |
|
564 | Additionally, you can enable Node.js low-level debug console output by setting the `NODE_DEBUG` environment variable appropriately, e.g.
|
565 |
|
566 | ```bash
|
567 | $ export NODE_DEBUG=http*,stream*
|
568 | $ export DEBUG=adobe/fetch*
|
569 |
|
570 | $ node test.js
|
571 | ```
|
572 |
|
573 | > Note: this will flood the console with highly verbose debug output.
|
574 |
|
575 | ## Acknowledgement
|
576 |
|
577 | Thanks to [node-fetch](https://github.com/node-fetch/node-fetch) and [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference.
|
578 |
|
579 | ## License
|
580 |
|
581 | [Apache 2.0](LICENSE.txt)
|