| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- "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);
- });
- });
- }));
- }
|