blob: 07524ab6a02ecfea9885c19d64f5e0ec961cece2 [file] [log] [blame]
Roger Meier6cf0ffc2014-04-05 00:45:42 +02001//
2// Licensed to the Apache Software Foundation (ASF) under one
3// or more contributor license agreements. See the NOTICE file
4// distributed with this work for additional information
5// regarding copyright ownership. The ASF licenses this file
6// to you under the Apache License, Version 2.0 (the
7// "License"); you may not use this file except in compliance
8// with the License. You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing,
13// software distributed under the License is distributed on an
14// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15// KIND, either express or implied. See the License for the
16// specific language governing permissions and limitations
17// under the License.
18//
19
20#include <lua.h>
21#include <lauxlib.h>
22
23#include <unistd.h>
24#include "string.h"
25#include "socket.h"
26
27////////////////////////////////////////////////////////////////////////////////
28
29static const char *SOCKET_ANY = "__thrift_socket_any";
30static const char *SOCKET_CONN = "__thrift_socket_connected";
31
32static const char *SOCKET_GENERIC = "__thrift_socket_generic";
33static const char *SOCKET_CLIENT = "__thrift_socket_client";
34static const char *SOCKET_SERVER = "__thrift_socket_server";
35
36static const char *DEFAULT_HOST = "localhost";
37
38typedef struct __t_tcp {
39 t_socket sock;
40 int timeout; // Milliseconds
41} t_tcp;
42typedef t_tcp *p_tcp;
43
44////////////////////////////////////////////////////////////////////////////////
45// Util
46
47static void throw_argerror(lua_State *L, int index, const char *expected) {
48 char msg[256];
49 sprintf(msg, "%s expected, got %s", expected, luaL_typename(L, index));
50 luaL_argerror(L, index, msg);
51}
52
53static void *checkgroup(lua_State *L, int index, const char *groupname) {
54 if (!lua_getmetatable(L, index)) {
55 throw_argerror(L, index, groupname);
56 }
57
58 lua_pushstring(L, groupname);
59 lua_rawget(L, -2);
60 if (lua_isnil(L, -1)) {
61 lua_pop(L, 2);
62 throw_argerror(L, index, groupname);
63 } else {
64 lua_pop(L, 2);
65 return lua_touserdata(L, index);
66 }
67 return NULL; // Not reachable
68}
69
70static void *checktype(lua_State *L, int index, const char *typename) {
71 if (strcmp(typename, SOCKET_ANY) == 0 ||
72 strcmp(typename, SOCKET_CONN) == 0) {
73 return checkgroup(L, index, typename);
74 } else {
75 return luaL_checkudata(L, index, typename);
76 }
77}
78
79static void settype(lua_State *L, int index, const char *typename) {
80 luaL_getmetatable(L, typename);
81 lua_setmetatable(L, index);
82}
83
84#define LUA_SUCCESS_RETURN(L) \
85 lua_pushnumber(L, 1); \
86 return 1
87
88#define LUA_CHECK_RETURN(L, err) \
89 if (err) { \
90 lua_pushnil(L); \
91 lua_pushstring(L, err); \
92 return 2; \
93 } \
94 LUA_SUCCESS_RETURN(L)
95
96////////////////////////////////////////////////////////////////////////////////
97
98static int l_socket_create(lua_State *L);
99static int l_socket_destroy(lua_State *L);
100static int l_socket_settimeout(lua_State *L);
101static int l_socket_getsockinfo(lua_State *L);
102
103static int l_socket_accept(lua_State *L);
104static int l_socket_listen(lua_State *L);
105
106static int l_socket_create_and_connect(lua_State *L);
107static int l_socket_connect(lua_State *L);
108static int l_socket_send(lua_State *L);
109static int l_socket_receive(lua_State *L);
110
111////////////////////////////////////////////////////////////////////////////////
112
113static const struct luaL_Reg methods_generic[] = {
114 {"destroy", l_socket_destroy},
115 {"settimeout", l_socket_settimeout},
116 {"getsockinfo", l_socket_getsockinfo},
117 {"listen", l_socket_listen},
118 {"connect", l_socket_connect},
119 {NULL, NULL}
120};
121
122static const struct luaL_Reg methods_server[] = {
123 {"destroy", l_socket_destroy},
124 {"getsockinfo", l_socket_getsockinfo},
125 {"accept", l_socket_accept},
126 {"send", l_socket_send},
127 {"receive", l_socket_receive},
128 {NULL, NULL}
129};
130
131static const struct luaL_Reg methods_client[] = {
132 {"destroy", l_socket_destroy},
133 {"settimeout", l_socket_settimeout},
134 {"getsockinfo", l_socket_getsockinfo},
135 {"send", l_socket_send},
136 {"receive", l_socket_receive},
137 {NULL, NULL}
138};
139
140static const struct luaL_Reg funcs_luasocket[] = {
141 {"create", l_socket_create},
142 {"create_and_connect", l_socket_create_and_connect},
143 {NULL, NULL}
144};
145
146////////////////////////////////////////////////////////////////////////////////
147
148// Check/enforce inheritance
149static void add_to_group(lua_State *L,
150 const char *metatablename,
151 const char *groupname) {
152 luaL_getmetatable(L, metatablename); // mt
153 lua_pushstring(L, groupname); // mt, "name"
154 lua_pushboolean(L, 1); // mt, "name", true
155 lua_rawset(L, -3); // mt
156 lua_pop(L, 1);
157}
158
159static void set_methods(lua_State *L,
160 const char *metatablename,
161 const struct luaL_Reg *methods) {
162 luaL_getmetatable(L, metatablename); // mt
163 // Create the __index table
164 lua_pushstring(L, "__index"); // mt, "__index"
165 lua_newtable(L); // mt, "__index", t
166 for (; methods->name; methods++) {
167 lua_pushstring(L, methods->name); // mt, "__index", t, "name"
168 lua_pushcfunction(L, methods->func); // mt, "__index", t, "name", func
169 lua_rawset(L, -3); // mt, "__index", t
170 }
171 lua_rawset(L, -3); // mt
172 lua_pop(L, 1);
173}
174
175int luaopen_libluasocket(lua_State *L) {
176 luaL_newmetatable(L, SOCKET_GENERIC);
177 luaL_newmetatable(L, SOCKET_CLIENT);
178 luaL_newmetatable(L, SOCKET_SERVER);
179 lua_pop(L, 3);
180 add_to_group(L, SOCKET_GENERIC, SOCKET_ANY);
181 add_to_group(L, SOCKET_CLIENT, SOCKET_ANY);
182 add_to_group(L, SOCKET_SERVER, SOCKET_ANY);
183 add_to_group(L, SOCKET_CLIENT, SOCKET_CONN);
184 add_to_group(L, SOCKET_SERVER, SOCKET_CONN);
185 set_methods(L, SOCKET_GENERIC, methods_generic);
186 set_methods(L, SOCKET_CLIENT, methods_client);
187 set_methods(L, SOCKET_SERVER, methods_server);
Thomaseb684d32024-07-28 15:32:23 +0200188#if LUA_VERSION_NUM >= 502
189 lua_newtable(L);
190 luaL_setfuncs(L, funcs_luasocket, 0);
191#else
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200192 luaL_register(L, "luasocket", funcs_luasocket);
Thomaseb684d32024-07-28 15:32:23 +0200193#endif
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200194 return 1;
195}
196
197////////////////////////////////////////////////////////////////////////////////
198// General
199
200// sock,err create(bind_host, bind_port)
201// sock,err create(bind_host) -> any port
202// sock,err create() -> any port on localhost
203static int l_socket_create(lua_State *L) {
204 const char *err;
205 t_socket sock;
206 const char *addr = lua_tostring(L, 1);
207 if (!addr) {
208 addr = DEFAULT_HOST;
209 }
210 unsigned short port = lua_tonumber(L, 2);
211 err = tcp_create(&sock);
212 if (!err) {
213 err = tcp_bind(&sock, addr, port); // bind on create
214 if (err) {
215 tcp_destroy(&sock);
216 } else {
217 p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
218 settype(L, -2, SOCKET_GENERIC);
219 socket_setnonblocking(&sock);
220 tcp->sock = sock;
221 tcp->timeout = 0;
222 return 1; // Return userdata
223 }
224 }
225 LUA_CHECK_RETURN(L, err);
226}
227
228// destroy()
229static int l_socket_destroy(lua_State *L) {
230 p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY);
231 const char *err = tcp_destroy(&tcp->sock);
232 LUA_CHECK_RETURN(L, err);
233}
234
235// send(socket, data)
236static int l_socket_send(lua_State *L) {
237 p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN);
238 p_tcp tcp = (p_tcp) checktype(L, 2, SOCKET_CONN);
239 size_t len;
240 const char *data = luaL_checklstring(L, 3, &len);
241 const char *err =
242 tcp_send(&tcp->sock, data, len, tcp->timeout);
243 LUA_CHECK_RETURN(L, err);
244}
245
246#define LUA_READ_STEP 8192
247static int l_socket_receive(lua_State *L) {
248 p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN);
249 p_tcp handle = (p_tcp) checktype(L, 2, SOCKET_CONN);
250 size_t len = luaL_checknumber(L, 3);
251 char buf[LUA_READ_STEP];
252 const char *err = NULL;
253 int received;
254 size_t got = 0, step = 0;
255 luaL_Buffer b;
256
257 luaL_buffinit(L, &b);
258 do {
259 step = (LUA_READ_STEP < len - got ? LUA_READ_STEP : len - got);
260 err = tcp_raw_receive(&handle->sock, buf, step, self->timeout, &received);
261 if (err == NULL) {
262 luaL_addlstring(&b, buf, received);
263 got += received;
264 }
265 } while (err == NULL && got < len);
266
267 if (err) {
268 lua_pushnil(L);
269 lua_pushstring(L, err);
270 return 2;
271 }
272 luaL_pushresult(&b);
273 return 1;
274}
275
276// settimeout(timeout)
277static int l_socket_settimeout(lua_State *L) {
278 p_tcp self = (p_tcp) checktype(L, 1, SOCKET_ANY);
279 int timeout = luaL_checknumber(L, 2);
280 self->timeout = timeout;
281 LUA_SUCCESS_RETURN(L);
282}
283
284// table getsockinfo()
285static int l_socket_getsockinfo(lua_State *L) {
286 char buf[256];
287 short port = 0;
288 p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY);
289 if (socket_get_info(&tcp->sock, &port, buf, 256) == SUCCESS) {
290 lua_newtable(L); // t
291 lua_pushstring(L, "host"); // t, "host"
292 lua_pushstring(L, buf); // t, "host", buf
293 lua_rawset(L, -3); // t
294 lua_pushstring(L, "port"); // t, "port"
295 lua_pushnumber(L, port); // t, "port", port
296 lua_rawset(L, -3); // t
297 return 1;
298 }
299 return 0;
300}
301
302////////////////////////////////////////////////////////////////////////////////
303// Server
304
305// accept()
306static int l_socket_accept(lua_State *L) {
307 const char *err;
308 p_tcp self = (p_tcp) checktype(L, 1, SOCKET_SERVER);
309 t_socket sock;
310 err = tcp_accept(&self->sock, &sock, self->timeout);
311 if (!err) { // Success
312 // Create a reference to the client
313 p_tcp client = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
314 settype(L, 2, SOCKET_CLIENT);
315 socket_setnonblocking(&sock);
316 client->sock = sock;
317 client->timeout = self->timeout;
318 return 1;
319 }
320 LUA_CHECK_RETURN(L, err);
321}
322
323static int l_socket_listen(lua_State *L) {
324 const char* err;
325 p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC);
326 int backlog = 10;
327 err = tcp_listen(&tcp->sock, backlog);
328 if (!err) {
329 // Set the current as a server
330 settype(L, 1, SOCKET_SERVER); // Now a server
331 }
332 LUA_CHECK_RETURN(L, err);
333}
334
335////////////////////////////////////////////////////////////////////////////////
336// Client
337
338// create_and_connect(host, port, timeout)
339extern double __gettime();
340static int l_socket_create_and_connect(lua_State *L) {
341 const char* err = NULL;
342 double end;
343 t_socket sock;
344 const char *host = luaL_checkstring(L, 1);
345 unsigned short port = luaL_checknumber(L, 2);
346 int timeout = luaL_checknumber(L, 3);
347
348 // Create and connect loop for timeout milliseconds
349 end = __gettime() + timeout/1000;
350 do {
Jeffrey Hane872b352020-09-24 10:41:12 -0700351 // Create and connect the socket
352 err = tcp_create_and_connect(&sock, host, port, timeout);
353 if (err) {
354 tcp_destroy(&sock);
355 usleep(100000); // sleep for 100ms
356 } else {
357 p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
358 settype(L, -2, SOCKET_CLIENT);
359 socket_setnonblocking(&sock);
360 tcp->sock = sock;
361 tcp->timeout = timeout;
362 return 1; // Return userdata
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200363 }
364 } while (err && __gettime() < end);
365
366 LUA_CHECK_RETURN(L, err);
367}
368
369// connect(host, port)
370static int l_socket_connect(lua_State *L) {
371 const char *err;
372 p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC);
373 const char *host = luaL_checkstring(L, 2);
374 unsigned short port = luaL_checknumber(L, 3);
375 err = tcp_connect(&tcp->sock, host, port, tcp->timeout);
376 if (!err) {
377 settype(L, 1, SOCKET_CLIENT); // Now a client
378 }
379 LUA_CHECK_RETURN(L, err);
380}