1 | 'use strict';
|
2 | const EventEmitter = require('events');
|
3 | const getStream = require('get-stream');
|
4 | const is = require('@sindresorhus/is');
|
5 | const PCancelable = require('p-cancelable');
|
6 | const requestAsEventEmitter = require('./request-as-event-emitter');
|
7 | const {HTTPError, ParseError, ReadError} = require('./errors');
|
8 |
|
9 | module.exports = options => {
|
10 | const proxy = new EventEmitter();
|
11 |
|
12 | const cancelable = new PCancelable((resolve, reject, onCancel) => {
|
13 | const emitter = requestAsEventEmitter(options);
|
14 | let cancelOnRequest = false;
|
15 |
|
16 | onCancel(() => {
|
17 | cancelOnRequest = true;
|
18 | });
|
19 |
|
20 | emitter.on('request', request => {
|
21 | if (cancelOnRequest) {
|
22 | request.abort();
|
23 | }
|
24 |
|
25 | proxy.emit('request', request);
|
26 |
|
27 | const uploadComplete = () => {
|
28 | request.emit('upload-complete');
|
29 | };
|
30 |
|
31 | onCancel(() => {
|
32 | request.abort();
|
33 | });
|
34 |
|
35 | if (is.nodeStream(options.body)) {
|
36 | options.body.once('end', uploadComplete);
|
37 | options.body.pipe(request);
|
38 | options.body = undefined;
|
39 | return;
|
40 | }
|
41 |
|
42 | request.end(options.body, uploadComplete);
|
43 | });
|
44 |
|
45 | emitter.on('response', async response => {
|
46 | proxy.emit('response', response);
|
47 |
|
48 | const stream = is.null(options.encoding) ? getStream.buffer(response) : getStream(response, options);
|
49 |
|
50 | let data;
|
51 | try {
|
52 | data = await stream;
|
53 | } catch (error) {
|
54 | reject(new ReadError(error, options));
|
55 | return;
|
56 | }
|
57 |
|
58 | const {statusCode} = response;
|
59 | const limitStatusCode = options.followRedirect ? 299 : 399;
|
60 |
|
61 | response.body = data;
|
62 |
|
63 | if (options.json && response.body) {
|
64 | try {
|
65 | response.body = JSON.parse(response.body);
|
66 | } catch (error) {
|
67 | if (statusCode >= 200 && statusCode < 300) {
|
68 | const parseError = new ParseError(error, statusCode, options, data);
|
69 | Object.defineProperty(parseError, 'response', {value: response});
|
70 | reject(parseError);
|
71 | }
|
72 | }
|
73 | }
|
74 |
|
75 | if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
|
76 | const error = new HTTPError(statusCode, response.statusMessage, response.headers, options);
|
77 | Object.defineProperty(error, 'response', {value: response});
|
78 | emitter.emit('retry', error, retried => {
|
79 | if (!retried) {
|
80 | if (options.throwHttpErrors) {
|
81 | reject(error);
|
82 | return;
|
83 | }
|
84 |
|
85 | resolve(response);
|
86 | }
|
87 | });
|
88 | return;
|
89 | }
|
90 |
|
91 | resolve(response);
|
92 | });
|
93 |
|
94 | emitter.once('error', reject);
|
95 | [
|
96 | 'redirect',
|
97 | 'uploadProgress',
|
98 | 'downloadProgress'
|
99 | ].forEach(event => emitter.on(event, (...args) => proxy.emit(event, ...args)));
|
100 | });
|
101 |
|
102 | const promise = cancelable;
|
103 |
|
104 | promise.cancel = cancelable.cancel.bind(cancelable);
|
105 |
|
106 | promise.on = (name, fn) => {
|
107 | proxy.on(name, fn);
|
108 | return promise;
|
109 | };
|
110 |
|
111 | return promise;
|
112 | };
|