1 | "use strict";
|
2 |
|
3 | const url = require("url");
|
4 |
|
5 | const NodeFetch = require("node-fetch");
|
6 |
|
7 | module.exports = function Fetch(agent, context) {
|
8 | const stack = [];
|
9 | const defaultRequestHeaders = (context.request && context.request.header) || {};
|
10 |
|
11 | fetching._pendingRequests = stack;
|
12 | return fetching;
|
13 |
|
14 | function fetching(uri, ...args) {
|
15 | const prom = fetch(uri, ...args);
|
16 |
|
17 | const stackedProm = prom.then(() => {
|
18 | stack.pop();
|
19 | }).catch(() => {
|
20 | stack.pop();
|
21 | });
|
22 | stack.push(stackedProm);
|
23 |
|
24 | return prom;
|
25 | }
|
26 |
|
27 | function fetch(uri, options = {}) {
|
28 | if (isLocalResource(uri)) return getRelative(uri, options);
|
29 |
|
30 | return NodeFetch(uri, {...options, redirect: "manual"}).then((res) => {
|
31 | const statusCode = res.status;
|
32 |
|
33 | const setCookies = res.headers.get("set-cookie");
|
34 | if (setCookies) {
|
35 | agent.jar.setCookies(setCookies.split(","), url.parse(res.url).host);
|
36 | }
|
37 |
|
38 | if (options.redirect !== "manual") {
|
39 | if ([301, 302, 303].includes(statusCode)) {
|
40 | return fetch(res.headers.get("location"));
|
41 | }
|
42 |
|
43 | if ([307, 308].includes(statusCode)) {
|
44 | return fetch(res.headers.get("location"), options);
|
45 | }
|
46 | }
|
47 |
|
48 | return res;
|
49 | });
|
50 | }
|
51 |
|
52 | function isLocalResource(uri) {
|
53 | if (uri.startsWith("/")) return true;
|
54 | return url.parse(uri).host === defaultRequestHeaders.host;
|
55 | }
|
56 |
|
57 | function getRelative(uri, options = {}) {
|
58 | let req;
|
59 |
|
60 | uri = url.parse(uri).path;
|
61 |
|
62 | if (options.method === "POST") {
|
63 | req = agent.post(uri).send(options.body);
|
64 | } else if (options.method === "HEAD") {
|
65 | req = agent.head(uri);
|
66 | } else {
|
67 | req = agent.get(uri);
|
68 | }
|
69 |
|
70 | const reqHeaders = defaultRequestHeaders;
|
71 | if (options.headers) {
|
72 | Object.assign(reqHeaders, options.headers);
|
73 | }
|
74 | Object.keys(reqHeaders).forEach((header) => {
|
75 | req.set(header, reqHeaders[header]);
|
76 | });
|
77 |
|
78 | const cookie = agent.jar.getCookies({path: uri, domain: defaultRequestHeaders.host}).toValueString();
|
79 | if (cookie) {
|
80 | req.set("cookie", cookie);
|
81 | }
|
82 |
|
83 | return req.then((res) => {
|
84 | const statusCode = res.statusCode;
|
85 |
|
86 | if (options.redirect !== "manual") {
|
87 | if ([301, 302, 303].includes(statusCode)) {
|
88 | return fetch(res.headers.location);
|
89 | }
|
90 |
|
91 | if ([307, 308].includes(statusCode)) {
|
92 | return fetch(res.headers.location, options);
|
93 | }
|
94 | }
|
95 |
|
96 | return {
|
97 | ok: statusCode >= 200 && statusCode < 300,
|
98 | status: statusCode,
|
99 | headers: new Map(Object.entries(res.headers)),
|
100 | text() {
|
101 | return Promise.resolve(res.text);
|
102 | },
|
103 | json() {
|
104 | return Promise.resolve(res.body);
|
105 | },
|
106 | };
|
107 | });
|
108 | }
|
109 | };
|