@peccul is peccu

(love peccu '(emacs lisp cat outdoor bicycle mac linux coffee))

サムライトのEngine.IOサーバーを試す

深町さんがサムライトでEngine.IOサーバーを書いていたので試しに動かしてみた。 github.com

ただ、そのままじゃClack(Lack)やwebsocket-driverの変更に追従していなかったので、forkして修正してみた。

github.com

果たして今さらプルリクエストを作成しても見てもらえるのだろうか。。。

インストー

ros install somewrite-adtech/engine-io-parser
# ros install somewrite-adtech/engine-io-server
ros install peccu/engine-io-server
# クライアントテスト用
npm i engine.io-client

echoサーバーのサンプル

  • server.ros
#!/bin/sh
#|-*- mode:lisp -*-|#
#| <Put a one-line description here>
exec ros -Q -- $0 "$@"
|#
(progn ;;init forms
  (ros:ensure-asdf)
  #+quicklisp (ql:quickload '(:engine-io-server) :silent t)
  )

(defpackage :ros.script.server.3706692586
  (:use :cl
        :engine-io-server)
  )
(in-package :ros.script.server.3706692586)

(defparameter *server* nil)
(defparameter *port* 3456)

;; https://github.com/somewrite-adtech/engine-io-server/blob/master/t/util.lisp#L47
(defmacro with-server ((&rest initargs) &body body)
  (let ((handler (gensym "HANDLER")))
    `(tagbody beginning
        (let* ((*server* (make-instance 'server ,@initargs))
               (,handler (start-server *server* :port *port*)))
          (unwind-protect (handler-bind ((usocket:connection-refused-error
                                          (lambda (e)
                                            (declare (ignore e))
                                            (sleep 1)
                                            (go beginning))))
                            ,@body)
            (stop-server ,handler)
            (sleep 0.5))))))

(defun main (&rest argv)
  (declare (ignorable argv))
  (with-server (:allow-upgrades nil)
    (on :connection *server*
        (lambda (socket)
;; 多分ここにイベントハンドラを増やしていけばよい
          ;; echo server
          (on :message socket
              (lambda (msg)
                (format t "~a~%" msg)
                (send socket msg)))

          ))
    (loop (sleep 0.1))))
;;; vim: set ft=lisp lisp:
  • client.js
var socket = require('engine.io-client')('ws://127.0.0.1:3456');
socket.on('open', function(){
  console.log('open');
  socket.on('message', function(data){
    console.log('message', data);
  });
  socket.on('close', function(){
    console.log('close');
  });
  socket.send('abc');
});
socket.on('error', function(e){
  console.log('error', e);
});
  • サーバーは ./server.ros で起動する。うまく動いていればこんな感じ
% ./server.ros
Wookie server is started.
Listening on localhost:3456.
  • クライアントは node index.jsで起動する。
% node index.js
open
message abc

感想

engine-io-serverの修正と動作確認のために実装を読んで、色々勉強になった。

  • Common LispからNode.jsを呼び出す方法、
  • アプリを起動してもすぐ終了しない方法(loop使ってタイムアウト処理を書けばいいんや)
  • asdファイルにパスが通っていればql:quickloadできて、ql:quickloadしていないとuseとかimport-fromができないこと

先日のLispユーザー会で佐野さんにドキュメントがない時のコードの読み方を聞いておいてよかった。 コードリーディングのエントリーポイントとか指針があると読めるような気になってくる。

ros install、ros-tap便利

Clack, Caveman, Engine.IOそれぞれで似たようなことをしようとしたらどうするのか比較してみる。

比較してみたいことは以下のとおり

  • POSTを受け付けてwebsocket用のURLを発行する
  • websocketのメッセージに対する処理を記述する