Skip to content

Instantly share code, notes, and snippets.

@shuhei
Last active December 10, 2015 23:19
Show Gist options
  • Save shuhei/4508778 to your computer and use it in GitHub Desktop.
Save shuhei/4508778 to your computer and use it in GitHub Desktop.
Node.js の http で OutgoingMessage がどういうタイミングで送られるかの検証です。
var http = require('http');
var logger = require('./logger');
var PORT = 1337;
//
// Methods of task execution
//
function takeItEasy(duration) {
return function () {
var callee = arguments.callee;
if (arguments.length > 0) {
var args = Array.prototype.slice.apply(arguments);
args.shift()();
setTimeout(function () {
callee.apply(null, args);
}, duration);
}
};
}
function imperative() {
for (var i = 0; i < arguments.length; i++) {
arguments[i]();
}
}
function serial() {
if (arguments.length > 0) {
var args = Array.prototype.slice.apply(arguments);
args.shift()(function () {
serial.apply(null, args);
});
}
}
//
// Server and client
//
function createServer(execMethod, onListening, onClose) {
var server = http.createServer();
server.on('request', function (req, res) {
logger.server('Req received header');
req.on('data', function (chunk) {
logger.server('Req received', chunk.toString());
});
req.on('end', function () {
logger.server('Req end');
execMethod(function () {
logger.server('Res writing header');
res.writeHead(200, { 'Content-Type': 'text/plain'});
}, function () {
logger.server('Res sending Foo');
res.write('Foo');
}, function () {
logger.server('Res ending with Bar');
res.end('Bar');
}, function () {
logger.server('Closing');
server.close();
});
});
});
server.on('listening', function () {
logger.server('Listening')
onListening();
});
server.on('close', onClose);
server.listen(PORT);
}
function createClient(execMethod) {
var messages = ['Hello', 'World'];
var req;
execMethod(function () {
logger.client('Req requesting')
var options = { host: 'localhost', port: PORT, path: '/' };
req = http.request(options);
req.on('response', function (res) {
logger.client('Res received header');
res.on('data', function (chunk) {
logger.client('Res received', chunk.toString());
});
res.on('end', function () {
logger.client('Res end');
})
});
}, function () {
logger.client('Req setting content-length');
var contentLength = messages.reduce(function (sum, value) {
return sum + Buffer.byteLength(value);
}, 0);
req.setHeader('Content-Length', contentLength);
}, function () {
logger.client('Req sending', messages[0]);
req.write(messages[0]);
}, function () {
logger.client('Req sending', messages[1]);
req.write(messages[1]);
}, function () {
logger.client('Req ending');
req.end();
});
}
//
// Make experiments
//
function experiment(name, execMethod) {
return function (callback) {
console.log('---------', name, '---------');
createServer(execMethod, createClient.bind(null, execMethod), callback);
};
}
serial(
experiment('setTimeout 1000', takeItEasy(1000)),
experiment('setTimeout 0', takeItEasy(0)),
experiment('imperative', imperative),
function (callback) {
callback();
}
);

setTimeout 0 の場合や普通に処理を進めていった場合は、write してもデータがバッファされ end するまでデータが送られないように見えます。

一方、1 秒待ちながら処理を進めていった場合、以下のようになりました。

  • クライアントからのリクエストも、サーバからのレスポンスも、write する毎にデータが送信される。
  • クライアントからのリクエストも、サーバからのレスポンスも、ヘッダを設定しただけでは送信されず、最初に write した際に送信される。
  • サーバは、リクエストの Content-Length 分の body を受け取ると、クライアントの end を待たずに end イベントが発火される。

Node.js の http.OutgoingMessage のソースを見ると、以下のようなことがわかります。

  • ヘッダは最初の write されるデータと一緒に送る。
  • データを socket に書き込みできない場合はバッファし、できる場合は socket に write する。

この例では OutgoingMessage ではバッファされていないようなので、データがバッファされる場合は http よりレイヤーでバッファされているようです。

module.exports = {
_green: '\u001b[32m',
_red: '\u001b[31m',
_reset: '\u001b[0m',
_log: function (args, color, prefix) {
console.log(color + prefix + Array.prototype.join.call(args, ' ') + this._reset);
},
server: function () {
this._log(arguments, this._green, '[Server] ');
},
client: function () {
this._log(arguments, this._red, '[Client] ');
}
};
--------- setTimeout 1000 ---------
[Server] Listening
[Client] Req requesting
[Client] Req setting content-length
[Client] Req sending Hello
[Server] Req received header
[Server] Req received Hello
[Client] Req sending World
[Server] Req received World
[Server] Req end
[Server] Res writing header
[Client] Req ending
[Server] Res sending Foo
[Client] Res received header
[Client] Res received Foo
[Server] Res ending with Bar
[Client] Res received Bar
[Client] Res end
[Server] Closing
--------- setTimeout 0 ---------
[Server] Listening
[Client] Req requesting
[Client] Req setting content-length
[Client] Req sending Hello
[Client] Req sending World
[Client] Req ending
[Server] Req received header
[Server] Req received HelloWorld
[Server] Req end
[Server] Res writing header
[Server] Res sending Foo
[Server] Res ending with Bar
[Server] Closing
[Client] Res received header
[Client] Res received Foo
[Client] Res received Bar
[Client] Res end
--------- imperative ---------
[Server] Listening
[Client] Req requesting
[Client] Req setting content-length
[Client] Req sending Hello
[Client] Req sending World
[Client] Req ending
[Server] Req received header
[Server] Req received HelloWorld
[Server] Req end
[Server] Res writing header
[Server] Res sending Foo
[Server] Res ending with Bar
[Server] Closing
[Client] Res received header
[Client] Res received Foo
[Client] Res received Bar
[Client] Res end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment