UNPKG

@whatwg-node/node-fetch

Version:

Fetch API implementation for Node

191 lines (190 loc) • 8.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createFetchUndici = createFetchUndici; const node_stream_1 = require("node:stream"); const types_1 = require("node:util/types"); const node_zlib_1 = require("node:zlib"); const fetchNodeHttp_js_1 = require("./fetchNodeHttp.js"); const Request_js_1 = require("./Request.js"); const Response_js_1 = require("./Response.js"); const URL_js_1 = require("./URL.js"); const utils_js_1 = require("./utils.js"); function createFetchUndici(getGlobalDispatcher) { return function fetchUndici(fetchRequest) { const dispatcher = fetchRequest.dispatcher || getGlobalDispatcher(); if (!dispatcher) { if (process.env.DEBUG) { console.debug('[@whatwg-node/node-fetch] - native undici dispatcher not available, falling back to node:http'); } return (0, fetchNodeHttp_js_1.fetchNodeHttp)(fetchRequest); } const deferred = (0, utils_js_1.createDeferredPromise)(); let abortListener; let passthrough; let response; let body = null; const bodyInit = fetchRequest['bodyInit']; if (bodyInit != null) { if (typeof bodyInit === 'string' || Buffer.isBuffer(bodyInit) || (0, types_1.isUint8Array)(bodyInit) || bodyInit instanceof node_stream_1.Readable) { body = bodyInit; } else if (fetchRequest.body != null) { if ((0, utils_js_1.isNodeReadable)(fetchRequest.body?.readable)) { body = fetchRequest.body.readable; } else { body = node_stream_1.Readable.from(fetchRequest.body); } } } function setPassthrough(contentEncoding) { switch (contentEncoding) { case 'x-gzip': case 'gzip': passthrough = (0, node_zlib_1.createGunzip)(); break; case 'x-deflate': case 'deflate': passthrough = (0, node_zlib_1.createInflate)(); break; case 'x-deflate-raw': case 'deflate-raw': passthrough = (0, node_zlib_1.createInflateRaw)(); break; case 'br': passthrough = (0, node_zlib_1.createBrotliDecompress)(); break; default: passthrough = new node_stream_1.PassThrough(); } return passthrough; } function onAbort(e) { fetchRequest['_signal']?.removeEventListener('abort', abortListener); passthrough?.destroy(e); deferred.reject(e); } const headersSerializer = fetchRequest.headersSerializer || utils_js_1.getHeadersObj; const nodeHeaders = headersSerializer(fetchRequest.headers); if (nodeHeaders['accept-encoding'] == null) { nodeHeaders['accept-encoding'] = 'gzip, deflate, br'; } const dispatcherReturn = dispatcher.dispatch({ origin: fetchRequest.parsedUrl.origin, path: fetchRequest.parsedUrl.pathname, query: Object.fromEntries(fetchRequest.parsedUrl.searchParams), method: fetchRequest.method, headers: nodeHeaders, body, }, { onRequestStart(controller) { abortListener = function abortListener() { onAbort(fetchRequest['_signal']?.reason); controller.abort(fetchRequest['_signal']?.reason); }; fetchRequest['_signal']?.addEventListener('abort', abortListener, { once: true }); }, onRequestUpgrade(_controller, statusCode, headers, socket) { response = new Response_js_1.PonyfillResponse(socket, { status: statusCode, headers: headers, url: fetchRequest.url, }); deferred.resolve(response); fetchRequest['_signal']?.removeEventListener('abort', abortListener); }, onResponseStart(controller, statusCode, headers, statusMessage) { if (headers.location) { if (fetchRequest.redirect === 'error') { const redirectError = new Error('Redirects are not allowed'); deferred.reject(redirectError); controller.resume(); return; } if (fetchRequest.redirect === 'follow') { const redirectedUrl = new URL_js_1.PonyfillURL(headers.location, fetchRequest.parsedUrl || fetchRequest.url); const redirectResponse$ = fetchUndici(new Request_js_1.PonyfillRequest(redirectedUrl, fetchRequest)); deferred.resolve(redirectResponse$.then(redirectResponse => { redirectResponse.redirected = true; return redirectResponse; })); controller.resume(); return; } } passthrough = setPassthrough(headers['content-encoding']); deferred.resolve(new Response_js_1.PonyfillResponse(passthrough, { status: statusCode, statusText: statusMessage, headers: headers, url: fetchRequest.url, })); }, onResponseData(controller, chunk) { passthrough.write(chunk); if (controller.reason) { onAbort(controller.reason); } }, onResponseEnd(controller, _trailers) { if (controller.reason) { onAbort(controller.reason); } else { passthrough.end(); fetchRequest['_signal']?.removeEventListener('abort', abortListener); } }, onResponseError(controller, error) { onAbort(error || controller.reason); }, // Old Undici support onConnect(abort) { abortListener = function abortListener() { abort(fetchRequest['_signal']?.reason); onAbort(fetchRequest['_signal']?.reason); }; fetchRequest['_signal']?.addEventListener('abort', abortListener, { once: true }); }, onError(error) { onAbort(error); }, // TODO: onUpgrade onHeaders(statusCode, headersBuf, _resume, statusText) { const headers = headersBuf.map(headerBuf => { const header = headerBuf .toString('utf-8') .split(/:\s(.+)/) .slice(0, 2); if (header[0] === 'content-encoding') { const contentEncoding = header[1]; setPassthrough(contentEncoding); } return header; }); passthrough ||= new node_stream_1.PassThrough(); deferred.resolve(new Response_js_1.PonyfillResponse(passthrough, { status: statusCode, statusText, headers, url: fetchRequest.url, })); return true; }, onData(chunk) { return passthrough.write(chunk); }, onComplete() { passthrough.end(); fetchRequest['_signal']?.removeEventListener('abort', abortListener); }, }); if (!dispatcherReturn) { console.warn('Undici dispatcher returned false'); } return deferred.promise; }; }