UNPKG

2.73 kBJavaScriptView Raw
1"use strict";
2
3const url = require("url");
4
5const NodeFetch = require("node-fetch");
6
7module.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};