http: Add write()/end() callbacks
This commit is contained in:
parent
ce3d18412c
commit
da93d6adfb
@ -60,6 +60,7 @@ function OutgoingMessage() {
|
|||||||
|
|
||||||
this.output = [];
|
this.output = [];
|
||||||
this.outputEncodings = [];
|
this.outputEncodings = [];
|
||||||
|
this.outputCallbacks = [];
|
||||||
|
|
||||||
this.writable = true;
|
this.writable = true;
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ OutgoingMessage.prototype.destroy = function(error) {
|
|||||||
|
|
||||||
|
|
||||||
// This abstract either writing directly to the socket or buffering it.
|
// 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
|
// 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
|
// the same packet. Future versions of Node are going to take care of
|
||||||
// this at a lower level and in a more general way.
|
// this at a lower level and in a more general way.
|
||||||
@ -122,15 +123,23 @@ OutgoingMessage.prototype._send = function(data, encoding) {
|
|||||||
} else {
|
} else {
|
||||||
this.output.unshift(this._header);
|
this.output.unshift(this._header);
|
||||||
this.outputEncodings.unshift('ascii');
|
this.outputEncodings.unshift('ascii');
|
||||||
|
this.outputCallbacks.unshift(null);
|
||||||
}
|
}
|
||||||
this._headerSent = true;
|
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 (data.length === 0) {
|
||||||
|
if (util.isFunction(callback))
|
||||||
|
process.nextTick(callback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,32 +150,33 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding) {
|
|||||||
// There might be pending data in the this.output buffer.
|
// There might be pending data in the this.output buffer.
|
||||||
while (this.output.length) {
|
while (this.output.length) {
|
||||||
if (!this.connection.writable) {
|
if (!this.connection.writable) {
|
||||||
this._buffer(data, encoding);
|
this._buffer(data, encoding, callback);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var c = this.output.shift();
|
var c = this.output.shift();
|
||||||
var e = this.outputEncodings.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.
|
// Directly write to socket.
|
||||||
return this.connection.write(data, encoding);
|
return this.connection.write(data, encoding, callback);
|
||||||
} else if (this.connection && this.connection.destroyed) {
|
} else if (this.connection && this.connection.destroyed) {
|
||||||
// The socket was destroyed. If we're still trying to write to it,
|
// The socket was destroyed. If we're still trying to write to it,
|
||||||
// then we haven't gotten the 'close' event yet.
|
// then we haven't gotten the 'close' event yet.
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// buffer, as long as we're not destroyed.
|
// buffer, as long as we're not destroyed.
|
||||||
this._buffer(data, encoding);
|
this._buffer(data, encoding, callback);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
OutgoingMessage.prototype._buffer = function(data, encoding) {
|
OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
|
||||||
this.output.push(data);
|
this.output.push(data);
|
||||||
this.outputEncodings.push(encoding);
|
this.outputEncodings.push(encoding);
|
||||||
|
this.outputCallbacks.push(callback);
|
||||||
return false;
|
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) {
|
if (!this._header) {
|
||||||
this._implicitHeader();
|
this._implicitHeader();
|
||||||
}
|
}
|
||||||
@ -401,7 +411,7 @@ OutgoingMessage.prototype.write = function(chunk, encoding) {
|
|||||||
encoding !== 'binary') {
|
encoding !== 'binary') {
|
||||||
len = Buffer.byteLength(chunk, encoding);
|
len = Buffer.byteLength(chunk, encoding);
|
||||||
chunk = len.toString(16) + CRLF + chunk + CRLF;
|
chunk = len.toString(16) + CRLF + chunk + CRLF;
|
||||||
ret = this._send(chunk, encoding);
|
ret = this._send(chunk, encoding, callback);
|
||||||
} else {
|
} else {
|
||||||
// buffer, or a non-toString-friendly encoding
|
// buffer, or a non-toString-friendly encoding
|
||||||
if (util.isString(chunk))
|
if (util.isString(chunk))
|
||||||
@ -411,15 +421,15 @@ OutgoingMessage.prototype.write = function(chunk, encoding) {
|
|||||||
|
|
||||||
if (this.connection)
|
if (this.connection)
|
||||||
this.connection.cork();
|
this.connection.cork();
|
||||||
this._send(len.toString(16), 'ascii');
|
this._send(len.toString(16), 'ascii', null);
|
||||||
this._send(crlf_buf);
|
this._send(crlf_buf, null, null);
|
||||||
this._send(chunk, encoding);
|
this._send(chunk, encoding, null);
|
||||||
ret = this._send(crlf_buf);
|
ret = this._send(crlf_buf, null, callback);
|
||||||
if (this.connection)
|
if (this.connection)
|
||||||
this.connection.uncork();
|
this.connection.uncork();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = this._send(chunk, encoding);
|
ret = this._send(chunk, encoding, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('write ret = ' + ret);
|
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');
|
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)) {
|
if (data && !util.isString(data) && !util.isBuffer(data)) {
|
||||||
throw new TypeError('first argument must be a string or Buffer');
|
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) {
|
if (data && !this._hasBody) {
|
||||||
debug('This type of response MUST NOT have a body. ' +
|
debug('This type of response MUST NOT have a body. ' +
|
||||||
'Ignoring data passed to end().');
|
'Ignoring data passed to end().');
|
||||||
data = false;
|
data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.connection && data)
|
if (this.connection && data)
|
||||||
@ -479,10 +497,10 @@ OutgoingMessage.prototype.end = function(data, encoding) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.chunkedEncoding) {
|
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 {
|
} else {
|
||||||
// Force a flush, HACK.
|
// Force a flush, HACK.
|
||||||
ret = this._send('');
|
ret = this._send('', 'ascii', callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.connection && data)
|
if (this.connection && data)
|
||||||
@ -537,8 +555,9 @@ OutgoingMessage.prototype._flush = function() {
|
|||||||
|
|
||||||
var data = this.output.shift();
|
var data = this.output.shift();
|
||||||
var encoding = this.outputEncodings.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) {
|
if (this.finished) {
|
||||||
|
@ -161,8 +161,8 @@ ServerResponse.prototype.detachSocket = function(socket) {
|
|||||||
this.socket = this.connection = null;
|
this.socket = this.connection = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerResponse.prototype.writeContinue = function() {
|
ServerResponse.prototype.writeContinue = function(cb) {
|
||||||
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii');
|
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii', cb);
|
||||||
this._sent100 = true;
|
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