UNPKG

got

Version:

Human-friendly and powerful HTTP request library for Node.js

122 lines (121 loc) 4.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const duplexer3 = require("duplexer3"); const http_1 = require("http"); const stream_1 = require("stream"); const errors_1 = require("./errors"); const request_as_event_emitter_1 = require("./request-as-event-emitter"); class ProxyStream extends stream_1.Duplex { } exports.ProxyStream = ProxyStream; function asStream(options) { const input = new stream_1.PassThrough(); const output = new stream_1.PassThrough(); const proxy = duplexer3(input, output); const piped = new Set(); let isFinished = false; options.retry.calculateDelay = () => 0; if (options.body || options.json || options.form) { proxy.write = () => { proxy.destroy(); throw new Error('Got\'s stream is not writable when the `body`, `json` or `form` option is used'); }; } else if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || (options.allowGetBody && options.method === 'GET')) { options.body = input; } else { proxy.write = () => { proxy.destroy(); throw new TypeError(`The \`${options.method}\` method cannot be used with a body`); }; } const emitter = request_as_event_emitter_1.default(options); const emitError = async (error) => { try { for (const hook of options.hooks.beforeError) { // eslint-disable-next-line no-await-in-loop error = await hook(error); } proxy.emit('error', error); } catch (error_) { proxy.emit('error', error_); } }; // Cancels the request proxy._destroy = (error, callback) => { callback(error); emitter.abort(); }; emitter.on('response', (response) => { const { statusCode, isFromCache } = response; proxy.isFromCache = isFromCache; if (options.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > 299)) { emitError(new errors_1.HTTPError(response, options)); return; } { const read = proxy._read; proxy._read = (...args) => { isFinished = true; proxy._read = read; return read.apply(proxy, args); }; } if (options.encoding) { proxy.setEncoding(options.encoding); } // We cannot use `stream.pipeline(...)` here, // because if we did then `output` would throw // the original error before throwing `ReadError`. response.pipe(output); response.once('error', error => { emitError(new errors_1.ReadError(error, options)); }); for (const destination of piped) { if (destination.headersSent) { continue; } for (const [key, value] of Object.entries(response.headers)) { // Got gives *decompressed* data. Overriding `content-encoding` header would result in an error. // It's not possible to decompress already decompressed data, is it? const isAllowed = options.decompress ? key !== 'content-encoding' : true; if (isAllowed) { destination.setHeader(key, value); } } destination.statusCode = response.statusCode; } proxy.emit('response', response); }); request_as_event_emitter_1.proxyEvents(proxy, emitter); emitter.on('error', (error) => proxy.emit('error', error)); const pipe = proxy.pipe.bind(proxy); const unpipe = proxy.unpipe.bind(proxy); proxy.pipe = (destination, options) => { if (isFinished) { throw new Error('Failed to pipe. The response has been emitted already.'); } pipe(destination, options); if (destination instanceof http_1.ServerResponse) { piped.add(destination); } return destination; }; proxy.unpipe = stream => { piped.delete(stream); return unpipe(stream); }; proxy.on('pipe', source => { if (source instanceof http_1.IncomingMessage) { options.headers = { ...source.headers, ...options.headers }; } }); proxy.isFromCache = undefined; return proxy; } exports.default = asStream;