node.js distinguishing errors when making http request

35,498

Here is an example:

var http = require('http');

var options = {
  hostname: 'localhost',
  port: 7777,
  path: '/',
  method: 'GET'
};

var req = http.request(options, function (res) {
  // check the returned response code
  if (('' + res.statusCode).match(/^2\d\d$/)) {
    // Request handled, happy
  } else if (('' + res.statusCode).match(/^5\d\d$/))
    // Server error, I have no idea what happend in the backend
    // but server at least returned correctly (in a HTTP protocol
    // sense) formatted response
  }
});

req.on('error', function (e) {
  // General error, i.e.
  //  - ECONNRESET - server closed the socket unexpectedly
  //  - ECONNREFUSED - server did not listen
  //  - HPE_INVALID_VERSION
  //  - HPE_INVALID_STATUS
  //  - ... (other HPE_* codes) - server returned garbage
  console.log(e);
});

req.on('timeout', function () {
  // Timeout happend. Server received request, but not handled it
  // (i.e. doesn't send any response or it took to long).
  // You don't know what happend.
  // It will emit 'error' message as well (with ECONNRESET code).

  console.log('timeout');
  req.abort();
});

req.setTimeout(5000);
req.end();

I recommend you play with it using netcat, ie.:

$ nc -l 7777
// Just listens and does not send any response (i.e. timeout)

$ echo -e "HTTP/1.1 200 OK\n\n" | nc -l 7777
// HTTP 200 OK

$ echo -e "HTTP/1.1 500 Internal\n\n" | nc -l 7777
// HTTP 500

(and so on...)

Share:
35,498
Heptic
Author by

Heptic

Updated on July 09, 2022

Comments

  • Heptic
    Heptic almost 2 years

    My node.js application is using http.request to the REST API http://army.gov/launch-nukes and I need to distinguish between three possible cases:

    • Success -- The server replies in the affirmative. I know my enemies are destroyed.
    • Failure -- Either I have received error from the server, or was unable to connect to server. I still have enemies.
    • Unknown -- After establishing a connection to the server, I have sent the request -- but not sure what happened. This could mean the request never made it to the server, or the server response to me never made it. I may or may not have just started a world war.

    As you can see, it's very important for me to distinguish the Failure and Unknown case, as they have very different consequences and different actions I need to take.

    I would also very much like to use http Keep-Alive -- as what can I say, I'm a bit of a war-monger and plan on making lots of requests in bursts (and then nothing for long periods of time)

    --

    The core of the question is how to separate a connection-error/time-out (which is a Failure) from an error/timeout that occurs after the request is put on the wire (which is an Unknown).

    In psuedo-code logic I want this:

    var tcp = openConnectionTo('army.gov') // start a new connection, or get an kept-alive one
    tcp.on('error', FAILURE_CASE);
    tcp.on('connectionEstablished',  function (connection) {
    
           var req = connection.httpGetRequest('launch-nukes');
           req.on('timeout', UNKNOWN_CASE);
           req.on('response', /* read server response and decide FAILURE OR SUCCESS */);
       }
    )