You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							100 lines
						
					
					
						
							3.7 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							100 lines
						
					
					
						
							3.7 KiB
						
					
					
				
								'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:
							 | 
						|
								  // 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')
							 | 
						|
								
							 | 
						|
								  // 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
							 |