UNPKG

27 kBJavaScriptView Raw
1'use strict';
2
3function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
5var url = require('url');
6
7var stream = require('stream');
8
9var fs = require('fs');
10
11var crypto = require('crypto');
12
13var http = require('http');
14
15var zlib = require('zlib');
16
17var https = require('https');
18
19function _interopDefaultLegacy(e) {
20 return e && typeof e === 'object' && 'default' in e ? e : {
21 'default': e
22 };
23}
24
25var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
26
27var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib);
28
29var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
30
31const inElectron = (() => {
32 if (!process.versions.electron) {
33 return false;
34 }
35
36 try {
37 const electron = require('electron');
38
39 return Boolean(electron && electron.app);
40 } catch {
41 return false;
42 }
43})();
44
45const extractContentType = body => {
46 if (typeof body === 'string') {
47 return 'text/plain;charset=UTF-8';
48 }
49
50 return null;
51};
52
53const isRedirect = code => {
54 if (typeof code !== 'number') return false;
55 return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
56};
57
58const isValidTokenChar = ch => {
59 if (ch >= 94 && ch <= 122) {
60 return true;
61 }
62
63 if (ch >= 65 && ch <= 90) {
64 return true;
65 }
66
67 if (ch === 45) {
68 return true;
69 }
70
71 if (ch >= 48 && ch <= 57) {
72 return true;
73 }
74
75 if (ch === 34 || ch === 40 || ch === 41 || ch === 44) {
76 return false;
77 }
78
79 if (ch >= 33 && ch <= 46) {
80 return true;
81 }
82
83 if (ch === 124 || ch === 126) {
84 return true;
85 }
86
87 return false;
88};
89
90const checkIsHttpToken = val => {
91 if (typeof val !== 'string' || val.length === 0) {
92 return false;
93 }
94
95 if (!isValidTokenChar(val.charCodeAt(0))) {
96 return false;
97 }
98
99 const len = val.length;
100
101 if (len > 1) {
102 if (!isValidTokenChar(val.charCodeAt(1))) {
103 return false;
104 }
105
106 if (len > 2) {
107 if (!isValidTokenChar(val.charCodeAt(2))) {
108 return false;
109 }
110
111 if (len > 3) {
112 if (!isValidTokenChar(val.charCodeAt(3))) {
113 return false;
114 }
115
116 for (let i = 4; i < len; i += 1) {
117 if (!isValidTokenChar(val.charCodeAt(i))) {
118 return false;
119 }
120 }
121 }
122 }
123 }
124
125 return true;
126};
127
128const checkInvalidHeaderChar = val => {
129 if (val.length < 1) {
130 return false;
131 }
132
133 let c = val.charCodeAt(0);
134
135 if (c <= 31 && c !== 9 || c > 255 || c === 127) {
136 return true;
137 }
138
139 if (val.length < 2) {
140 return false;
141 }
142
143 c = val.charCodeAt(1);
144
145 if (c <= 31 && c !== 9 || c > 255 || c === 127) {
146 return true;
147 }
148
149 if (val.length < 3) {
150 return false;
151 }
152
153 c = val.charCodeAt(2);
154
155 if (c <= 31 && c !== 9 || c > 255 || c === 127) {
156 return true;
157 }
158
159 for (let i = 3; i < val.length; i += 1) {
160 c = val.charCodeAt(i);
161
162 if (c <= 31 && c !== 9 || c > 255 || c === 127) {
163 return true;
164 }
165 }
166
167 return false;
168};
169
170const sanitizeKey = name => {
171 if (!checkIsHttpToken(name)) {
172 throw new TypeError(`${name} is not a legal HTTP header name`);
173 }
174
175 return name.toLowerCase();
176};
177
178const sanitizeValue = value => {
179 if (checkInvalidHeaderChar(value)) {
180 throw new TypeError(`${value} is not a legal HTTP header value`);
181 }
182
183 return value;
184};
185
186class Headers {
187 constructor() {
188 let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
189
190 _defineProperty(this, "map", new Map());
191
192 _defineProperty(this, "raw", () => {
193 const result = {};
194
195 for (const [key, value] of this.map.entries()) {
196 result[key] = value;
197 }
198
199 return result;
200 });
201
202 _defineProperty(this, "append", (key, value) => {
203 const prev = this.get(key);
204
205 if (!prev) {
206 this.set(key, value);
207 } else {
208 this.set(key, Array.isArray(prev) ? [...prev, value] : [prev, value]);
209 }
210 });
211
212 _defineProperty(this, "get", key => {
213 const value = this.map.get(sanitizeKey(key));
214
215 if (typeof value === 'string') {
216 return value;
217 }
218
219 if (Array.isArray(value)) {
220 return value.join(',');
221 }
222
223 return null;
224 });
225
226 _defineProperty(this, "has", key => this.map.has(sanitizeKey(key)));
227
228 _defineProperty(this, "set", (key, value) => {
229 const data = Array.isArray(value) ? value.map(sanitizeValue) : sanitizeValue(value);
230 this.map.set(sanitizeKey(key), data);
231 });
232
233 _defineProperty(this, "delete", key => {
234 this.map.delete(sanitizeKey(key));
235 });
236
237 for (const [key, value] of Object.entries(init)) {
238 if (value) {
239 this.set(key, value);
240 }
241 }
242 }
243
244}
245
246class ElectronAdapter {
247 constructor() {
248 _defineProperty(this, "electron", require('electron'));
249 }
250
251 whenReady() {
252 return this.electron.app.whenReady();
253 }
254
255 request(options) {
256 return this.electron.net.request(options);
257 }
258
259 getDefaultSession() {
260 return this.electron.session.defaultSession;
261 }
262
263}
264
265class BlobImpl {
266 constructor(blobParts, options) {
267 _defineProperty(this, "buffer", void 0);
268
269 _defineProperty(this, "closed", void 0);
270
271 _defineProperty(this, "privateType", '');
272
273 const buffers = [];
274
275 if (blobParts) {
276 if (!blobParts || typeof blobParts !== 'object' || blobParts instanceof Date || blobParts instanceof RegExp) {
277 throw new TypeError('Blob parts must be objects that are not Dates or RegExps');
278 }
279
280 for (let i = 0, l = Number(blobParts.length); i < l; i += 1) {
281 const part = blobParts[i];
282 let buf;
283
284 if (part instanceof Buffer) {
285 buf = part;
286 } else if (part instanceof ArrayBuffer) {
287 buf = Buffer.from(new Uint8Array(part));
288 } else if (part instanceof BlobImpl) {
289 buf = part.buffer;
290 } else if (ArrayBuffer.isView(part)) {
291 buf = Buffer.from(new Uint8Array(part.buffer, part.byteOffset, part.byteLength));
292 } else {
293 buf = Buffer.from(typeof part === 'string' ? part : String(part));
294 }
295
296 buffers.push(buf);
297 }
298 }
299
300 this.buffer = Buffer.concat(buffers);
301 this.closed = false;
302 const type = options && options.type !== undefined && String(options.type).toLowerCase();
303
304 if (type && !/[^\u0020-\u007E]/.test(type)) {
305 this.privateType = type;
306 }
307 }
308
309 get size() {
310 return this.buffer.length;
311 }
312
313 get type() {
314 return this.privateType;
315 }
316
317 get content() {
318 return this.buffer;
319 }
320
321 get isClosed() {
322 return this.closed;
323 }
324
325 slice(start, end, type) {
326 const {
327 size,
328 buffer
329 } = this;
330 let relativeStart;
331 let relativeEnd;
332
333 if (start === void 0) {
334 relativeStart = 0;
335 } else if (start < 0) {
336 relativeStart = Math.max(size + start, 0);
337 } else {
338 relativeStart = Math.min(start, size);
339 }
340
341 if (end === void 0) {
342 relativeEnd = size;
343 } else if (end < 0) {
344 relativeEnd = Math.max(size + end, 0);
345 } else {
346 relativeEnd = Math.min(end, size);
347 }
348
349 const span = Math.max(relativeEnd - relativeStart, 0);
350 const slicedBuffer = buffer.slice(relativeStart, relativeStart + span);
351 const blob = new BlobImpl([], {
352 type: type || this.type
353 });
354 blob.buffer = slicedBuffer;
355 blob.closed = this.closed;
356 return blob;
357 }
358
359 close() {
360 this.closed = true;
361 }
362
363 toString() {
364 return '[object Blob]';
365 }
366
367}
368
369const newError = (message, code) => {
370 const error = new Error(message);
371 error.code = code;
372 return error;
373};
374
375class DigestTransform extends stream.Transform {
376 get actual() {
377 return this._actual;
378 }
379
380 constructor(options) {
381 super();
382
383 _defineProperty(this, "digester", void 0);
384
385 _defineProperty(this, "_actual", null);
386
387 _defineProperty(this, "expected", void 0);
388
389 _defineProperty(this, "algorithm", void 0);
390
391 _defineProperty(this, "encoding", void 0);
392
393 _defineProperty(this, "isValidateOnEnd", true);
394
395 const {
396 expected,
397 algorithm = 'md5',
398 encoding = 'base64'
399 } = options;
400 this.expected = expected;
401 this.algorithm = algorithm;
402 this.encoding = encoding;
403 this.digester = crypto.createHash(algorithm);
404 }
405
406 _transform(chunk, encoding, callback) {
407 this.digester.update(chunk);
408 callback(null, chunk);
409 }
410
411 _flush(callback) {
412 this._actual = this.digester.digest(this.encoding);
413
414 if (this.isValidateOnEnd) {
415 try {
416 this.validate();
417 } catch (e) {
418 callback(e);
419 return;
420 }
421 }
422
423 callback(null);
424 }
425
426 validate() {
427 if (this._actual == null) {
428 throw newError('Not finished yet', 'ERR_STREAM_NOT_FINISHED');
429 }
430
431 if (this._actual !== this.expected) {
432 throw newError(`${this.algorithm} checksum mismatch, expected ${this.expected}, got ${this._actual}`, 'ERR_CHECKSUM_MISMATCH');
433 }
434
435 return null;
436 }
437
438}
439
440class ProgressCallbackTransform extends stream.Transform {
441 constructor(total, onProgress) {
442 super();
443
444 _defineProperty(this, "start", Date.now());
445
446 _defineProperty(this, "transferred", 0);
447
448 _defineProperty(this, "delta", 0);
449
450 _defineProperty(this, "total", void 0);
451
452 _defineProperty(this, "onProgress", void 0);
453
454 _defineProperty(this, "nextUpdate", this.start + 1000);
455
456 this.total = total;
457 this.onProgress = onProgress;
458 }
459
460 _transform(chunk, encoding, callback) {
461 const chunkLength = chunk.length;
462 this.transferred += chunkLength;
463 this.delta += chunkLength;
464
465 if (this.total >= this.transferred) {
466 const now = Date.now();
467
468 if (now >= this.nextUpdate) {
469 this.nextUpdate = now + 1000;
470 this.onProgress({
471 total: this.total,
472 delta: this.delta,
473 transferred: this.transferred,
474 percent: this.transferred / this.total * 100,
475 bytesPerSecond: Math.round(this.transferred / ((now - this.start) / 1000))
476 });
477 this.delta = 0;
478 }
479 }
480
481 callback(null, chunk);
482 }
483
484 _flush(callback) {
485 const {
486 total,
487 transferred
488 } = this;
489 const totalChunk = transferred > total ? transferred : total;
490 this.onProgress({
491 total: totalChunk,
492 delta: this.delta,
493 transferred: totalChunk,
494 percent: 100,
495 bytesPerSecond: Math.round(this.transferred / ((Date.now() - this.start) / 1000))
496 });
497 this.delta = 0;
498 callback(null);
499 }
500
501}
502
503class ResponseImpl {
504 constructor(body, options) {
505 _defineProperty(this, "disturbed", void 0);
506
507 _defineProperty(this, "body", void 0);
508
509 _defineProperty(this, "config", void 0);
510
511 _defineProperty(this, "consumeResponse", () => {
512 const {
513 requestURL,
514 size
515 } = this.config;
516
517 if (this.disturbed) {
518 return Promise.reject(new Error(`Response used already for: ${requestURL}`));
519 }
520
521 this.disturbed = true;
522
523 if (this.body === null) {
524 return Promise.resolve(Buffer.alloc(0));
525 }
526
527 if (typeof this.body === 'string') {
528 return Promise.resolve(Buffer.from(this.body));
529 }
530
531 if (this.body instanceof BlobImpl) {
532 return Promise.resolve(this.body.content);
533 }
534
535 if (Buffer.isBuffer(this.body)) {
536 return Promise.resolve(this.body);
537 }
538
539 if (!(this.body instanceof stream.Stream)) {
540 return Promise.resolve(Buffer.alloc(0));
541 }
542
543 const accum = [];
544 let accumBytes = 0;
545 let abort = false;
546 return new Promise((resolve, reject) => {
547 this.body.on("error", err => {
548 reject(new Error(`Invalid response body while trying to fetch ${requestURL}: ${err.message}`));
549 });
550 this.body.on("data", chunk => {
551 if (abort || chunk === null) {
552 return;
553 }
554
555 if (size && accumBytes + chunk.length > size) {
556 abort = true;
557 reject(new Error(`Content size at ${requestURL} over limit: ${size}`));
558 this.body.emit("cancel-request");
559 return;
560 }
561
562 accumBytes += chunk.length;
563 accum.push(chunk);
564 });
565 this.body.on("end", () => {
566 if (abort) {
567 return;
568 }
569
570 resolve(Buffer.concat(accum, accumBytes));
571 });
572 });
573 });
574
575 _defineProperty(this, "download", async (fileOut, onProgress, validateOptions) => {
576 const feedStreams = [];
577
578 if (typeof onProgress === 'function') {
579 const contentLength = Number(this.config.headers.get("content-length"));
580 feedStreams.push(new ProgressCallbackTransform(contentLength, onProgress));
581 }
582
583 if (validateOptions) {
584 feedStreams.push(new DigestTransform(validateOptions));
585 }
586
587 feedStreams.push(fileOut);
588 return new Promise((resolve, reject) => {
589 let lastStream = this.stream;
590
591 for (const stream of feedStreams) {
592 stream.on('error', error => {
593 reject(error);
594 });
595 lastStream = lastStream.pipe(stream);
596 }
597
598 fileOut.once('finish', () => {
599 if (fileOut instanceof fs.WriteStream && typeof fileOut.close === 'function') {
600 fileOut.close();
601 }
602
603 resolve();
604 });
605 });
606 });
607
608 _defineProperty(this, "arrayBuffer", async () => {
609 const buf = await this.consumeResponse();
610 return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
611 });
612
613 _defineProperty(this, "blob", async () => {
614 const contentType = this.config.headers.get("content-Type") || '';
615 const buffer = await this.consumeResponse();
616 const blob = new BlobImpl([buffer], {
617 type: contentType.toLowerCase()
618 });
619 return blob;
620 });
621
622 _defineProperty(this, "text", async () => {
623 const buffer = await this.consumeResponse();
624 return buffer.toString();
625 });
626
627 _defineProperty(this, "json", async () => {
628 const buffer = await this.consumeResponse();
629 const text = buffer.toString();
630
631 try {
632 return JSON.parse(text);
633 } catch {
634 throw new Error(text);
635 }
636 });
637
638 _defineProperty(this, "buffer", () => {
639 return this.consumeResponse();
640 });
641
642 this.body = body;
643 this.config = options;
644 this.disturbed = false;
645 }
646
647 get ok() {
648 const {
649 statusCode
650 } = this.config;
651 return statusCode >= 200 && statusCode < 300;
652 }
653
654 get headers() {
655 return this.config.headers.raw();
656 }
657
658 get stream() {
659 if (this.disturbed) {
660 throw new Error(`Response used already for: ${this.config.requestURL}`);
661 }
662
663 return this.body;
664 }
665
666}
667
668class ElectronRequestClient {
669 constructor(_options) {
670 _defineProperty(this, "electronAdapter", inElectron ? new ElectronAdapter() : null);
671
672 _defineProperty(this, "options", void 0);
673
674 _defineProperty(this, "redirectCount", 0);
675
676 _defineProperty(this, "timeoutId", null);
677
678 _defineProperty(this, "clearRequestTimeout", () => {
679 if (this.timeoutId === null) return;
680 clearTimeout(this.timeoutId);
681 this.timeoutId = null;
682 });
683
684 _defineProperty(this, "createRequest", async () => {
685 if (this.electronAdapter === null) {
686 throw new Error('Error in environmental judgment');
687 }
688
689 await this.electronAdapter.whenReady();
690 const {
691 requestURL,
692 parsedURL: {
693 protocol,
694 host,
695 hostname,
696 port,
697 pathname,
698 origin,
699 search
700 },
701 method,
702 session,
703 useSessionCookies,
704 headers
705 } = this.options;
706 const options = {
707 method,
708 url: `${requestURL}${pathname}${search || ''}`,
709 session: session || this.electronAdapter.getDefaultSession(),
710 useSessionCookies,
711 protocol,
712 host,
713 hostname,
714 origin,
715 port: Number(port)
716 };
717 const clientRequest = this.electronAdapter.request(options);
718
719 for (const [key, value] of Object.entries(headers.raw())) {
720 if (Array.isArray(value)) {
721 for (const v of value) {
722 clientRequest.setHeader(key, v);
723 }
724 } else {
725 clientRequest.setHeader(key, value);
726 }
727 }
728
729 return clientRequest;
730 });
731
732 _defineProperty(this, "send", async () => {
733 const {
734 method,
735 followRedirect,
736 maxRedirectCount,
737 requestURL,
738 parsedURL,
739 size,
740 username,
741 password,
742 timeout,
743 body: requestBody
744 } = this.options;
745 const clientRequest = await this.createRequest();
746
747 const cancelRequest = () => {
748 clientRequest.abort();
749 };
750
751 const writeToRequest = () => {
752 if (requestBody === null) {
753 clientRequest.end();
754 } else if (requestBody instanceof stream.Stream) {
755 requestBody.pipe(new stream.PassThrough()).pipe(clientRequest);
756 } else {
757 clientRequest.write(requestBody);
758 clientRequest.end();
759 }
760 };
761
762 const bindRequestEvent = (onFulfilled, onRejected) => {
763 if (timeout) {
764 this.timeoutId = setTimeout(() => {
765 onRejected(new Error(`Electron request timeout in ${timeout} ms`));
766 }, timeout);
767 }
768
769 clientRequest.on("error", onRejected);
770 clientRequest.on("abort", () => {
771 onRejected(new Error('Electron request was aborted by the server'));
772 });
773 clientRequest.on("login", (authInfo, callback) => {
774 if (username && password) {
775 callback(username, password);
776 } else {
777 onRejected(new Error(`Login event received from ${authInfo.host} but no credentials provided`));
778 }
779 });
780 clientRequest.on("response", res => {
781 this.clearRequestTimeout();
782 const {
783 statusCode = 200,
784 headers: responseHeaders
785 } = res;
786 const headers = new Headers(responseHeaders);
787
788 if (isRedirect(statusCode) && followRedirect) {
789 if (maxRedirectCount && this.redirectCount >= maxRedirectCount) {
790 onRejected(new Error(`Maximum redirect reached at: ${requestURL}`));
791 }
792
793 if (!headers.get("location")) {
794 onRejected(new Error(`Redirect location header missing at: ${requestURL}`));
795 }
796
797 if (statusCode === 303 || (statusCode === 301 || statusCode === 302) && method === "POST") {
798 this.options.method = "GET";
799 this.options.body = null;
800 this.options.headers.delete("content-length");
801 }
802
803 this.redirectCount += 1;
804 this.options.parsedURL = new url.URL(String(headers.get("location")), parsedURL.toString());
805 onFulfilled(this.send());
806 }
807
808 const responseBody = stream.pipeline(res, new stream.PassThrough(), error => {
809 if (error !== null) {
810 onRejected(error);
811 }
812 });
813 responseBody.on("cancel-request", cancelRequest);
814 onFulfilled(new ResponseImpl(responseBody, {
815 requestURL,
816 statusCode,
817 headers,
818 size
819 }));
820 });
821 };
822
823 return new Promise((resolve, reject) => {
824 const onRejected = reason => {
825 this.clearRequestTimeout();
826 cancelRequest();
827 reject(reason);
828 };
829
830 bindRequestEvent(resolve, onRejected);
831 writeToRequest();
832 });
833 });
834
835 this.options = _options;
836 }
837
838}
839
840const adapterForHttp = protocol => {
841 if (protocol === 'http:') {
842 return http__default["default"];
843 }
844
845 if (protocol === 'https:') {
846 return https__default["default"];
847 }
848
849 throw new TypeError('Only HTTP(S) protocols are supported');
850};
851
852class NativeRequestClient {
853 constructor(_options2) {
854 _defineProperty(this, "options", void 0);
855
856 _defineProperty(this, "redirectCount", 0);
857
858 _defineProperty(this, "createRequest", () => {
859 const {
860 parsedURL: {
861 protocol,
862 host,
863 hostname,
864 port,
865 pathname,
866 search
867 },
868 headers,
869 method
870 } = this.options;
871 const options = {
872 protocol,
873 host,
874 hostname,
875 port,
876 path: `${pathname}${search || ''}`,
877 headers: headers.raw(),
878 method
879 };
880 return adapterForHttp(protocol).request(options);
881 });
882
883 _defineProperty(this, "send", () => {
884 const {
885 method,
886 followRedirect,
887 maxRedirectCount,
888 requestURL,
889 parsedURL,
890 size,
891 timeout,
892 body: requestBody
893 } = this.options;
894 const clientRequest = this.createRequest();
895
896 const cancelRequest = () => {
897 clientRequest.destroy();
898 };
899
900 const writeToRequest = () => {
901 if (requestBody === null) {
902 clientRequest.end();
903 } else if (requestBody instanceof stream.Stream) {
904 requestBody.pipe(clientRequest);
905 } else {
906 clientRequest.write(requestBody);
907 clientRequest.end();
908 }
909 };
910
911 const bindRequestEvent = (onFulfilled, onRejected) => {
912 if (timeout) {
913 clientRequest.setTimeout(timeout, () => {
914 onRejected(new Error(`NodeJS request timeout in ${timeout} ms`));
915 });
916 }
917
918 clientRequest.on("error", onRejected);
919 clientRequest.on("abort", () => {
920 onRejected(new Error('NodeJS request was aborted by the server'));
921 });
922 clientRequest.on("response", res => {
923 const {
924 statusCode = 200,
925 headers: responseHeaders
926 } = res;
927 const headers = new Headers(responseHeaders);
928
929 if (isRedirect(statusCode) && followRedirect) {
930 if (maxRedirectCount && this.redirectCount >= maxRedirectCount) {
931 onRejected(new Error(`Maximum redirect reached at: ${requestURL}`));
932 }
933
934 if (!headers.get("location")) {
935 onRejected(new Error(`Redirect location header missing at: ${requestURL}`));
936 }
937
938 if (statusCode === 303 || (statusCode === 301 || statusCode === 302) && method === "POST") {
939 this.options.method = "GET";
940 this.options.body = null;
941 this.options.headers.delete("content-length");
942 }
943
944 this.redirectCount += 1;
945 this.options.parsedURL = new url.URL(String(headers.get("location")), parsedURL.toString());
946 onFulfilled(this.send());
947 }
948
949 const pumpCallback = error => {
950 if (error !== null) {
951 onRejected(error);
952 }
953 };
954
955 let responseBody = stream.pipeline(res, new stream.PassThrough(), pumpCallback);
956 responseBody.on("cancel-request", cancelRequest);
957
958 const resolveResponse = () => {
959 onFulfilled(new ResponseImpl(responseBody, {
960 requestURL,
961 statusCode,
962 headers,
963 size
964 }));
965 };
966
967 const codings = headers.get("content-encoding");
968
969 if (method !== "HEAD" && codings !== null && statusCode !== 204 && statusCode !== 304) {
970 switch (codings) {
971 case "br":
972 responseBody = stream.pipeline(responseBody, zlib__default["default"].createBrotliDecompress(), pumpCallback);
973 break;
974
975 case "gzip":
976 case `x-${"gzip"}`:
977 responseBody = stream.pipeline(responseBody, zlib__default["default"].createGunzip(), pumpCallback);
978 break;
979
980 case "deflate":
981 case `x-${"deflate"}`:
982 stream.pipeline(res, new stream.PassThrough(), pumpCallback).once('data', chunk => {
983 if ((chunk[0] & 0x0f) === 0x08) {
984 responseBody = stream.pipeline(responseBody, zlib__default["default"].createInflate(), pumpCallback);
985 } else {
986 responseBody = stream.pipeline(responseBody, zlib__default["default"].createInflateRaw(), pumpCallback);
987 }
988
989 resolveResponse();
990 });
991 return;
992 }
993 }
994
995 resolveResponse();
996 });
997 };
998
999 return new Promise((resolve, reject) => {
1000 const onRejected = reason => {
1001 cancelRequest();
1002 reject(reason);
1003 };
1004
1005 bindRequestEvent(resolve, onRejected);
1006 writeToRequest();
1007 });
1008 });
1009
1010 this.options = _options2;
1011 }
1012
1013}
1014
1015const DEFAULT_OPTIONS = {
1016 method: 'GET',
1017 body: null,
1018 followRedirect: true,
1019 maxRedirectCount: 20,
1020 timeout: 0,
1021 size: 0,
1022 useSessionCookies: true,
1023 useNative: false
1024};
1025const SUPPORTED_COMPRESSIONS = ["gzip", "deflate", "br"];
1026
1027const getRequestOptions = constructorOptions => {
1028 const options = { ...DEFAULT_OPTIONS,
1029 ...constructorOptions
1030 };
1031 const method = options.method.toUpperCase();
1032 const {
1033 body,
1034 requestURL,
1035 query,
1036 headers: headerOptions
1037 } = options;
1038
1039 if (body !== null && (method === "GET" || method === "HEAD")) {
1040 throw new TypeError('Request with GET/HEAD method cannot have body');
1041 }
1042
1043 const parsedURL = new url.URL(requestURL);
1044
1045 if (!parsedURL.protocol || !parsedURL.hostname) {
1046 throw new TypeError('Only absolute URLs are supported');
1047 }
1048
1049 if (!/^https?:$/.test(parsedURL.protocol)) {
1050 throw new TypeError('Only HTTP(S) protocols are supported');
1051 }
1052
1053 if (query) {
1054 for (const [queryKey, queryValue] of Object.entries(query)) {
1055 parsedURL.searchParams.append(queryKey, queryValue);
1056 }
1057 }
1058
1059 const headers = new Headers(headerOptions);
1060 headers.delete("content-length");
1061 headers.set("accept-encoding", SUPPORTED_COMPRESSIONS.join(','));
1062
1063 if (!headers.has("accept")) {
1064 headers.set("accept", '*/*');
1065 }
1066
1067 if (!headers.has("connection")) {
1068 headers.set("connection", 'close');
1069 }
1070
1071 if (body && !headers.has("content-Type")) {
1072 const contentType = extractContentType(body);
1073
1074 if (contentType) {
1075 headers.append("content-Type", contentType);
1076 }
1077 }
1078
1079 return { ...options,
1080 method,
1081 parsedURL,
1082 headers
1083 };
1084};
1085
1086class Request {
1087 constructor(constructorOptions) {
1088 _defineProperty(this, "client", void 0);
1089
1090 _defineProperty(this, "send", () => {
1091 return this.client.send();
1092 });
1093
1094 const options = getRequestOptions(constructorOptions);
1095 this.client = !options.useNative && inElectron ? new ElectronRequestClient(options) : new NativeRequestClient(options);
1096 }
1097
1098}
1099
1100const main = function (requestURL) {
1101 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1102 const request = new Request({
1103 requestURL,
1104 ...options
1105 });
1106 return request.send();
1107};
1108
1109module.exports = main;