// // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // #include #include #include #include "string.h" #include "socket.h" //////////////////////////////////////////////////////////////////////////////// static const char *SOCKET_ANY = "__thrift_socket_any"; static const char *SOCKET_CONN = "__thrift_socket_connected"; static const char *SOCKET_GENERIC = "__thrift_socket_generic"; static const char *SOCKET_CLIENT = "__thrift_socket_client"; static const char *SOCKET_SERVER = "__thrift_socket_server"; static const char *DEFAULT_HOST = "localhost"; typedef struct __t_tcp { t_socket sock; int timeout; // Milliseconds } t_tcp; typedef t_tcp *p_tcp; //////////////////////////////////////////////////////////////////////////////// // Util static void throw_argerror(lua_State *L, int index, const char *expected) { char msg[256]; sprintf(msg, "%s expected, got %s", expected, luaL_typename(L, index)); luaL_argerror(L, index, msg); } static void *checkgroup(lua_State *L, int index, const char *groupname) { if (!lua_getmetatable(L, index)) { throw_argerror(L, index, groupname); } lua_pushstring(L, groupname); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 2); throw_argerror(L, index, groupname); } else { lua_pop(L, 2); return lua_touserdata(L, index); } return NULL; // Not reachable } static void *checktype(lua_State *L, int index, const char *typename) { if (strcmp(typename, SOCKET_ANY) == 0 || strcmp(typename, SOCKET_CONN) == 0) { return checkgroup(L, index, typename); } else { return luaL_checkudata(L, index, typename); } } static void settype(lua_State *L, int index, const char *typename) { luaL_getmetatable(L, typename); lua_setmetatable(L, index); } #define LUA_SUCCESS_RETURN(L) \ lua_pushnumber(L, 1); \ return 1 #define LUA_CHECK_RETURN(L, err) \ if (err) { \ lua_pushnil(L); \ lua_pushstring(L, err); \ return 2; \ } \ LUA_SUCCESS_RETURN(L) //////////////////////////////////////////////////////////////////////////////// static int l_socket_create(lua_State *L); static int l_socket_destroy(lua_State *L); static int l_socket_settimeout(lua_State *L); static int l_socket_getsockinfo(lua_State *L); static int l_socket_accept(lua_State *L); static int l_socket_listen(lua_State *L); static int l_socket_create_and_connect(lua_State *L); static int l_socket_connect(lua_State *L); static int l_socket_send(lua_State *L); static int l_socket_receive(lua_State *L); //////////////////////////////////////////////////////////////////////////////// static const struct luaL_Reg methods_generic[] = { {"destroy", l_socket_destroy}, {"settimeout", l_socket_settimeout}, {"getsockinfo", l_socket_getsockinfo}, {"listen", l_socket_listen}, {"connect", l_socket_connect}, {NULL, NULL} }; static const struct luaL_Reg methods_server[] = { {"destroy", l_socket_destroy}, {"getsockinfo", l_socket_getsockinfo}, {"accept", l_socket_accept}, {"send", l_socket_send}, {"receive", l_socket_receive}, {NULL, NULL} }; static const struct luaL_Reg methods_client[] = { {"destroy", l_socket_destroy}, {"settimeout", l_socket_settimeout}, {"getsockinfo", l_socket_getsockinfo}, {"send", l_socket_send}, {"receive", l_socket_receive}, {NULL, NULL} }; static const struct luaL_Reg funcs_luasocket[] = { {"create", l_socket_create}, {"create_and_connect", l_socket_create_and_connect}, {NULL, NULL} }; //////////////////////////////////////////////////////////////////////////////// // Check/enforce inheritance static void add_to_group(lua_State *L, const char *metatablename, const char *groupname) { luaL_getmetatable(L, metatablename); // mt lua_pushstring(L, groupname); // mt, "name" lua_pushboolean(L, 1); // mt, "name", true lua_rawset(L, -3); // mt lua_pop(L, 1); } static void set_methods(lua_State *L, const char *metatablename, const struct luaL_Reg *methods) { luaL_getmetatable(L, metatablename); // mt // Create the __index table lua_pushstring(L, "__index"); // mt, "__index" lua_newtable(L); // mt, "__index", t for (; methods->name; methods++) { lua_pushstring(L, methods->name); // mt, "__index", t, "name" lua_pushcfunction(L, methods->func); // mt, "__index", t, "name", func lua_rawset(L, -3); // mt, "__index", t } lua_rawset(L, -3); // mt lua_pop(L, 1); } int luaopen_libluasocket(lua_State *L) { luaL_newmetatable(L, SOCKET_GENERIC); luaL_newmetatable(L, SOCKET_CLIENT); luaL_newmetatable(L, SOCKET_SERVER); lua_pop(L, 3); add_to_group(L, SOCKET_GENERIC, SOCKET_ANY); add_to_group(L, SOCKET_CLIENT, SOCKET_ANY); add_to_group(L, SOCKET_SERVER, SOCKET_ANY); add_to_group(L, SOCKET_CLIENT, SOCKET_CONN); add_to_group(L, SOCKET_SERVER, SOCKET_CONN); set_methods(L, SOCKET_GENERIC, methods_generic); set_methods(L, SOCKET_CLIENT, methods_client); set_methods(L, SOCKET_SERVER, methods_server); luaL_register(L, "luasocket", funcs_luasocket); return 1; } //////////////////////////////////////////////////////////////////////////////// // General // sock,err create(bind_host, bind_port) // sock,err create(bind_host) -> any port // sock,err create() -> any port on localhost static int l_socket_create(lua_State *L) { const char *err; t_socket sock; const char *addr = lua_tostring(L, 1); if (!addr) { addr = DEFAULT_HOST; } unsigned short port = lua_tonumber(L, 2); err = tcp_create(&sock); if (!err) { err = tcp_bind(&sock, addr, port); // bind on create if (err) { tcp_destroy(&sock); } else { p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); settype(L, -2, SOCKET_GENERIC); socket_setnonblocking(&sock); tcp->sock = sock; tcp->timeout = 0; return 1; // Return userdata } } LUA_CHECK_RETURN(L, err); } // destroy() static int l_socket_destroy(lua_State *L) { p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY); const char *err = tcp_destroy(&tcp->sock); LUA_CHECK_RETURN(L, err); } // send(socket, data) static int l_socket_send(lua_State *L) { p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN); p_tcp tcp = (p_tcp) checktype(L, 2, SOCKET_CONN); size_t len; const char *data = luaL_checklstring(L, 3, &len); const char *err = tcp_send(&tcp->sock, data, len, tcp->timeout); LUA_CHECK_RETURN(L, err); } #define LUA_READ_STEP 8192 static int l_socket_receive(lua_State *L) { p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN); p_tcp handle = (p_tcp) checktype(L, 2, SOCKET_CONN); size_t len = luaL_checknumber(L, 3); char buf[LUA_READ_STEP]; const char *err = NULL; int received; size_t got = 0, step = 0; luaL_Buffer b; luaL_buffinit(L, &b); do { step = (LUA_READ_STEP < len - got ? LUA_READ_STEP : len - got); err = tcp_raw_receive(&handle->sock, buf, step, self->timeout, &received); if (err == NULL) { luaL_addlstring(&b, buf, received); got += received; } } while (err == NULL && got < len); if (err) { lua_pushnil(L); lua_pushstring(L, err); return 2; } luaL_pushresult(&b); return 1; } // settimeout(timeout) static int l_socket_settimeout(lua_State *L) { p_tcp self = (p_tcp) checktype(L, 1, SOCKET_ANY); int timeout = luaL_checknumber(L, 2); self->timeout = timeout; LUA_SUCCESS_RETURN(L); } // table getsockinfo() static int l_socket_getsockinfo(lua_State *L) { char buf[256]; short port = 0; p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY); if (socket_get_info(&tcp->sock, &port, buf, 256) == SUCCESS) { lua_newtable(L); // t lua_pushstring(L, "host"); // t, "host" lua_pushstring(L, buf); // t, "host", buf lua_rawset(L, -3); // t lua_pushstring(L, "port"); // t, "port" lua_pushnumber(L, port); // t, "port", port lua_rawset(L, -3); // t return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////// // Server // accept() static int l_socket_accept(lua_State *L) { const char *err; p_tcp self = (p_tcp) checktype(L, 1, SOCKET_SERVER); t_socket sock; err = tcp_accept(&self->sock, &sock, self->timeout); if (!err) { // Success // Create a reference to the client p_tcp client = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); settype(L, 2, SOCKET_CLIENT); socket_setnonblocking(&sock); client->sock = sock; client->timeout = self->timeout; return 1; } LUA_CHECK_RETURN(L, err); } static int l_socket_listen(lua_State *L) { const char* err; p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC); int backlog = 10; err = tcp_listen(&tcp->sock, backlog); if (!err) { // Set the current as a server settype(L, 1, SOCKET_SERVER); // Now a server } LUA_CHECK_RETURN(L, err); } //////////////////////////////////////////////////////////////////////////////// // Client // create_and_connect(host, port, timeout) extern double __gettime(); static int l_socket_create_and_connect(lua_State *L) { const char* err = NULL; double end; t_socket sock; const char *host = luaL_checkstring(L, 1); unsigned short port = luaL_checknumber(L, 2); int timeout = luaL_checknumber(L, 3); // Create and connect loop for timeout milliseconds end = __gettime() + timeout/1000; do { // Create the socket err = tcp_create(&sock); if (!err) { // Connect err = tcp_connect(&sock, host, port, timeout); if (err) { tcp_destroy(&sock); usleep(100000); // sleep for 100ms } else { p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); settype(L, -2, SOCKET_CLIENT); socket_setnonblocking(&sock); tcp->sock = sock; tcp->timeout = timeout; return 1; // Return userdata } } } while (err && __gettime() < end); LUA_CHECK_RETURN(L, err); } // connect(host, port) static int l_socket_connect(lua_State *L) { const char *err; p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC); const char *host = luaL_checkstring(L, 2); unsigned short port = luaL_checknumber(L, 3); err = tcp_connect(&tcp->sock, host, port, tcp->timeout); if (!err) { settype(L, 1, SOCKET_CLIENT); // Now a client } LUA_CHECK_RETURN(L, err); }