test-tcpモジュールを作ってみた

はじめてのnpm publish。
PerlTest::TCPを愛用しているのですが、Node.jsでもこういうの欲しいと思っていて探してみたけどそれらしきものが見当たらなかったので作ってみました。
https://github.com/sugyan/node-test-tcp


「サーバのインスタンスとクライアントのコードを渡すと、空いているportでサーバを立ち上げてクライアントコードを実行してくれる」というもの。
こんなカンジで使うイメージです。

var assert = require('assert');
var net = require('net');

require('test-tcp').test_tcp({
    server: net.createServer(function (socket) {
        socket.on('data', function (data) {
            socket.write(data);
        });
    }),
    client: function (port, done) {
        var socket = new net.Socket();
        socket.connect(port, function () {
            socket.on('close', done);
            socket.on('data', function (data) {
                assert.equal(data.toString(), 'foo');
                socket.end();
            });
            socket.write('foo');
        });
    }
});

express使ったhttpサーバとかでも

var assert = require('assert');
var express = require('express');
var app = express.createServer();

app.get('/foo', function (req, res) {
    res.send('bar');
});

require('test-tcp').test_tcp({
    server: app,
    client: function (port, done) {
        require('http').get({
            port: port,
            path: '/foo'
        }, function (res) {
            var buffer = '';
            assert.equal(res.statusCode, 200);
            res.on('data', function (chunk) {
                buffer += chunk;
            });
            res.on('end', function () {
                assert.equal(buffer, 'bar');
                done();
            });
        });
    }
});

というカンジで。


もともとのTest::TCPではサーバ起動時にforkして子プロセスとして動かしてシグナルで制御したりということをやっているようなんですが、Nodeでそれをやるのは大変そうだったので、普通に同一プロセス上でサーバを動かす形にしています。また、サーバ側の指定方法も元々はportを渡して自分でサーバを立ち上げる使い方だけど、NodeではDESTROY時に裏で勝手に停止させるとか出来なそうだし普通にnet.Serverを明示的にlisten、close呼ばないと、ということで どうせそうなるならnet.Serverのインスタンスを渡す形でいいか、ということでそういうインタフェースにしてみました。
処理の流れとしては

  1. 空いているポートを探す
  2. 空いているポートでサーバ起動
  3. クライアントコードにポート番号、終了時コールバック関数を渡して実行
  4. クライアントコードの終了時コールバックが呼ばれたらサーバを停止

となるので、クライアントコードに第2引数で渡すコールバックが呼ばれないとプログラムは終了してくれない。非同期プログラミングではこういう風になってしまうのは仕方ないかなぁ、と。。せめてtimeoutとかは設定値に渡すようにしておくべきかしら。