node.js distinguishing errors when making http req

2019-03-10 22:12发布

问题:

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 */);
   }
)

回答1:

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...)



回答2:

This is typically in the APIs status code. In the request package you can access it like this

request('http://www.google.com', function (error, response, body) {
   if (!error && response.statusCode == 200) {
       console.log(body) // Print the google web page.
   }
})

response.statusCode being 200 means that it worked. 500 would be failure. Unknown would be the callback never being called.

If the API you're describing doesn't follow standard response codes, I don't know. You'll have to look at the docs.