123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- 'use strict';
- const EventEmitter = require('events');
- const getStream = require('get-stream');
- const is = require('@sindresorhus/is');
- const PCancelable = require('p-cancelable');
- const requestAsEventEmitter = require('./request-as-event-emitter');
- const {HTTPError, ParseError, ReadError} = require('./errors');
- const {options: mergeOptions} = require('./merge');
- const {reNormalize} = require('./normalize-arguments');
-
- const asPromise = options => {
- const proxy = new EventEmitter();
-
- const promise = new PCancelable((resolve, reject, onCancel) => {
- const emitter = requestAsEventEmitter(options);
-
- onCancel(emitter.abort);
-
- emitter.on('response', async response => {
- proxy.emit('response', response);
-
- const stream = is.null(options.encoding) ? getStream.buffer(response) : getStream(response, options);
-
- let data;
- try {
- data = await stream;
- } catch (error) {
- reject(new ReadError(error, options));
- return;
- }
-
- const limitStatusCode = options.followRedirect ? 299 : 399;
-
- response.body = data;
-
- try {
- for (const [index, hook] of Object.entries(options.hooks.afterResponse)) {
- // eslint-disable-next-line no-await-in-loop
- response = await hook(response, updatedOptions => {
- updatedOptions = reNormalize(mergeOptions(options, {
- ...updatedOptions,
- retry: 0,
- throwHttpErrors: false
- }));
-
- // Remove any further hooks for that request, because we we'll call them anyway.
- // The loop continues. We don't want duplicates (asPromise recursion).
- updatedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);
-
- return asPromise(updatedOptions);
- });
- }
- } catch (error) {
- reject(error);
- return;
- }
-
- const {statusCode} = response;
-
- if (options.json && response.body) {
- try {
- response.body = JSON.parse(response.body);
- } catch (error) {
- if (statusCode >= 200 && statusCode < 300) {
- const parseError = new ParseError(error, statusCode, options, data);
- Object.defineProperty(parseError, 'response', {value: response});
- reject(parseError);
- return;
- }
- }
- }
-
- if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
- const error = new HTTPError(response, options);
- Object.defineProperty(error, 'response', {value: response});
- if (emitter.retry(error) === false) {
- if (options.throwHttpErrors) {
- reject(error);
- return;
- }
-
- resolve(response);
- }
-
- return;
- }
-
- resolve(response);
- });
-
- emitter.once('error', reject);
- [
- 'request',
- 'redirect',
- 'uploadProgress',
- 'downloadProgress'
- ].forEach(event => emitter.on(event, (...args) => proxy.emit(event, ...args)));
- });
-
- promise.on = (name, fn) => {
- proxy.on(name, fn);
- return promise;
- };
-
- return promise;
- };
-
- module.exports = asPromise;
|