-
-
Notifications
You must be signed in to change notification settings - Fork 233
/
Copy pathhttp.js
180 lines (159 loc) · 5.06 KB
/
http.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"use strict";
const http = require("http");
const https = require("https");
const { ono } = require("@jsdevtools/ono");
const url = require("../util/url");
const { ResolverError } = require("../util/errors");
module.exports = {
/**
* The order that this resolver will run, in relation to other resolvers.
*
* @type {number}
*/
order: 200,
/**
* HTTP headers to send when downloading files.
*
* @example:
* {
* "User-Agent": "JSON Schema $Ref Parser",
* Accept: "application/json"
* }
*
* @type {object}
*/
headers: null,
/**
* HTTP request timeout (in milliseconds).
*
* @type {number}
*/
timeout: 5000, // 5 seconds
/**
* The maximum number of HTTP redirects to follow.
* To disable automatic following of redirects, set this to zero.
*
* @type {number}
*/
redirects: 5,
/**
* The `withCredentials` option of XMLHttpRequest.
* Set this to `true` if you're downloading files from a CORS-enabled server that requires authentication
*
* @type {boolean}
*/
withCredentials: false,
/**
* Determines whether this resolver can read a given file reference.
* Resolvers that return true will be tried in order, until one successfully resolves the file.
* Resolvers that return false will not be given a chance to resolve the file.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @returns {boolean}
*/
canRead (file) {
return url.isHttp(file.url);
},
/**
* Reads the given URL and returns its raw contents as a Buffer.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @returns {Promise<Buffer>}
*/
read (file) {
let u = url.parse(file.url);
if (process.browser && !u.protocol) {
// Use the protocol of the current page
u.protocol = url.parse(location.href).protocol;
}
return download(u, this);
}
};
/**
* Downloads the given file.
*
* @param {Url|string} u - The url to download (can be a parsed {@link Url} object)
* @param {object} httpOptions - The `options.resolve.http` object
* @param {number} [redirects] - The redirect URLs that have already been followed
*
* @returns {Promise<Buffer>}
* The promise resolves with the raw downloaded data, or rejects if there is an HTTP error.
*/
function download (u, httpOptions, redirects) {
return new Promise(((resolve, reject) => {
u = url.parse(u);
redirects = redirects || [];
redirects.push(u.href);
get(u, httpOptions)
.then((res) => {
if (res.statusCode >= 400) {
throw ono({ status: res.statusCode }, `HTTP ERROR ${res.statusCode}`);
}
else if (res.statusCode >= 300) {
if (redirects.length > httpOptions.redirects) {
reject(new ResolverError(ono({ status: res.statusCode },
`Error downloading ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`)));
}
else if (!res.headers.location) {
throw ono({ status: res.statusCode }, `HTTP ${res.statusCode} redirect with no location header`);
}
else {
// console.log('HTTP %d redirect %s -> %s', res.statusCode, u.href, res.headers.location);
let redirectTo = url.resolve(u, res.headers.location);
download(redirectTo, httpOptions, redirects).then(resolve, reject);
}
}
else {
resolve(res.body || Buffer.alloc(0));
}
})
.catch((err) => {
reject(new ResolverError(ono(err, `Error downloading ${u.href}`), u.href));
});
}));
}
/**
* Sends an HTTP GET request.
*
* @param {Url} u - A parsed {@link Url} object
* @param {object} httpOptions - The `options.resolve.http` object
*
* @returns {Promise<Response>}
* The promise resolves with the HTTP Response object.
*/
function get (u, httpOptions) {
return new Promise(((resolve, reject) => {
// console.log('GET', u.href);
let protocol = u.protocol === "https:" ? https : http;
let req = protocol.get({
hostname: u.hostname,
port: u.port,
path: u.path,
auth: u.auth,
protocol: u.protocol,
headers: httpOptions.headers || {},
withCredentials: httpOptions.withCredentials
});
if (typeof req.setTimeout === "function") {
req.setTimeout(httpOptions.timeout);
}
req.on("timeout", () => {
req.abort();
});
req.on("error", reject);
req.once("response", (res) => {
res.body = Buffer.alloc(0);
res.on("data", (data) => {
res.body = Buffer.concat([res.body, Buffer.from(data)]);
});
res.on("error", reject);
res.on("end", () => {
resolve(res);
});
});
}));
}