开发者

With node.js HTTP, how does res.end() guarantee a disconnection of the socket?

This is node.js' end implementation:

OutgoingMessage.prototype.end = function(data, encoding) {
  if (this.finished) {
    return false;
  }
  if (!this._header) {
    this._implicitHeader();
  }

  if (data && !this._hasBody) {
    console.error('This type of response MUST NOT have a body. ' +
                  'Ignoring data passed to end().');
    data = false;
  }

  var ret;

  var hot = this._headerSent === false &&
            typeof(data) === 'string' &&
            data.length > 0 &&
            this.output.length === 0 &&
            this.connection &&
            this.connection.writable &&
            this.connection._httpMessage === this;

  if (hot) {
    // Hot path. They're doing
    //   res.writeHead();
    //   res.end(blah);
    // HACKY.

    if (this.chunkedEncoding) {
      var l = Buffer.byteLength(data, encoding).toString(16);
      ret = this.connection.write(this._header + l + CRLF +
                                  data + '\r\n0\r\n' +
                                  this._trailer + '\r\n', encoding);
    } else {
      ret = this.connection.write(this._header + data, encoding);
    }
    this._headerSent = true;

  } else if (data) {
    // Normal body write.
    ret = this.write(data, encoding);
  }

  if (!hot) {
    if (this.chunkedEncoding) {
开发者_JAVA百科      ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
    } else {
      // Force a flush, HACK.
      ret = this._send('');
    }
  }

  this.finished = true;

  // There is the first message on the outgoing queue, and we've sent
  // everything to the socket.
  if (this.output.length === 0 && this.connection._httpMessage === this) {
    debug('outgoing message end.');
    this._finish();
  }

  return ret;
};

Source: https://github.com/joyent/node/blob/master/lib/http.js#L645

Apparently, the connection is only "finished" when output.length === 0.

So, if there is still data waiting to be written, and the receiving client for some reason is dodgy about receiving this data, will the request ever be ended?

I have also seen such issue where an end is not effective while trying to end a http request made by a flash uploader. I ended up doing the following, which did help:

    res.end(failureJSON, 'utf8');
    req.once('end', function _destroyConn() {
        req.connection.destroy();
    });

Seems very hackish. Anyway, is such behavior with req.connection.destroy needed to guarantee a disconnection from the socket?


Unfortunately, res.end() does not directly “guarantee a disconnection of the socket” because it needs to account for HTTP Keep-Alive. According to the docs, end tells the server that everything has been sent, and that the response is complete. It’s entirely up to the server object whether or not to disconnect immediately.

To answer your question more specifically, the important thing is that the response needs to emit a finish event. If you take a look at the implementation of _finish(), it pretty much just emits the event.

As you noted though, it doesn’t always call _finish() directly…but it did set this.finished = true. When _flush() executes, it sends any remaining data and THEN calls _finish().

It’s kind of complicated, and I don’t think I can go into any more detail without the risk of being wrong.

As far as connections sometimes not closing, have you checked if you have keep-alive configured properly? If the HTTP connection is set up with keep-alive by default, then calling end will not close the socket.

If your print out res.shouldKeepAlive, it will tell you if your server is trying to use keep-alive. Set it to false at the start of your request handler if you want to stop the server from doing this.


I don't know if this helps you as I am building my framework for node 4.4+ but I have confirmed that you can send Connection: close header in your response to get node to close the connection.

let res = getResponseSomehow()

res.statusCode = 408
res.setHeader("Connection", "close")
res.end()

Also your destroy code could use the following tweak:

// First we give node the time to close the connection
// We can give it 2 seconds
let socket = getSocketSomehow();
let timer = setTimeout(function() {
  socket.destroy();
}, 2000);

socket.on("close", function() {
  clearTimeout(timer);
});

I'm not quite sure if it's the close event you want. I normally try to use a library and stay away from the net api so this is just a guess.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