twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

redirect.js (4635B)


      1 'use strict'
      2 
      3 var url = require('url')
      4 var isUrl = /^https?:/
      5 
      6 function Redirect (request) {
      7   this.request = request
      8   this.followRedirect = true
      9   this.followRedirects = true
     10   this.followAllRedirects = false
     11   this.followOriginalHttpMethod = false
     12   this.allowRedirect = function () { return true }
     13   this.maxRedirects = 10
     14   this.redirects = []
     15   this.redirectsFollowed = 0
     16   this.removeRefererHeader = false
     17 }
     18 
     19 Redirect.prototype.onRequest = function (options) {
     20   var self = this
     21 
     22   if (options.maxRedirects !== undefined) {
     23     self.maxRedirects = options.maxRedirects
     24   }
     25   if (typeof options.followRedirect === 'function') {
     26     self.allowRedirect = options.followRedirect
     27   }
     28   if (options.followRedirect !== undefined) {
     29     self.followRedirects = !!options.followRedirect
     30   }
     31   if (options.followAllRedirects !== undefined) {
     32     self.followAllRedirects = options.followAllRedirects
     33   }
     34   if (self.followRedirects || self.followAllRedirects) {
     35     self.redirects = self.redirects || []
     36   }
     37   if (options.removeRefererHeader !== undefined) {
     38     self.removeRefererHeader = options.removeRefererHeader
     39   }
     40   if (options.followOriginalHttpMethod !== undefined) {
     41     self.followOriginalHttpMethod = options.followOriginalHttpMethod
     42   }
     43 }
     44 
     45 Redirect.prototype.redirectTo = function (response) {
     46   var self = this
     47   var request = self.request
     48 
     49   var redirectTo = null
     50   if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) {
     51     var location = response.caseless.get('location')
     52     request.debug('redirect', location)
     53 
     54     if (self.followAllRedirects) {
     55       redirectTo = location
     56     } else if (self.followRedirects) {
     57       switch (request.method) {
     58         case 'PATCH':
     59         case 'PUT':
     60         case 'POST':
     61         case 'DELETE':
     62           // Do not follow redirects
     63           break
     64         default:
     65           redirectTo = location
     66           break
     67       }
     68     }
     69   } else if (response.statusCode === 401) {
     70     var authHeader = request._auth.onResponse(response)
     71     if (authHeader) {
     72       request.setHeader('authorization', authHeader)
     73       redirectTo = request.uri
     74     }
     75   }
     76   return redirectTo
     77 }
     78 
     79 Redirect.prototype.onResponse = function (response) {
     80   var self = this
     81   var request = self.request
     82 
     83   var redirectTo = self.redirectTo(response)
     84   if (!redirectTo || !self.allowRedirect.call(request, response)) {
     85     return false
     86   }
     87 
     88   request.debug('redirect to', redirectTo)
     89 
     90   // ignore any potential response body.  it cannot possibly be useful
     91   // to us at this point.
     92   // response.resume should be defined, but check anyway before calling. Workaround for browserify.
     93   if (response.resume) {
     94     response.resume()
     95   }
     96 
     97   if (self.redirectsFollowed >= self.maxRedirects) {
     98     request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href))
     99     return false
    100   }
    101   self.redirectsFollowed += 1
    102 
    103   if (!isUrl.test(redirectTo)) {
    104     redirectTo = url.resolve(request.uri.href, redirectTo)
    105   }
    106 
    107   var uriPrev = request.uri
    108   request.uri = url.parse(redirectTo)
    109 
    110   // handle the case where we change protocol from https to http or vice versa
    111   if (request.uri.protocol !== uriPrev.protocol) {
    112     delete request.agent
    113   }
    114 
    115   self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo })
    116 
    117   if (self.followAllRedirects && request.method !== 'HEAD' &&
    118     response.statusCode !== 401 && response.statusCode !== 307) {
    119     request.method = self.followOriginalHttpMethod ? request.method : 'GET'
    120   }
    121   // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215
    122   delete request.src
    123   delete request.req
    124   delete request._started
    125   if (response.statusCode !== 401 && response.statusCode !== 307) {
    126     // Remove parameters from the previous response, unless this is the second request
    127     // for a server that requires digest authentication.
    128     delete request.body
    129     delete request._form
    130     if (request.headers) {
    131       request.removeHeader('host')
    132       request.removeHeader('content-type')
    133       request.removeHeader('content-length')
    134       if (request.uri.hostname !== request.originalHost.split(':')[0]) {
    135         // Remove authorization if changing hostnames (but not if just
    136         // changing ports or protocols).  This matches the behavior of curl:
    137         // https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710
    138         request.removeHeader('authorization')
    139       }
    140     }
    141   }
    142 
    143   if (!self.removeRefererHeader) {
    144     request.setHeader('referer', uriPrev.href)
    145   }
    146 
    147   request.emit('redirect')
    148 
    149   request.init()
    150 
    151   return true
    152 }
    153 
    154 exports.Redirect = Redirect