Skip to content

Commit

Permalink
Lua-binding
Browse files Browse the repository at this point in the history
Signed-off-by: Jianhui Zhao <jianhuizhao329@gmail.com>
  • Loading branch information
Jianhui Zhao committed Apr 27, 2019
1 parent 1b91718 commit 236e70f
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 1 deletion.
83 changes: 83 additions & 0 deletions example/example.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/lua

local ev = require "ev"
local uwsc = require "uwsc"
local loop = ev.Loop.default

local url = "ws://localhost:8082/echo"
local PING_INTERVAL = 5
local auto_reconnect = true
local RECONNECT_INTERVAL = 5
local do_connect = nil
local sender = nil

local function stop_send()
if sender then
sender:stop(loop)
sender = nil
end
end

local function on_open(c)
print(loop:now(), "open ok")

sender = ev.Timer.new(function(loop, timer, revents)
c:send_text("Text Message - " .. os.time())
c:send_binary("Binary Message - " .. os.time())
end, 0.5, 1)

sender:start(loop)
end

local function start_reconnect()
if not auto_reconnect then
loop:unloop()
return
end

ev.Timer.new(function()
do_connect()
end, RECONNECT_INTERVAL):start(loop)
end

do_connect = function()
local c, err = uwsc.new(url, PING_INTERVAL)
if not c then
print(loop:now(), err)
return
end

c:on("open", function()
on_open(c)
end)

c:on("message", function(data, is_binary)
print(loop:now(), "Received message:", data, "is binary:", is_binary)
end)

c:on("close", function(code, reason)
print(loop:now(), "Closed by peer:", code, reason)
stop_send()
start_reconnect()
end)

c:on("error", function(err, msg)
print(loop:now(), "Error occurred:", err, msg)
stop_send()
start_reconnect()
end)
end

ev.Signal.new(function()
loop:unloop()
end, ev.SIGINT):start(loop)

do_connect()

-- major minor patch
local version = string.format("%d.%d.%d", uwsc.version())
print(loop:now(), "Version:", version)

loop:loop()

print(loop:now(), "Normal quit")
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set(UWSC_VERSION_PATCH 2)
# Check the third party Libraries
find_package(Libev REQUIRED)

include_directories(${CMAKE_CURRENT_BINARY_DIR} ${LIBEV_INCLUDE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR} ${LIBEV_INCLUDE_DIR})

set(EXTRA_LIBS ${LIBEV_LIBRARY} dl)
set(SOURCE_FILES uwsc.c log.c utils.c buffer.c sha1.c base64.c ssl.c)
Expand Down Expand Up @@ -97,6 +97,8 @@ target_link_libraries(uwsc_s ${EXTRA_LIBS})
# configure a header file to pass some of the CMake settings to the source code
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)

add_subdirectory(lua)

install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/config.h
Expand Down
22 changes: 22 additions & 0 deletions src/lua/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
find_package(Lua)

set(UWSC_LUA_SUPPORT_DEFAULT "ON")

if (NOT LUA_FOUND)
set(BUILD_LUA_DEFAULT "OFF")
endif (NOT LUA_FOUND)

option(UMQTT_LUA_SUPPORT "Enable build of Lua module" ${UWSC_LUA_SUPPORT_DEFAULT})

if(UMQTT_LUA_SUPPORT)
include_directories(${LUA_INCLUDE_DIR})

add_library(uwsc-lua MODULE uwsc_lua.c)
set_target_properties(uwsc-lua PROPERTIES OUTPUT_NAME uwsc PREFIX "")

target_link_libraries(uwsc-lua uwsc)

