Skip to content

Host header is not replaced if explicitly specified on 307 responses #2666

@czardoz

Description

@czardoz

Summary

If a Host header is provided to request, and if the response is a 307 with a different host, the old host is reused, which causes a maxRedirects error. The test case below reproduces the issue.

Simplest Example to Reproduce

'use strict'

var http = require('http')
var request = require('../index')
var tape = require('tape')

// test data
var redirecter = 'test1.local.omg' // always resolves to 127.0.0.1
var responder = 'test2.local.omg'  // this too.

var server = http.createServer(function (req, res) {
  if (req.headers.host.indexOf(redirecter) === 0) {
    res.setHeader('location', `http://${responder}:${port}/foo`)
    res.statusCode = 307
    return res.end('try again')
  } else if (req.headers.host.indexOf(responder) === 0) {
    res.statusCode = 200
    return res.end('ok')
  }

  res.statusCode = 404
  return res.end('not found')
})

var port

tape('setup', function (t) {
  server.listen(0, function () {
    port = this.address().port
    t.end()
  })
})

tape('307 redirect should work when host is set explicitly, but changes on redirect', function (t) {
  var redirects = 0

  request({
    url: `http://${redirecter}:${port}/foo`,
    headers: {
      Host: redirecter
    },
    followAllRedirects: true,
    followRedirect: true,
    encoding: null,
    lookup: function (hostname, options, callback) {
      callback(null, '127.0.0.1', 4)  // All hosts will resolve to 127.0.0.1
    }
  }, function (err, res, body) {
    t.equal(err, null)
    t.equal(body.toString(), 'ok')
    t.equal(redirects, 1)
    t.end()
  }).on('redirect', function () {
    redirects++
  })
})

tape('cleanup', function (t) {
  server.close(function () {
    t.end()
  })
})

Expected Behavior

The module should replace the host header with the new one.

Current Behavior

We see an error, Exceeded maxRedirects. Probably stuck in a redirect loop.

Possible Solution

Add this code in redirect.js (right after computing uriPrev):

  // remove the host header if the response is a 307 & the host has changed.
  // (the new host will be populated auto-magically).
  // For other types of redirects, it's removed automatically (see code below)
  if (response.statusCode === 307 && request.headers && uriPrev.host !== request.uri.host) {
    request.removeHeader('host')
  }

Context

Your Environment

software version
request *
node *
npm *
Operating System *

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions