WebSockets with OpenResty

Lua WebSocket Implementation Installation

I have been following OpenResty development closely for a while now, but I did never got an inspiration to really try it out, until now. Yichun Zhang (@agentzh) of OpenResty-fame announced that he just released a preliminary WebSockets support for Lua Nginx module (lua-nginx-module). I have been waiting for this to happen.

I managed to install, and test this on my Mac. Here is how I did it:

$ # brew install luajit (not needed, included with openresty)
$ brew install pcre
$ wget http://openresty.org/download/ngx_openresty-
$ tar zxf ngx_openresty-
$ git clone https://github.com/chaoslawful/lua-nginx-module.git
$ cd lua-nginx-module
$ git checkout -b websocket origin/websocket
$ cd ../ngx_openresty-
$ rm -Rf ngx_lua-0.8.9
$ ln -s ../../lua-nginx-module ngx_lua-0.8.9
$ cd ..
$ ./configure \
--with-luajit \
--with-cc-opt="-I/usr/local/Cellar/pcre/8.33/include" \
$ make -j2
$ make install

Now we should have OpenResty installed with Lua module that supports WebSockets at /usr/local/openresty. Before we continue to test it out, let’s also pull lua-resty-websocket libraries:

$ cd ..
$ git clone https://github.com/agentzh/lua-resty-websocket.git

At this point, if everything went without errors, we should have all that we need to test out WebSockets. Time to do some configurations:

cd /usr/local/openresty/nginx/conf

Now open nginx.conf, and add this before “include mime.types;” inside “http { … }”:

lua_package_path "/path/to/lua-resty-websocket/lib/?.lua;;";

Next we need to write the WebSockets server code (right now just a stupid echoing server). Again, edit nginx.conf, and add a new location after “location / { … }”:

location /s {
lua_socket_log_errors off;
lua_check_client_abort on;
content_by_lua '
local server = require "resty.websocket.server"
local wb, err = server:new{
timeout = 5000,
max_payload_len = 65535
if not wb then
ngx.log(ngx.ERR, "failed to new websocket: ", err)
return ngx.exit(444)
while true do
local data, typ, err = wb:recv_frame()
if wb.fatal then
ngx.log(ngx.ERR, "failed to receive frame: ", err)
return ngx.exit(444)
if not data then
local bytes, err = wb:send_ping()
if not bytes then
ngx.log(ngx.ERR, "failed to send ping: ", err)
return ngx.exit(444)
elseif typ == "close" then break
elseif typ == "ping" then
local bytes, err = wb:send_pong()
if not bytes then
ngx.log(ngx.ERR, "failed to send pong: ", err)
return ngx.exit(444)
elseif typ == "pong" then
ngx.log(ngx.INFO, "client ponged")
elseif typ == "text" then
local bytes, err = wb:send_text(data)
if not bytes then
ngx.log(ngx.ERR, "failed to send text: ", err)
return ngx.exit(444)

Looks great. Now add websockets.html to /usr/local/openresty/nginx/html directory:

var ws = null;
function connect() {
if (ws !== null) return log('already connected');
ws = new WebSocket('ws://');
ws.onopen = function () {
ws.onerror = function (error) {
ws.onmessage = function (e) {
log('recv: ' + e.data);
ws.onclose = function () {
ws = null;
return false;
function disconnect() {
if (ws === null) return log('already disconnected');
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);
return false;
function log(text) {
var li = document.createElement('li');
return false;
<form onsubmit="return send();">
<button type="button" onclick="return connect();">Connect</button>
<button type="button" onclick="return disconnect();">Disconnect</button>
<input id="text" type="text">
<button type="submit">Send</button>
<ol id="log"></ol>

And now start the nginx with:

sudo /usr/local/openresty/nginx/sbin/nginx

Then open a browser that has WebSocket support enabled, and open following url:
Lua Web Sockets in Action

To guard against half-open TCP connections, it is a good idea to enable TCP keepalive:

$ sysctl net.inet.tcp.always_keepalive
net.inet.tcp.always_keepalive: 0
$ sudo sysctl -w net.inet.tcp.always_keepalive=1
net.inet.tcp.always_keepalive: 0 -> 1

(for Linux, see: Using TCP keepalive under Linux)