1 /*
2 * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdarg.h>
9
10 #include "amp_config.h"
11 #include "amp_platform.h"
12 #include "amp_defines.h"
13 #include "amp_task.h"
14 #include "amp_memory.h"
15 #include "aos_system.h"
16 #include "aos_tcp.h"
17 #include "quickjs.h"
18 #include "quickjs_addon_common.h"
19
20 #define MOD_STR "TCP"
21 #define MAX_TCP_RECV_LEN 256
22 #define MAX_TCP_RECV_TIMEOUT 200
23
24 typedef struct {
25 int sock_id;
26 char *msg;
27 int msg_len;
28 JSValue js_cb_ref;
29 } tcp_send_param_t;
30
31 typedef struct {
32 int ret;
33 JSValue js_cb_ref;
34 } tcp_send_notify_param_t;
35
36 typedef struct {
37 int sock_id;
38 JSValue js_cb_ref;
39 } tcp_recv_param_t;
40
41 typedef struct {
42 char *host;
43 int port;
44 JSValue js_cb_ref;
45 } tcp_create_param_t;
46
47 typedef struct {
48 int ret;
49 JSValue js_cb_ref;
50 } tcp_create_notify_param_t;
51
52 typedef struct {
53 char buf[MAX_TCP_RECV_LEN];
54 int recv_len;
55 JSValue js_cb_ref;
56 } tcp_recv_notify_param_t;
57
58 static char g_tcp_close_flag = 0;
59 static char g_tcp_recv_flag = 0;
60 static aos_sem_t g_tcp_close_sem = NULL;
61 static JSClassID js_tcp_class_id;
62
tcp_create_notify(void * pdata)63 static void tcp_create_notify(void *pdata)
64 {
65 tcp_create_notify_param_t *p = (tcp_create_notify_param_t *)pdata;
66 JSContext *ctx = js_get_context();
67 JSValue args = JS_NewInt32(ctx, p->ret);
68 JSValue val = JS_Call(ctx, p->js_cb_ref, JS_UNDEFINED, 1, &args);
69 JS_FreeValue(ctx, args);
70 if (JS_IsException(val)) {
71 js_std_dump_error(ctx);
72 }
73 JS_FreeValue(ctx, val);
74 JS_FreeValue(ctx, p->js_cb_ref);
75 amp_free(p);
76 }
77
tcp_create_routine(tcp_create_param_t * create_param)78 static int tcp_create_routine(tcp_create_param_t *create_param)
79 {
80 int ret = -1;
81
82 JSContext *ctx = js_get_context();
83 tcp_create_notify_param_t *p = amp_calloc(1, sizeof(tcp_create_notify_param_t));
84 if (!p) {
85 amp_warn(MOD_STR, "allocate memory failed");
86 JS_FreeValue(ctx, create_param->js_cb_ref);
87 goto out;
88 }
89
90 ret = aos_tcp_establish(create_param->host, create_param->port);
91 if (ret < 0) {
92 amp_warn(MOD_STR, "tcp establish failed");
93 amp_free(p);
94 JS_FreeValue(ctx, create_param->js_cb_ref);
95 goto out;
96 }
97
98 amp_debug(MOD_STR, "sock_id = %d", ret);
99 p->js_cb_ref = create_param->js_cb_ref;
100 p->ret = ret;
101 amp_task_schedule_call(tcp_create_notify, p);
102
103 out:
104 JS_FreeCString(ctx, create_param->host);
105 amp_free(create_param);
106 return ret;
107 }
108
109 /*************************************************************************************
110 * Function: native_udp_create_socket
111 * Description: js native addon for TCP.createSocket();
112 * Called by: js api
113 * Input: none
114 * Output: return socket fd when create socket success,
115 * return error number when create socket fail
116 **************************************************************************************/
native_tcp_create_socket(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)117 static JSValue native_tcp_create_socket(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
118 {
119 int err;
120 int ret = -1;
121 int port = 0;
122 char *host = NULL;
123 tcp_create_param_t *create_param = NULL;
124
125 amp_debug(MOD_STR, "native_tcp_create_socket");
126 if (!JS_IsObject(argv[0]) || !JS_IsFunction(ctx, argv[1])) {
127 amp_warn(MOD_STR, "parameter must be (object, function)");
128 goto out;
129 }
130
131 JSValue j_host = JS_GetPropertyStr(ctx, argv[0], "host");
132 host = JS_ToCString(ctx, j_host);
133 JS_FreeValue(ctx, j_host);
134
135 JSValue j_port = JS_GetPropertyStr(ctx, argv[0], "port");
136 JS_ToInt32(ctx, &port, j_port);
137 JS_FreeValue(ctx, j_port);
138
139 amp_debug(MOD_STR, "host: %s, port: %d", host, port);
140 create_param = (tcp_create_param_t *)amp_malloc(sizeof(*create_param));
141 if (!create_param) {
142 amp_error(MOD_STR, "allocate memory failed");
143 JS_FreeCString(ctx, host);
144 goto out;
145 }
146
147 create_param->host = host;
148 create_param->port = port;
149 create_param->js_cb_ref = JS_DupValue(ctx, argv[1]);
150
151 ret = tcp_create_routine(create_param);
152 if (ret < 0) {
153 amp_warn(MOD_STR, "tcp create socket failed");
154 goto out;
155 }
156
157 out:
158 return JS_NewInt32(ctx, ret);
159 }
160
tcp_send_notify(void * pdata)161 static void tcp_send_notify(void *pdata)
162 {
163 tcp_send_notify_param_t *p = (tcp_send_notify_param_t *)pdata;
164 JSContext *ctx = js_get_context();
165 JSValue args = JS_NewInt32(ctx, p->ret);
166 JSValue val = JS_Call(ctx, p->js_cb_ref, JS_UNDEFINED, 1, &args);
167 if (JS_IsException(val)) {
168 js_std_dump_error(ctx);
169 }
170 JS_FreeValue(ctx, args);
171 JS_FreeValue(ctx, val);
172 JS_FreeValue(ctx, p->js_cb_ref);
173 amp_free(p);
174 }
175
176 /*************************************************************************************
177 * Function: udp_send_routin
178 * Description: create a task for blocking sendto call
179 * Called by:
180 **************************************************************************************/
tcp_send_routine(tcp_send_param_t * send_param)181 static int tcp_send_routine(tcp_send_param_t *send_param)
182 {
183 JSContext *ctx = js_get_context();
184 int sock_id = send_param->sock_id;
185 int ret = aos_tcp_write(sock_id, send_param->msg, send_param->msg_len, 0);
186 if (ret < 0) {
187 JS_FreeValue(ctx, send_param->js_cb_ref);
188 goto out;
189 }
190
191 tcp_send_notify_param_t *p = amp_calloc(1, sizeof(tcp_send_notify_param_t));
192 if (!p) {
193 amp_warn(MOD_STR, "allocate memory failed");
194 JS_FreeValue(ctx, send_param->js_cb_ref);
195 goto out;
196 }
197
198 p->js_cb_ref = send_param->js_cb_ref;
199 p->ret = ret;
200 amp_task_schedule_call(tcp_send_notify, p);
201 ret = 0;
202
203 out:
204 JS_FreeCString(ctx, send_param->msg);
205 amp_free(send_param);
206 return ret;
207 }
208
209 /*************************************************************************************
210 * Function: native_udp_sendto
211 * Description: js native addon for
212 *TCP.send(sock_id,option,buffer_array,function(ret){}) Called by: js api
213 * Input: sock_id: interger
214 * options: is a object include options.ip and options.port
215 * buffer_array: is a array which include message to send
216 * function(ret): is the callback function which has a ret input
217 *param Output: return send msg length when send success return error
218 *number when send fail
219 **************************************************************************************/
220
native_tcp_send(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)221 static JSValue native_tcp_send(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
222 {
223 int ret = -1;
224 int sock_id;
225 int i;
226 int msg_len;
227 char *msg;
228
229 if (!JS_IsNumber(argv[0]) || !JS_IsString(argv[1]) ||
230 !JS_IsFunction(ctx, argv[2])) {
231 amp_warn(MOD_STR, "parameter must be (number, string, function)");
232 goto out;
233 }
234
235 JS_ToInt32(ctx, &sock_id, argv[0]);
236 if (sock_id <= 0) {
237 amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
238 goto out;
239 }
240
241 msg = JS_ToCString(ctx, argv[1]);
242 msg_len = strlen(msg);
243 amp_debug(MOD_STR, "msg is:%s, length is: %d", msg, msg_len);
244
245 tcp_send_param_t *send_param = (tcp_send_param_t *)amp_malloc(sizeof(*send_param));
246 if (!send_param) {
247 amp_error(MOD_STR, "allocate memory failed");
248 JS_FreeCString(ctx, msg);
249 goto out;
250 }
251 send_param->sock_id = sock_id;
252 send_param->msg = msg;
253 send_param->msg_len = msg_len;
254 send_param->js_cb_ref = JS_DupValue(ctx, argv[2]);
255 tcp_send_routine(send_param);
256
257 out:
258 return JS_NewInt32(ctx, ret);
259 }
260
tcp_recv_notify(void * pdata)261 static void tcp_recv_notify(void *pdata)
262 {
263 int i = 0;
264 tcp_recv_notify_param_t *p = (tcp_recv_notify_param_t *)pdata;
265 JSContext *ctx = js_get_context();
266
267 JSValue args[2];
268 args[0] = JS_NewInt32(ctx, p->recv_len);
269 args[1] = JS_NewStringLen(ctx, p->buf, p->recv_len);
270
271 JSValue val = JS_Call(ctx, p->js_cb_ref, JS_UNDEFINED, 2, args);
272 JS_FreeValue(ctx, args[0]);
273 JS_FreeValue(ctx, args[1]);
274 if (JS_IsException(val)) {
275 js_std_dump_error(ctx);
276 }
277 JS_FreeValue(ctx, val);
278 }
279
280 /*************************************************************************************
281 * Function: udp_recv_routine
282 * Description: create a task for blocking recvfrom call
283 * Called by:
284 **************************************************************************************/
tcp_recv_routine(void * arg)285 static void tcp_recv_routine(void *arg)
286 {
287 tcp_recv_param_t *recv_param = (tcp_recv_param_t *)arg;
288 int sock_id;
289 JSContext *ctx = js_get_context();
290
291 g_tcp_recv_flag = 1;
292 sock_id = recv_param->sock_id;
293 tcp_recv_notify_param_t *p = amp_calloc(1, sizeof(*p));
294 if (!p) {
295 amp_warn(MOD_STR, "allocate memory failed");
296 goto out;
297 }
298
299 p->js_cb_ref = recv_param->js_cb_ref;
300
301 while (1) {
302 p->recv_len = aos_tcp_read(sock_id, p->buf, sizeof(p->buf), MAX_TCP_RECV_TIMEOUT);
303 if (p->recv_len > 0) {
304 amp_task_schedule_call(tcp_recv_notify, p);
305 }
306
307 if (p->recv_len < 0) {
308 amp_warn(MOD_STR, "connection closed: %d", p->recv_len);
309 break;
310 }
311
312 if (g_tcp_close_flag) {
313 amp_warn(MOD_STR, "close tcp connect");
314 break;
315 }
316 }
317 aos_tcp_destroy(sock_id);
318
319 out:
320 JS_FreeValue(ctx, recv_param->js_cb_ref);
321 amp_free(recv_param);
322 if (p)
323 amp_free(p);
324
325 g_tcp_recv_flag = 0;
326 aos_sem_signal(&g_tcp_close_sem);
327 aos_task_exit(0);
328 return;
329 }
330
331 /*************************************************************************************
332 * Function: native_udp_recvfrom
333 * Description: js native addon for
334 * TCP.recv(sock_id,function(length, buffer_array, src_ip,
335 *src_port){}) Called by: js api Input: sock_id: interger
336 * function(length, buffer_array, src_ip, src_port): the callback
337 *function length: the recv msg length buffer_array: the recv msg buffer src_ip:
338 *the peer ip string src_port: the peer port which is a interger
339 *
340 * Output: return 0 when TCP.recv call ok
341 * return error number TCP.recv call fail
342 **************************************************************************************/
native_tcp_receive(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)343 static JSValue native_tcp_receive(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
344 {
345 int ret = -1;
346 int sock_id = 0;
347 aos_task_t tcp_recv_task;
348 tcp_recv_param_t *recv_param;
349
350 if (!JS_IsNumber(argv[0]) || !JS_IsFunction(ctx, argv[1])) {
351 amp_warn(MOD_STR, "parameter must be number and function");
352 goto out;
353 }
354
355 JS_ToInt32(ctx, &sock_id, argv[0]);
356 if (sock_id < 0) {
357 amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
358 goto out;
359 }
360
361 amp_debug(MOD_STR, "sock_id: %d", sock_id);
362 recv_param = (tcp_recv_param_t *)amp_calloc(1, sizeof(*recv_param));
363 if (!recv_param) {
364 amp_warn(MOD_STR, "allocate memory failed");
365 goto out;
366 }
367
368 recv_param->sock_id = sock_id;
369 recv_param->js_cb_ref = JS_DupValue(ctx, argv[1]);
370
371 ret = aos_task_new_ext(&tcp_recv_task, "amp_tcp_recv_task", tcp_recv_routine, recv_param, 1024 * 4, ADDON_TSK_PRIORRITY);
372 if (ret != 0) {
373 amp_warn(MOD_STR, "tcp recv task error");
374 goto out;
375 }
376
377 out:
378 return JS_NewInt32(ctx, ret);
379 }
380
381 /*************************************************************************************
382 * Function: native_tcp_close
383 * Description: js native addon for
384 * TCP.close(sock_id)
385 * Called by: js api
386 * Input: sock_id: interger
387 *
388 * Output: return 0 when TCP.close call ok
389 * return error number TCP.close call fail
390 **************************************************************************************/
native_tcp_close(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)391 static JSValue native_tcp_close(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
392 {
393 int ret = -1;
394 int sock_id = 0;
395
396 if ((argc < 1) || (!JS_IsNumber(argv[0]))) {
397 amp_warn(MOD_STR, "parameter must be number");
398 goto out;
399 }
400
401 JS_ToInt32(ctx, &sock_id, argv[0]);
402 if (sock_id <= 0) {
403 amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
404 goto out;
405 }
406
407 g_tcp_close_flag = 1;
408 aos_sem_wait(&g_tcp_close_sem, MAX_TCP_RECV_TIMEOUT + 50);
409 g_tcp_close_flag = 0;
410 ret = 0;
411
412 out:
413 return JS_NewInt32(ctx, ret);
414 }
415
module_tcp_source_clean(void)416 static void module_tcp_source_clean(void)
417 {
418 if (g_tcp_recv_flag) {
419 g_tcp_close_flag = 1;
420 aos_sem_wait(&g_tcp_close_sem, MAX_TCP_RECV_TIMEOUT + 50);
421 g_tcp_close_flag = 0;
422 }
423 }
424
425
426 static JSClassDef js_tcp_class = {
427 "TCP",
428 };
429
430 static const JSCFunctionListEntry js_tcp_funcs[] = {
431 JS_CFUNC_DEF("createSocket", 2, native_tcp_create_socket),
432 JS_CFUNC_DEF("send", 3, native_tcp_send),
433 JS_CFUNC_DEF("recv", 2, native_tcp_receive),
434 JS_CFUNC_DEF("close", 1, native_tcp_close)
435 };
436
js_tcp_init(JSContext * ctx,JSModuleDef * m)437 static int js_tcp_init(JSContext *ctx, JSModuleDef *m)
438 {
439 JSValue proto;
440
441 JS_NewClassID(&js_tcp_class_id);
442
443 JS_NewClass(JS_GetRuntime(ctx), js_tcp_class_id, &js_tcp_class);
444 proto = JS_NewObject(ctx);
445 JS_SetPropertyFunctionList(ctx, proto, js_tcp_funcs, countof(js_tcp_funcs));
446 JS_SetClassProto(ctx, js_tcp_class_id, proto);
447 return JS_SetModuleExportList(ctx, m, js_tcp_funcs, countof(js_tcp_funcs));
448 }
449
js_init_module_tcp(JSContext * ctx,const char * module_name)450 JSModuleDef *js_init_module_tcp(JSContext *ctx, const char *module_name)
451 {
452 JSModuleDef *m;
453 m = JS_NewCModule(ctx, module_name, js_tcp_init);
454 if (!m)
455 return NULL;
456 JS_AddModuleExportList(ctx, m, js_tcp_funcs, countof(js_tcp_funcs));
457 return m;
458 }
459
module_tcp_register(void)460 void module_tcp_register(void)
461 {
462 amp_debug(MOD_STR, "module_tcp_register");
463 JSContext *ctx = js_get_context();
464
465 if (!g_tcp_close_sem) {
466 if (aos_sem_new(&g_tcp_close_sem, 0) != 0) {
467 amp_error(MOD_STR, "create tcp sem fail");
468 return;
469 }
470 }
471
472 amp_module_free_register(module_tcp_source_clean);
473 js_init_module_tcp(ctx, "TCP");
474 }
475