install(TARGETS uwsc-lua
LIBRARY DESTINATION lib/lua/${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
)
endif(UMQTT_LUA_SUPPORT)
246 changes: 246 additions & 0 deletions src/lua/uwsc_lua.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/*
* Copyright (C) 2019 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utils.h"
#include "uwsc_lua.h"

#define UWSC_MT "uqmtt"

/* https://github.com/brimworks/lua-ev/blob/master/lua_ev.h#L33 */
#define EV_LOOP_MT "ev{loop}"
#define EV_UNINITIALIZED_DEFAULT_LOOP (struct ev_loop *)1

static int uwsc_lua_version(lua_State *L)
{
lua_pushinteger(L, UWSC_VERSION_MAJOR);
lua_pushinteger(L, UWSC_VERSION_MINOR);
lua_pushinteger(L, UWSC_VERSION_PATCH);

return 3;
}

static void uwsc_onmessage(struct uwsc_client *cli, void *data, size_t len, bool binary)
{
struct uwsc_client_lua *cl = container_of(cli, struct uwsc_client_lua, cli);
lua_State *L = cl->L;

lua_rawgeti(L, LUA_REGISTRYINDEX, cl->onmessage_ref);
if (!lua_isfunction(L, -1))
return;

lua_pushlstring(L, data, len);
lua_pushboolean(L, binary);

lua_call(L, 2, 0);
}

static void uwsc_onerror(struct uwsc_client *cli, int err, const char *msg)
{
struct uwsc_client_lua *cl = container_of(cli, struct uwsc_client_lua, cli);
lua_State *L = cl->L;

cl->connected = false;

lua_rawgeti(L, LUA_REGISTRYINDEX, cl->onerror_ref);
if (!lua_isfunction(L, -1))
return;

lua_pushinteger(L, err);
lua_pushstring(L, msg);

lua_call(L, 2, 0);
}

static void uwsc_onclose(struct uwsc_client *cli, int code, const char *reason)
{
struct uwsc_client_lua *cl = container_of(cli, struct uwsc_client_lua, cli);
lua_State *L = cl->L;

cl->connected = false;

lua_rawgeti(L, LUA_REGISTRYINDEX, cl->onclose_ref);
if (!lua_isfunction(L, -1))
return;

lua_pushinteger(L, code);
lua_pushstring(L, reason);

lua_call(L, 2, 0);
}

static void uwsc_onopen(struct uwsc_client *cli)
{
struct uwsc_client_lua *cl = container_of(cli, struct uwsc_client_lua, cli);
lua_State *L = cl->L;

cl->connected = true;

lua_rawgeti(L, LUA_REGISTRYINDEX, cl->onopen_ref);
if (!lua_isfunction(L, -1))
return;

lua_call(L, 0, 0);
}

static int uwsc_lua_new(lua_State *L)
{
struct ev_loop *loop = NULL;
struct uwsc_client_lua *cl;
const char *url = lua_tostring(L, 1);
int ping_interval = lua_tointeger(L, 2);
const char *extra_header = NULL;

if (lua_istable(L, 3)) {
struct ev_loop **tmp;

lua_getfield(L, 3, "loop");
if (!lua_isnil(L, -1)) {
tmp = luaL_checkudata(L, 2, EV_LOOP_MT);
if (*tmp != EV_UNINITIALIZED_DEFAULT_LOOP)
loop = *tmp;
}

lua_getfield(L, 3, "extra_header");
extra_header = lua_tostring(L, -1);
}

cl = lua_newuserdata(L, sizeof(struct uwsc_client_lua));
if (!cl) {
lua_pushnil(L);
lua_pushstring(L, "lua_newuserdata() failed");
return 2;
}

memset(cl, 0, sizeof(struct uwsc_client_lua));

luaL_getmetatable(L, UWSC_MT);
lua_setmetatable(L, -2);

if (uwsc_init(&cl->cli, loop, url, ping_interval, extra_header) < 0) {
lua_pushnil(L);
lua_pushfstring(L, "uwsc_init() failed");
return 2;
}

cl->L = L;
cl->cli.onopen = uwsc_onopen;
cl->cli.onmessage = uwsc_onmessage;
cl->cli.onerror = uwsc_onerror;
cl->cli.onclose = uwsc_onclose;

return 1;
}

static int uwsc_lua_on(lua_State *L)
{
struct uwsc_client_lua *cl = luaL_checkudata(L, 1, UWSC_MT);
const char *name = luaL_checkstring(L, 2);
int ref;

luaL_checktype(L, 3, LUA_TFUNCTION);

ref = luaL_ref(L, LUA_REGISTRYINDEX);

if (!strcmp(name, "open"))
cl->onopen_ref = ref;
else if (!strcmp(name, "message"))
cl->onmessage_ref = ref;
else if (!strcmp(name, "error"))
cl->onerror_ref = ref;
else if (!strcmp(name, "close"))
cl->onclose_ref = ref;
else
luaL_argcheck(L, false, 2, "available event name: open message error close");

return 0;
}

static int __uwsc_lua_send(lua_State *L, int op)
{
struct uwsc_client_lua *cl = luaL_checkudata(L, 1, UWSC_MT);
size_t len;
const void *data = luaL_checklstring(L, 2, &len);

cl->cli.send(&cl->cli, data, len, op);

return 0;
}

static int uwsc_lua_send(lua_State *L)
{
int op = lua_tointeger(L, 3);

return __uwsc_lua_send(L, op);
}

static int uwsc_lua_send_text(lua_State *L)
{
return __uwsc_lua_send(L, UWSC_OP_TEXT);
}

static int uwsc_lua_send_binary(lua_State *L)
{
return __uwsc_lua_send(L, UWSC_OP_BINARY);
}

static int uwsc_lua_gc(lua_State *L)
{
struct uwsc_client_lua *cl = luaL_checkudata(L, 1, UWSC_MT);

if (cl->connected) {
cl->cli.send_close(&cl->cli, UWSC_CLOSE_STATUS_NORMAL, "");
cl->cli.free(&cl->cli);
cl->connected = false;
}

return 0;
}

static const luaL_Reg uwsc_meta[] = {
{"on", uwsc_lua_on},
{"send", uwsc_lua_send},
{"send_text", uwsc_lua_send_text},
{"send_binary", uwsc_lua_send_binary},
{"__gc", uwsc_lua_gc},
{NULL, NULL}
};

static const luaL_Reg uwsc_fun[] = {
{"new", uwsc_lua_new},
{"version", uwsc_lua_version},
{NULL, NULL}
};

int luaopen_uwsc(lua_State *L)
{
/* metatable.__index = metatable */
luaL_newmetatable(L, UWSC_MT);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, uwsc_meta, 0);

lua_newtable(L);
luaL_setfuncs(L, uwsc_fun, 0);

return 1;
}
Loading

0 comments on commit 236e70f

Please sign in to comment.