~benaiah/fennel-openresty

706435a04e1a7772f9948b4fa82de9e54346d7ed — Benaiah Mischenko 2 years ago 269cbdc
Basic websockets server in Fennel
4 files changed, 62 insertions(+), 5 deletions(-)

M conf/nginx.conf
M makefile
M server/server.fnl
A server/wss.fnl
M conf/nginx.conf => conf/nginx.conf +5 -1
@@ 9,7 9,11 @@ http {
        location / {
            default_type text/html;
            content_by_lua_file server/server.lua;
	    lua_code_cache off;
            lua_code_cache off;
        }
        location /wss {
            content_by_lua_file server/wss.lua;
            lua_code_cache off;
        }
    }
}
\ No newline at end of file

M makefile => makefile +2 -1
@@ 5,7 5,7 @@ all: server
clean: clean-server

.PHONY: server
server: server/server.lua server/html.lua
server: server/server.lua server/html.lua server/wss.lua

.PHONY: clean-server
clean-server:


@@ 13,3 13,4 @@ clean-server:

server/server.lua: server/server.fnl ; modules/fennel/fennel --compile $^ > $@
server/html.lua: server/html.fnl ; modules/fennel/fennel --compile $^ > $@
server/wss.lua: server/wss.fnl ; modules/fennel/fennel --compile $^ > $@

M server/server.fnl => server/server.fnl +15 -3
@@ 1,8 1,20 @@
(global ngx ngx)
(local html (require :html))
(local html (require :server.html))

(local script "var ws = null; function connect() { if (ws !== null) return log('already connected'); ws = new WebSocket('ws://127.0.0.1:8090/wss/'); ws.onopen = function () { log('connected'); }; ws.onerror = function (error) { log(error); }; ws.onmessage = function (e) { log('recv: ' + e.data); }; ws.onclose = function () { log('disconnected'); ws = null; }; return false;}function disconnect() { if (ws === null) return log('already disconnected'); ws.close(); return false;}function send() { if (ws === null) return log('please connect first'); var text = document.getElementById('text').value; document.getElementById('text').value = ''; log('send: ' + text); ws.send(text); return false;}function log(text) { var li = document.createElement('li'); li.appendChild(document.createTextNode(text)); document.getElementById('log').appendChild(li); return false;}")

(local homepage
       [:div {}
        [:h1 {} "Hello, world!"]])
       [:html {}
        [:head {}
         [:script {} script]]
        [:body {}
         [:div {}
          [:h1 {} "Hello, world!"]
          [:form {:onSubmit "return send();"}
           [:button {:type :button :onClick "return connect();"} :Connect]
           [:button {:type :button :onClick "return disconnect();"} :Disconnect]
           [:input {:id :text :type :text}]
           [:button {:type :submit} :Send]]]
         [:ol {:id :log}]]])

(ngx.say (html homepage))

A server/wss.fnl => server/wss.fnl +40 -0
@@ 0,0 1,40 @@
(global ngx ngx)
(local server (require :resty.websocket.server))
(local (wb, err) (: server :new {:timeout 60000 :max_payload_len 65535}))

(if (not wb)
    (do (ngx.log ngx.ERR "failed to connect to new websocket: ", err)
        (ngx.exit 444))

    (while true
      (local (data typ err) (: wb :recv_frame))
      (if wb.fatal
          (do (ngx.log ngx.ERR "failed to receive frame: ", err)
              (ngx.exit 444))

          (not data)
          (let [(bytes err) (: wb :send_ping)]
            (when (not bytes)
              (ngx.log ngx.ERR "failed to send ping: " err)
              (ngx.exit 444)))

          (= typ :close)
          (: wb :send_close)

          (= typ :ping)
          (let [(bytes err) (: wb :send_pong)]
            (when (not bytes)
              (ngx.log ngx.ERR "failed to send pong: " err)
              (ngx.exit 444)))

          (= typ :pong)
          (do (ngx.log ngx.INFO "client ponged")
              (: wb :send_close))

          (= typ :text)
          (let [(bytes err) (: wb :send_text data)]
            (if (not bytes)
                (do (ngx.log ngx.ERR "failed to send text: " err)
                    (ngx.exit 444))))

          (: wb :send_close))))