trouble-in-terror-town/node_modules/make-fetch-happen/lib/fetch.js
Mikolaj 2bbacbea09 did some more work on networking and removed EOS in favor of LRM
did some more work on networking and removed EOS in favor of Light Reflective Mirror
2022-05-31 15:04:31 +02:00

118 lines
3.9 KiB
JavaScript

'use strict'
const { FetchError, Request, isRedirect } = require('minipass-fetch')
const url = require('url')
const CachePolicy = require('./cache/policy.js')
const cache = require('./cache/index.js')
const remote = require('./remote.js')
// given a Request, a Response and user options
// return true if the response is a redirect that
// can be followed. we throw errors that will result
// in the fetch being rejected if the redirect is
// possible but invalid for some reason
const canFollowRedirect = (request, response, options) => {
if (!isRedirect(response.status)) {
return false
}
if (options.redirect === 'manual') {
return false
}
if (options.redirect === 'error') {
throw new FetchError(`redirect mode is set to error: ${request.url}`,
'no-redirect', { code: 'ENOREDIRECT' })
}
if (!response.headers.has('location')) {
throw new FetchError(`redirect location header missing for: ${request.url}`,
'no-location', { code: 'EINVALIDREDIRECT' })
}
if (request.counter >= request.follow) {
throw new FetchError(`maximum redirect reached at: ${request.url}`,
'max-redirect', { code: 'EMAXREDIRECT' })
}
return true
}
// given a Request, a Response, and the user's options return an object
// with a new Request and a new options object that will be used for
// following the redirect
const getRedirect = (request, response, options) => {
const _opts = { ...options }
const location = response.headers.get('location')
const redirectUrl = new url.URL(location, /^https?:/.test(location) ? undefined : request.url)
// Comment below is used under the following license:
/**
* @license
* Copyright (c) 2010-2012 Mikeal Rogers
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
// Remove authorization if changing hostnames (but not if just
// changing ports or protocols). This matches the behavior of request:
// https://github.com/request/request/blob/b12a6245/lib/redirect.js#L134-L138
if (new url.URL(request.url).hostname !== redirectUrl.hostname) {
request.headers.delete('authorization')
request.headers.delete('cookie')
}
// for POST request with 301/302 response, or any request with 303 response,
// use GET when following redirect
if (
response.status === 303 ||
(request.method === 'POST' && [301, 302].includes(response.status))
) {
_opts.method = 'GET'
_opts.body = null
request.headers.delete('content-length')
}
_opts.headers = {}
request.headers.forEach((value, key) => {
_opts.headers[key] = value
})
_opts.counter = ++request.counter
const redirectReq = new Request(url.format(redirectUrl), _opts)
return {
request: redirectReq,
options: _opts,
}
}
const fetch = async (request, options) => {
const response = CachePolicy.storable(request, options)
? await cache(request, options)
: await remote(request, options)
// if the request wasn't a GET or HEAD, and the response
// status is between 200 and 399 inclusive, invalidate the
// request url
if (!['GET', 'HEAD'].includes(request.method) &&
response.status >= 200 &&
response.status <= 399) {
await cache.invalidate(request, options)
}
if (!canFollowRedirect(request, response, options)) {
return response
}
const redirect = getRedirect(request, response, options)
return fetch(redirect.request, redirect.options)
}
module.exports = fetch