http: Add write()/end() callbacks
This commit is contained in:
parent
ce3d18412c
commit
da93d6adfb
@ -60,6 +60,7 @@ function OutgoingMessage() {
|
||||
|
||||
this.output = [];
|
||||
this.outputEncodings = [];
|
||||
this.outputCallbacks = [];
|
||||
|
||||
this.writable = true;
|
||||
|
||||
@ -110,7 +111,7 @@ OutgoingMessage.prototype.destroy = function(error) {
|
||||
|
||||
|
||||
// This abstract either writing directly to the socket or buffering it.
|
||||
OutgoingMessage.prototype._send = function(data, encoding) {
|
||||
OutgoingMessage.prototype._send = function(data, encoding, callback) {
|
||||
// This is a shameful hack to get the headers and first body chunk onto
|
||||
// the same packet. Future versions of Node are going to take care of
|
||||
// this at a lower level and in a more general way.
|
||||
@ -122,15 +123,23 @@ OutgoingMessage.prototype._send = function(data, encoding) {
|
||||
} else {
|
||||
this.output.unshift(this._header);
|
||||
this.outputEncodings.unshift('ascii');
|
||||
this.outputCallbacks.unshift(null);
|
||||
}
|
||||
this._headerSent = true;
|
||||
}
|
||||
return this._writeRaw(data, encoding);
|
||||
return this._writeRaw(data, encoding, callback);
|
||||
};
|
||||
|
||||
|
||||
OutgoingMessage.prototype._writeRaw = function(data, encoding) {
|
||||
OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
|
||||
if (util.isFunction(encoding)) {
|
||||
callback = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
if (util.isFunction(callback))
|
||||
process.nextTick(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -141,32 +150,33 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding) {
|
||||
// There might be pending data in the this.output buffer.
|
||||
while (this.output.length) {
|
||||
if (!this.connection.writable) {
|
||||
this._buffer(data, encoding);
|
||||
this._buffer(data, encoding, callback);
|
||||
return false;
|
||||
}
|
||||
var c = this.output.shift();
|
||||
var e = this.outputEncodings.shift();
|
||||
this.connection.write(c, e);
|
||||
var cb = this.outputCallbacks.shift();
|
||||
this.connection.write(c, e, cb);
|
||||
}
|
||||
|
||||
// Directly write to socket.
|
||||
return this.connection.write(data, encoding);
|
||||
return this.connection.write(data, encoding, callback);
|
||||
} else if (this.connection && this.connection.destroyed) {
|
||||
// The socket was destroyed. If we're still trying to write to it,
|
||||
// then we haven't gotten the 'close' event yet.
|
||||
return false;
|
||||
} else {
|
||||
// buffer, as long as we're not destroyed.
|
||||
this._buffer(data, encoding);
|
||||
this._buffer(data, encoding, callback);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
OutgoingMessage.prototype._buffer = function(data, encoding) {
|
||||
OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
|
||||
this.output.push(data);
|
||||
this.outputEncodings.push(encoding);
|
||||
|
||||
this.outputCallbacks.push(callback);
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -373,7 +383,7 @@ Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
|
||||
});
|
||||
|
||||
|
||||
OutgoingMessage.prototype.write = function(chunk, encoding) {
|
||||
OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
|
||||
if (!this._header) {
|
||||
this._implicitHeader();
|
||||
}
|
||||
@ -401,7 +411,7 @@ OutgoingMessage.prototype.write = function(chunk, encoding) {
|
||||
encoding !== 'binary') {
|
||||
len = Buffer.byteLength(chunk, encoding);
|
||||
chunk = len.toString(16) + CRLF + chunk + CRLF;
|
||||
ret = this._send(chunk, encoding);
|
||||
ret = this._send(chunk, encoding, callback);
|
||||
} else {
|
||||
// buffer, or a non-toString-friendly encoding
|
||||
if (util.isString(chunk))
|
||||
@ -411,15 +421,15 @@ OutgoingMessage.prototype.write = function(chunk, encoding) {
|
||||
|
||||
if (this.connection)
|
||||
this.connection.cork();
|
||||
this._send(len.toString(16), 'ascii');
|
||||
this._send(crlf_buf);
|
||||
this._send(chunk, encoding);
|
||||
ret = this._send(crlf_buf);
|
||||
this._send(len.toString(16), 'ascii', null);
|
||||
this._send(crlf_buf, null, null);
|
||||
this._send(chunk, encoding, null);
|
||||
ret = this._send(crlf_buf, null, callback);
|
||||
if (this.connection)
|
||||
this.connection.uncork();
|
||||
}
|
||||
} else {
|
||||
ret = this._send(chunk, encoding);
|
||||
ret = this._send(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
debug('write ret = ' + ret);
|
||||
@ -451,7 +461,15 @@ var zero_chunk_buf = new Buffer('\r\n0\r\n');
|
||||
var crlf_buf = new Buffer('\r\n');
|
||||
|
||||
|
||||
OutgoingMessage.prototype.end = function(data, encoding) {
|
||||
OutgoingMessage.prototype.end = function(data, encoding, callback) {
|
||||
if (util.isFunction(data)) {
|
||||
callback = data;
|
||||
data = null;
|
||||
} else if (util.isFunction(encoding)) {
|
||||
callback = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
if (data && !util.isString(data) && !util.isBuffer(data)) {
|
||||
throw new TypeError('first argument must be a string or Buffer');
|
||||
}
|
||||
@ -466,7 +484,7 @@ OutgoingMessage.prototype.end = function(data, encoding) {
|
||||
if (data && !this._hasBody) {
|
||||
debug('This type of response MUST NOT have a body. ' +
|
||||
'Ignoring data passed to end().');
|
||||
data = false;
|
||||
data = null;
|
||||
}
|
||||
|
||||
if (this.connection && data)
|
||||
@ -479,10 +497,10 @@ OutgoingMessage.prototype.end = function(data, encoding) {
|
||||
}
|
||||
|
||||
if (this.chunkedEncoding) {
|
||||
ret = this._send('0\r\n' + this._trailer + '\r\n', 'ascii');
|
||||
ret = this._send('0\r\n' + this._trailer + '\r\n', 'ascii', callback);
|
||||
} else {
|
||||
// Force a flush, HACK.
|
||||
ret = this._send('');
|
||||
ret = this._send('', 'ascii', callback);
|
||||
}
|
||||
|
||||
if (this.connection && data)
|
||||
@ -537,8 +555,9 @@ OutgoingMessage.prototype._flush = function() {
|
||||
|
||||
var data = this.output.shift();
|
||||
var encoding = this.outputEncodings.shift();
|
||||
var cb = this.outputCallbacks.shift();
|
||||
|
||||
ret = this.socket.write(data, encoding);
|
||||
ret = this.socket.write(data, encoding, cb);
|
||||
}
|
||||
|
||||
if (this.finished) {
|
||||
|
@ -161,8 +161,8 @@ ServerResponse.prototype.detachSocket = function(socket) {
|
||||
this.socket = this.connection = null;
|
||||
};
|
||||
|
||||
ServerResponse.prototype.writeContinue = function() {
|
||||
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii');
|
||||
ServerResponse.prototype.writeContinue = function(cb) {
|
||||
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii', cb);
|
||||
this._sent100 = true;
|
||||
};
|
||||
|
||||
|
100
test/simple/test-http-write-callbacks.js
Normal file
100
test/simple/test-http-write-callbacks.js
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var http = require('http');
|
||||
|
||||
var serverEndCb = false;
|
||||
var serverIncoming = '';
|
||||
var serverIncomingExpect = 'bazquuxblerg';
|
||||
|
||||
var clientEndCb = false;
|
||||
var clientIncoming = '';
|
||||
var clientIncomingExpect = 'asdffoobar';
|
||||
|
||||
process.on('exit', function() {
|
||||
assert(serverEndCb);
|
||||
assert.equal(serverIncoming, serverIncomingExpect);
|
||||
assert(clientEndCb);
|
||||
assert.equal(clientIncoming, clientIncomingExpect);
|
||||
console.log('ok');
|
||||
});
|
||||
|
||||
// Verify that we get a callback when we do res.write(..., cb)
|
||||
var server = http.createServer(function(req, res) {
|
||||
res.statusCode = 400;
|
||||
res.end('Bad Request.\nMust send Expect:100-continue\n');
|
||||
});
|
||||
|
||||
server.on('checkContinue', function(req, res) {
|
||||
server.close();
|
||||
assert.equal(req.method, 'PUT');
|
||||
res.writeContinue(function() {
|
||||
// continue has been written
|
||||
req.on('end', function() {
|
||||
res.write('asdf', function(er) {
|
||||
assert.ifError(er);
|
||||
res.write('foo', 'ascii', function(er) {
|
||||
assert.ifError(er);
|
||||
res.end(new Buffer('bar'), 'buffer', function(er) {
|
||||
serverEndCb = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
req.setEncoding('ascii');
|
||||
req.on('data', function(c) {
|
||||
serverIncoming += c;
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(common.PORT, function() {
|
||||
var req = http.request({
|
||||
port: common.PORT,
|
||||
method: 'PUT',
|
||||
headers: { 'expect': '100-continue' }
|
||||
});
|
||||
req.on('continue', function() {
|
||||
// ok, good to go.
|
||||
req.write('YmF6', 'base64', function(er) {
|
||||
assert.ifError(er);
|
||||
req.write(new Buffer('quux'), function(er) {
|
||||
assert.ifError(er);
|
||||
req.end('626c657267', 'hex', function(er) {
|
||||
assert.ifError(er);
|
||||
clientEndCb = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
req.on('response', function(res) {
|
||||
// this should not come until after the end is flushed out
|
||||
assert(clientEndCb);
|
||||
res.setEncoding('ascii');
|
||||
res.on('data', function(c) {
|
||||
clientIncoming += c;
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user