How to create a basic websocket with OpenResty and Lua within Fasty

Sometimes in our applications we need some realtime events. I used to use nodejs + socket.io but fort that I have to create a new script and deploy it via docker. It works well but will consume some RAM. An another approach is to use directly Lua websocket implementation.

For that you'll need to modify you nginx.conf file to add the endpoint.

location /ws {
  client_max_body_size 32k;

  lua_socket_log_errors off;
  lua_check_client_abort on;
  content_by_lua_file git/ws.lua;
}

Then on your git folder create a ws.lua file

local server = require "resty.websocket.server"

local wb, err = server:new{
    timeout = 5000,  -- in milliseconds
    max_payload_len = 65535,
}

if not wb then
    ngx.log(ngx.ERR, "failed to new websocket: ", err)
    return ngx.exit(444)
end

while true do
    local data, typ, err = wb:recv_frame()

    if not data then
        if not string.find(err, "timeout", 1, true) then
            ngx.log(ngx.ERR, "failed to receive a frame: ", err)
            return ngx.exit(444)
        end
    end

    if typ == "close" then
        -- for typ "close", err contains the status code
        local code = err

        -- send a close frame back:

        local bytes, err = wb:send_close(1000, "enough, enough!")
        if not bytes then
            ngx.log(ngx.ERR, "failed to send the close frame: ", err)
            return
        end
        ngx.log(ngx.INFO, "closing with status code ", code, " and message ", data)
        return
    end

    if typ == "text" then
        wb:send_text(data)
    end

    if typ == "ping" then
        -- send a pong frame back:

        local bytes, err = wb:send_pong(data)
        if not bytes then
            ngx.log(ngx.ERR, "failed to send frame: ", err)
            return
        end
    elseif typ == "pong" then
        -- just discard the incoming pong frame
    else
        ngx.log(ngx.INFO, "received a frame of type ", typ, " and payload ", data)
    end

end

wb:send_close()

It's a basic echo server but the idea is here :)

Now from your browser you can connect to your ws endoint like that

const socket = new WebSocket('ws://localhost:8080/ws');

socket.addEventListener('open', function (event) {
 socket.send('Hello World !');
});

socket.addEventListener('message', function (event) {
  console.log('A new server message', event.data);
});

Happy coding & happy new year ;)


Leave a comment