| "use strict"; |
| var __importDefault = (this && this.__importDefault) || function (mod) { |
| return (mod && mod.__esModule) ? mod : { "default": mod }; |
| }; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.http = void 0; |
| const http_1 = __importDefault(require("http")); |
| const https_1 = __importDefault(require("https")); |
| const events_1 = require("events"); |
| const debug_1 = __importDefault(require("debug")); |
| const http_error_1 = __importDefault(require("./http-error")); |
| const notfound_1 = __importDefault(require("./notfound")); |
| const notmodified_1 = __importDefault(require("./notmodified")); |
| const debug = (0, debug_1.default)('get-uri:http'); |
| /** |
| * Returns a Readable stream from an "http:" URI. |
| */ |
| const http = async (url, opts = {}) => { |
| debug('GET %o', url.href); |
| const cache = getCache(url, opts.cache); |
| // first check the previous Expires and/or Cache-Control headers |
| // of a previous response if a `cache` was provided |
| if (cache && isFresh(cache) && typeof cache.statusCode === 'number') { |
| // check for a 3xx "redirect" status code on the previous cache |
| const type = (cache.statusCode / 100) | 0; |
| if (type === 3 && cache.headers.location) { |
| debug('cached redirect'); |
| throw new Error('TODO: implement cached redirects!'); |
| } |
| // otherwise we assume that it's the destination endpoint, |
| // since there's nowhere else to redirect to |
| throw new notmodified_1.default(); |
| } |
| // 5 redirects allowed by default |
| const maxRedirects = typeof opts.maxRedirects === 'number' ? opts.maxRedirects : 5; |
| debug('allowing %o max redirects', maxRedirects); |
| let mod; |
| if (opts.http) { |
| // the `https` module passed in from the "http.js" file |
| mod = opts.http; |
| debug('using secure `https` core module'); |
| } |
| else { |
| mod = http_1.default; |
| debug('using `http` core module'); |
| } |
| const options = { ...opts }; |
| // add "cache validation" headers if a `cache` was provided |
| if (cache) { |
| if (!options.headers) { |
| options.headers = {}; |
| } |
| const lastModified = cache.headers['last-modified']; |
| if (lastModified) { |
| options.headers['If-Modified-Since'] = lastModified; |
| debug('added "If-Modified-Since" request header: %o', lastModified); |
| } |
| const etag = cache.headers.etag; |
| if (etag) { |
| options.headers['If-None-Match'] = etag; |
| debug('added "If-None-Match" request header: %o', etag); |
| } |
| } |
| const req = mod.get(url, options); |
| const [res] = await (0, events_1.once)(req, 'response'); |
| const code = res.statusCode || 0; |
| // assign a Date to this response for the "Cache-Control" delta calculation |
| res.date = Date.now(); |
| res.parsed = url; |
| debug('got %o response status code', code); |
| // any 2xx response is a "success" code |
| const type = (code / 100) | 0; |
| // check for a 3xx "redirect" status code |
| const location = res.headers.location; |
| if (type === 3 && location) { |
| if (!opts.redirects) |
| opts.redirects = []; |
| const redirects = opts.redirects; |
| if (redirects.length < maxRedirects) { |
| debug('got a "redirect" status code with Location: %o', location); |
| // flush this response - we're not going to use it |
| res.resume(); |
| // hang on to this Response object for the "redirects" Array |
| redirects.push(res); |
| const newUri = new URL(location, url.href); |
| debug('resolved redirect URL: %o', newUri.href); |
| const left = maxRedirects - redirects.length; |
| debug('%o more redirects allowed after this one', left); |
| // check if redirecting to a different protocol |
| if (newUri.protocol !== url.protocol) { |
| opts.http = newUri.protocol === 'https:' ? https_1.default : undefined; |
| } |
| return (0, exports.http)(newUri, opts); |
| } |
| } |
| // if we didn't get a 2xx "success" status code, then create an Error object |
| if (type !== 2) { |
| res.resume(); |
| if (code === 304) { |
| throw new notmodified_1.default(); |
| } |
| else if (code === 404) { |
| throw new notfound_1.default(); |
| } |
| // other HTTP-level error |
| throw new http_error_1.default(code); |
| } |
| if (opts.redirects) { |
| // store a reference to the "redirects" Array on the Response object so that |
| // they can be inspected during a subsequent call to GET the same URI |
| res.redirects = opts.redirects; |
| } |
| return res; |
| }; |
| exports.http = http; |
| /** |
| * Returns `true` if the provided cache's "freshness" is valid. That is, either |
| * the Cache-Control header or Expires header values are still within the allowed |
| * time period. |
| * |
| * @return {Boolean} |
| * @api private |
| */ |
| function isFresh(cache) { |
| let fresh = false; |
| let expires = parseInt(cache.headers.expires || '', 10); |
| const cacheControl = cache.headers['cache-control']; |
| if (cacheControl) { |
| // for Cache-Control rules, see: http://www.mnot.net/cache_docs/#CACHE-CONTROL |
| debug('Cache-Control: %o', cacheControl); |
| const parts = cacheControl.split(/,\s*?\b/); |
| for (let i = 0; i < parts.length; i++) { |
| const part = parts[i]; |
| const subparts = part.split('='); |
| const name = subparts[0]; |
| switch (name) { |
| case 'max-age': |
| expires = |
| (cache.date || 0) + parseInt(subparts[1], 10) * 1000; |
| fresh = Date.now() < expires; |
| if (fresh) { |
| debug('cache is "fresh" due to previous %o Cache-Control param', part); |
| } |
| return fresh; |
| case 'must-revalidate': |
| // XXX: what we supposed to do here? |
| break; |
| case 'no-cache': |
| case 'no-store': |
| debug('cache is "stale" due to explicit %o Cache-Control param', name); |
| return false; |
| default: |
| // ignore unknown cache value |
| break; |
| } |
| } |
| } |
| else if (expires) { |
| // for Expires rules, see: http://www.mnot.net/cache_docs/#EXPIRES |
| debug('Expires: %o', expires); |
| fresh = Date.now() < expires; |
| if (fresh) { |
| debug('cache is "fresh" due to previous Expires response header'); |
| } |
| return fresh; |
| } |
| return false; |
| } |
| /** |
| * Attempts to return a previous Response object from a previous GET call to the |
| * same URI. |
| * |
| * @api private |
| */ |
| function getCache(url, cache) { |
| if (cache) { |
| if (cache.parsed && cache.parsed.href === url.href) { |
| return cache; |
| } |
| if (cache.redirects) { |
| for (let i = 0; i < cache.redirects.length; i++) { |
| const c = getCache(url, cache.redirects[i]); |
| if (c) { |
| return c; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| //# sourceMappingURL=http.js.map |