UNPKG

2.7 kBJavaScriptView Raw
1'use strict';
2const EventEmitter = require('events');
3const getStream = require('get-stream');
4const is = require('@sindresorhus/is');
5const PCancelable = require('p-cancelable');
6const requestAsEventEmitter = require('./request-as-event-emitter');
7const {HTTPError, ParseError, ReadError} = require('./errors');
8
9module.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};