@request/core
Version:
HTTP Client Library
175 lines (150 loc) • 4.51 kB
JavaScript
var url = require('url')
var utils = require('../utils')
var modules = require('../modules')
exports.request = function (req, options) {
req._redirect = true
if (options.auth && options.auth.sendImmediately === false) {
req._auth = true
}
if (typeof options.redirect !== 'object') {
options.redirect = {max: 5, followed: 0, allow: function () {return true}}
}
else {
if (options.redirect.max === undefined) {
options.redirect.max = 5
}
if (options.redirect.allow === undefined) {
options.redirect.allow = function () {return true}
}
options.redirect.followed = 0
}
}
exports.response = function (req, res, options) {
var loc = location(res, options)
if (loc && options.redirect.allow(res)) {
options.redirect.followed += 1
if (options.redirect.followed === options.redirect.max) {
// req.emit('error', new Error(
// 'redirect: Exceeded maxRedirects. ' +
// 'Probably stuck in a redirect loop ' + options.url.href))
// req._redirected = true
req._redirect = false
req._chunks = []
if (req._src) {
req._src.close()
}
return
}
storeOriginalHost(req, options)
// relative path
if (!/^https?:/.test(loc)) {
loc = url.resolve(options.url.href, loc)
}
loc = url.parse(loc)
cleanup(req, res, options, loc)
options.url = loc
if (options.proxy) {
if (utils.isTunnelEnabled(options)) {
var tunnel = modules('tunnel')
tunnel(req, res, options)
}
else {
var proxy = modules('proxy')
proxy(req, res, options)
if (options.agent && options.agent._tunnel) {
delete options.agent
}
}
}
req._redirected = true
req.emit('redirect', res)
req.emit('options', options)
req.init(utils.filter(options))
if (req._auth) {
req._chunks.forEach(function (chunk) {
req.write(chunk)
})
if (req._src) {
req._src.resume()
}
}
if (req._ended) {
req.end()
}
}
else {
req._redirect = false
// if (req._auth) {
// req._chunks = []
// if (req._src) {
// req._src.resume()
// }
// }
}
}
function location (res, options) {
var location = res.headers.get('location')
if (res.statusCode >= 300 && res.statusCode < 400 && location) {
if (options.redirect.all) {
return location
}
else if (!/PATCH|PUT|POST|DELETE/.test(options.method)) {
return location
}
}
// basic auth
else if (res.statusCode === 401) {
if (options.headers.get('authorization')) {
return options.url.path
}
}
}
function storeOriginalHost (req, options) {
if (!req._redirected) {
// Save the original host before any redirect (if it changes, we need to
// remove any authorization headers). Also remember the case of the header
// name because lots of broken servers expect Host instead of host and we
// want the caller to be able to specify this.
options.redirect.host = {
name: req._req._headerNames.host,
value: req._req._headers.host
}
}
}
function cleanup (req, res, options, location) {
// handle the case where we change protocol from https to http or vice versa
if (options.url.protocol !== location.protocol) {
req.update(location.protocol)
// delete options.agent
}
// store redirects
// self.redirects.push({
// statusCode: res.statusCode,
// location: location
// })
if (options.redirect.all &&
options.method !== 'HEAD' &&
res.statusCode !== 401 && res.statusCode !== 307) {
options.method = 'GET'
}
if (res.statusCode !== 401 && res.statusCode !== 307) {
// Remove parameters from the previous response,
// unless this is the second request
// for a server that requires digest authentication.
delete options.body
delete req._src
options.headers.remove('host')
options.headers.remove('content-type')
options.headers.remove('content-length')
// Remove authorization if changing hostnames,
// but not if just changing ports or protocols.
// This matches the behavior of curl:
// https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710
if (location.hostname !== options.redirect.host.value.split(':')[0]) {
options.headers.remove('authorization')
}
}
if (!options.redirect.removeReferer) {
options.headers.set('referer', options.url.href)
}
}