title: Node.js handling redirect (302) for http/s.request is failing to resolve promise
body: <p>I am writing a small function to handle server to server http/s requests.
  The problem is coming when I am handling redirects.</p>
  
  <p>I have wrapped the request in a promise and 99.99% works as expected for all request types (http/s, get, post, put, delete etc) and resolves or rejects correctly given the input parameters however redirects are tripping me up.</p>
  
  <p>When handling a 300 - 400 http response, I am aborting the request, recursively calling the function and rejecting that promise instance and logging out the results looks correct, but on the last call after a 200 response code is received, it isn't resolving the promise.</p>
  
  <p>Here's the log from a call to: <a href="https://www.httpbin.org/absolute-redirect/5" rel="nofollow noreferrer">https://www.httpbin.org/absolute-redirect/5</a></p>
  
  <pre><code>302
  Loc: http://www.httpbin.org/absolute-redirect/4 maxRedirects: 9
  {"msg":"redirect"}
  302
  Loc: http://www.httpbin.org/absolute-redirect/3 maxRedirects: 8
  302
  Loc: http://www.httpbin.org/absolute-redirect/2 maxRedirects: 7
  302
  Loc: http://www.httpbin.org/absolute-redirect/1 maxRedirects: 6
  302
  Loc: http://www.httpbin.org/get maxRedirects: 5
  200
  { status: 200,
    headers: '{"connection":"close","server":"meinheld/0.6.1","date":"Tue, 27 Jun 2017 06:28:54 GMT","content-type":"application/json","access-control-allow-origin":"*","access-control-allow-credentials":"true","x-powered-by":"Flask","x-processed-time":"0.00139999389648","content-length":"167","via":"1.1 vegur"}' }
  {
    "args": {}, 
    "headers": {
      "Connection": "close", 
      "Host": "www.httpbin.org"
    }, 
    "origin": "***.***.***.***", 
    "url": "http://www.httpbin.org/get"
  }
  </code></pre>
  
  <p>and here is the function itself:</p>
  
  <pre><code>function gimme(requestOptions) {
      //** Validate request object properties and set defaults where required
      typeof requestOptions.url === 'undefined' || requestOptions.url === null ? requestOptions.url = null : requestOptions.url = requestOptions.url;
      typeof requestOptions.method === 'undefined' ? requestOptions.method = 'GET' : requestOptions.method = requestOptions.method.toUpperCase();
      typeof requestOptions.body === 'undefined' ? requestOptions.body = null : requestOptions.body = requestOptions.body;
      typeof requestOptions.contentType === 'undefined' ? requestOptions.contentType = 'form' : requestOptions.contentType = requestOptions.contentType;
      typeof requestOptions.followRedirect === 'undefined' ? requestOptions.followRedirect = true : requestOptions.followRedirect = requestOptions.followRedirect;
      typeof requestOptions.maxRedirects === 'undefined' ? requestOptions.maxRedirects = 10 : requestOptions.maxRedirects = requestOptions.maxRedirects;
      typeof requestOptions.timeout === 'undefined' ? requestOptions.timeout = 10000 : requestOptions.timeout = requestOptions.timeout;
      typeof requestOptions.rejectUnauthorized === 'undefined' ? requestOptions.rejectUnauthorized = false : requestOptions.rejectUnauthorized = requestOptions.rejectUnauthorized;
  
      //** Create request literal to populate and use with request call
      let requestObject = {};
  
      return new Promise((resolve, reject) =&gt; {
      //** No url no worky!
      if(requestOptions.url === null) reject(JSON.stringify({msg:'Missing url'}));
  
      //** If protocol is missing backfill
      if(requestOptions.url.search(/^http[s]?\:\/\//) == -1){
          requestOptions.url = 'http://' + requestOptions.url;
      }
  
      //** Parse the url and return and object
      let urlObject = url.parse(requestOptions.url);
  
      //** Populate the request object
      requestObject.method = requestOptions.method;
      requestObject.hostname = urlObject.host;
      requestObject.path = urlObject.path;
  
      //** if https set port 443 else port 80 ?? Deal with non-standard ports
      urlObject.protocol === 'https' ? requestObject.port = 443 : requestObject.port = 80;
  
      //** Set the protocol to use for request call and require the relevant lib
      const lib = urlObject.protocol === 'https' ? require('https') : require('http');
  
      //** If body in payload parse and set data accordingly
      if(requestOptions.body !== null) {
          if(requestObject.method === 'GET'){
              requestObject.path = requestObject.path + '?' + querystring.stringify(requestOptions.body);
          } else {
              //** Populate the data to send
              var postData = JSON.stringify(requestOptions.body);
  
              //** Set headers 
              switch (requestOptions.contentType){
                  case 'json':
                      requestObject.headers = {
                          'Content-Type': 'application/json',
                          'Content-Length': postData.length
                      };
                  break;
                  case 'form':
                      requestObject.headers = {
                          'Content-Type': 'application/x-www-form-urlencoded',
                          'Content-Length': postData.length
                      };
                  break;
                  default:
                      requestObject.headers = {
                          'Content-Type': 'application/x-www-form-urlencoded',
                          'Content-Length': postData.length
                      };
                  break;
              };
          };
      };
  
          let req = lib.request(requestObject, function (response) {
              if(response.statusCode &gt; 300 &amp;&amp; response.statusCode &lt; 400 &amp;&amp; response.headers.location) { //** Detect a redirect
                  requestOptions.url = response.headers.location;
                  --requestOptions.maxRedirects;
                  req.abort();
                  gimme(requestOptions);
                  reject(JSON.stringify({msg:'redirect'}));
                  return;
              } else if(response.statusCode == 405) {
                  reject(JSON.stringify({msg:'Method Not Allowed'}));
              } else {
                  let requestResult = {
                      status: response.statusCode,
                      headers: JSON.stringify(response.headers)
                  };
                  console.log(requestResult);
  
                  //** temporary data holder
                  const body = [];
  
                  response.on('data', function (chunk) {
                      body.push(chunk);
                  });
  
                  //** do 
                  response.on('end', function () {
                      let reqRes = body.join('');
                      resolve(reqRes);
                      return;
                  });
              };
          })
  
          req.on('socket', function (socket) {
              socket.setTimeout(requestOptions.timeout);  
              socket.on('timeout', function() {
                  req.abort();
                  reject(JSON.stringify({msg:'timeout'}));
              });
          });
  
          req.on('error', function (e) {
              reject(e.code + ' ' + e.syscall);
          });
  
          if(typeof postData !== 'undefined' &amp;&amp; postData !== null){
              req.write(postData);
          }
          req.end();
      });
  };
  </code></pre>
  
  <p>Any help gratefully appreciated!</p>
  
  <p>Cheers
  George</p>