luat_lib_websocket.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*
  2. @module websocket
  3. @summary websocket客户端
  4. @version 1.0
  5. @date 2022.11.28
  6. @demo websocket
  7. @tag LUAT_USE_WEBSOCKET
  8. @usage
  9. local wsc = nil
  10. if websocket then
  11. wsc = websocket.create(nil, "ws://echo.airtun.air32.cn/ws/echo")
  12. wsc:autoreconn(true, 3000) -- 自动重连机制
  13. wsc:on(function(wsc, event, data)
  14. log.info("wsc", event, data)
  15. if event == "conack" then
  16. wsc:send((json.encode({action="echo", device_id=device_id})))
  17. sys.publish("wsc_conack")
  18. end
  19. end)
  20. wsc:connect()
  21. --sys.waitUntil("websocket_conack", 15000)
  22. while true do
  23. sys.wait(45000)
  24. if wsc:ready() then
  25. wsc:send((json.encode({action="echo", msg=os.date()})))
  26. end
  27. end
  28. wsc:close()
  29. wsc = nil
  30. end
  31. */
  32. #include "luat_base.h"
  33. #include "luat_network_adapter.h"
  34. #include "luat_rtos.h"
  35. #include "luat_zbuff.h"
  36. #include "luat_mem.h"
  37. #include "luat_websocket.h"
  38. #define LUAT_LOG_TAG "websocket"
  39. #include "luat_log.h"
  40. #define LUAT_WEBSOCKET_CTRL_TYPE "WS*"
  41. static const char *error_string[WEBSOCKET_MSG_ERROR_MAX - WEBSOCKET_MSG_ERROR_CONN + 1] ={
  42. "connect",
  43. "tx",
  44. "rx",
  45. "other"
  46. };
  47. #ifdef LUAT_USE_NETDRV
  48. #include "luat_netdrv.h"
  49. #include "luat_netdrv_event.h"
  50. #endif
  51. static luat_websocket_ctrl_t *get_websocket_ctrl(lua_State *L)
  52. {
  53. if (luaL_testudata(L, 1, LUAT_WEBSOCKET_CTRL_TYPE))
  54. {
  55. return ((luat_websocket_ctrl_t *)luaL_checkudata(L, 1, LUAT_WEBSOCKET_CTRL_TYPE));
  56. }
  57. else
  58. {
  59. return ((luat_websocket_ctrl_t *)lua_touserdata(L, 1));
  60. }
  61. }
  62. int l_websocket_callback(lua_State *L, void *ptr)
  63. {
  64. (void)ptr;
  65. rtos_msg_t *msg = (rtos_msg_t *)lua_topointer(L, -1);
  66. luat_websocket_ctrl_t *websocket_ctrl = (luat_websocket_ctrl_t *)msg->ptr;
  67. luat_websocket_pkg_t pkg = {0};
  68. // size_t payload_size = 0;
  69. switch (msg->arg1)
  70. {
  71. case WEBSOCKET_MSG_TIMER_PING:
  72. {
  73. luat_websocket_ping(websocket_ctrl);
  74. break;
  75. }
  76. case WEBSOCKET_MSG_RECONNECT:
  77. {
  78. luat_websocket_reconnect(websocket_ctrl);
  79. break;
  80. }
  81. case WEBSOCKET_MSG_PUBLISH:
  82. {
  83. if (websocket_ctrl->websocket_cb_id)
  84. {
  85. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  86. if (lua_isfunction(L, -1))
  87. {
  88. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  89. lua_pushstring(L, "recv");
  90. luat_websocket_payload((char *)msg->arg2, &pkg, 64 * 1024);
  91. lua_pushlstring(L, pkg.payload, pkg.plen);
  92. lua_pushinteger(L, pkg.FIN);
  93. lua_pushinteger(L, pkg.OPT_CODE);
  94. lua_call(L, 5, 0);
  95. }
  96. }
  97. luat_heap_free((char *)msg->arg2);
  98. break;
  99. }
  100. case WEBSOCKET_MSG_CONNACK:
  101. {
  102. if (websocket_ctrl->websocket_cb_id)
  103. {
  104. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  105. if (lua_isfunction(L, -1))
  106. {
  107. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  108. lua_pushstring(L, "conack");
  109. lua_call(L, 2, 0);
  110. }
  111. lua_getglobal(L, "sys_pub");
  112. if (lua_isfunction(L, -1))
  113. {
  114. lua_pushstring(L, "WEBSOCKET_CONNACK");
  115. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  116. lua_call(L, 2, 0);
  117. }
  118. }
  119. break;
  120. }
  121. case WEBSOCKET_MSG_RELEASE:
  122. {
  123. if (websocket_ctrl->websocket_ref)
  124. {
  125. luaL_unref(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  126. websocket_ctrl->websocket_ref = 0;
  127. }
  128. break;
  129. }
  130. case WEBSOCKET_MSG_SENT :
  131. {
  132. if (websocket_ctrl->websocket_cb_id)
  133. {
  134. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  135. if (lua_isfunction(L, -1))
  136. {
  137. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  138. lua_pushstring(L, "sent");
  139. lua_call(L, 2, 0);
  140. }
  141. }
  142. break;
  143. }
  144. case WEBSOCKET_MSG_DISCONNECT :
  145. {
  146. if (websocket_ctrl->websocket_cb_id)
  147. {
  148. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  149. if (lua_isfunction(L, -1))
  150. {
  151. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  152. lua_pushstring(L, "disconnect");
  153. lua_call(L, 2, 0);
  154. }
  155. }
  156. break;
  157. }
  158. case WEBSOCKET_MSG_ERROR_CONN :
  159. case WEBSOCKET_MSG_ERROR_TX :
  160. case WEBSOCKET_MSG_ERROR_RX :
  161. {
  162. if (websocket_ctrl->websocket_cb_id)
  163. {
  164. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  165. if (lua_isfunction(L, -1))
  166. {
  167. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  168. lua_pushstring(L, "error");
  169. lua_pushstring(L, error_string[msg->arg1 - WEBSOCKET_MSG_ERROR_CONN]);
  170. lua_pushinteger(L, msg->arg2);
  171. lua_call(L, 4, 0);
  172. }
  173. }
  174. #ifdef LUAT_USE_NETDRV
  175. luat_netdrv_fire_socket_event_netctrl(EV_NW_SOCKET_ERROR, websocket_ctrl->netc, 5);
  176. #endif
  177. break;
  178. }
  179. default:
  180. {
  181. LLOGD("l_websocket_callback error arg1:%d", msg->arg1);
  182. break;
  183. }
  184. }
  185. // lua_pushinteger(L, 0);
  186. return 0;
  187. }
  188. int l_luat_websocket_msg_cb(luat_websocket_ctrl_t *websocket_ctrl, int arg1, int arg2)
  189. {
  190. rtos_msg_t msg = {
  191. .handler = l_websocket_callback,
  192. .ptr = websocket_ctrl,
  193. .arg1 = arg1,
  194. .arg2 = arg2,
  195. };
  196. luat_msgbus_put(&msg, 0);
  197. return 0;
  198. }
  199. /*
  200. 配置是否打开debug信息
  201. @api wsc:debug(onoff)
  202. @boolean 是否打开debug开关
  203. @return nil 无返回值
  204. @usage wsc:debug(true)
  205. */
  206. static int l_websocket_set_debug(lua_State *L)
  207. {
  208. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  209. if (lua_isboolean(L, 2))
  210. {
  211. websocket_ctrl->netc->is_debug = lua_toboolean(L, 2);
  212. }
  213. return 0;
  214. }
  215. /*
  216. websocket客户端创建
  217. @api websocket.create(adapter, url, keepalive, use_ipv6)
  218. @int 适配器序号, 参考socket库的常量,默认为nil,会选择平台自带的方式
  219. @string 连接字符串,参考usage
  220. @int 心跳间隔,默认60秒. 2024.4.28新增
  221. @boolean 是否使用ipv6,默认false. 2024.6.17新增
  222. @return userdata 若成功会返回websocket客户端实例,否则返回nil
  223. @usage
  224. -- 普通TCP链接
  225. wsc = websocket.create(nil,"ws://air32.cn/abc")
  226. -- 加密TCP链接
  227. wsc = websocket.create(nil,"wss://air32.cn/abc")
  228. */
  229. static int l_websocket_create(lua_State *L)
  230. {
  231. int ret = 0;
  232. int adapter_index = luaL_optinteger(L, 1, network_register_get_default());
  233. if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY)
  234. {
  235. return 0;
  236. }
  237. // 连接参数相关
  238. luat_websocket_connopts_t opts = {0};
  239. size_t ip_len = 0;
  240. opts.url = luaL_checklstring(L, 2, &ip_len);
  241. if (lua_isinteger(L, 3)) {
  242. opts.keepalive = luaL_checkinteger(L, 3);
  243. }
  244. if (lua_type(L, 4) == LUA_TBOOLEAN) {
  245. opts.use_ipv6 = lua_toboolean(L, 4);
  246. }
  247. luat_websocket_ctrl_t *websocket_ctrl = (luat_websocket_ctrl_t *)lua_newuserdata(L, sizeof(luat_websocket_ctrl_t));
  248. if (!websocket_ctrl)
  249. {
  250. LLOGE("out of memory when malloc websocket_ctrl");
  251. return 0;
  252. }
  253. ret = luat_websocket_init(websocket_ctrl, adapter_index);
  254. if (ret)
  255. {
  256. LLOGE("websocket init FAID ret %d", ret);
  257. return 0;
  258. }
  259. network_set_ip_invaild(&websocket_ctrl->ip_addr);
  260. ret = luat_websocket_set_connopts(websocket_ctrl, &opts);
  261. if (ret){
  262. luat_websocket_release_socket(websocket_ctrl);
  263. return 0;
  264. }
  265. // TODO 判断ret, 如果初始化失败, 应该终止
  266. luaL_setmetatable(L, LUAT_WEBSOCKET_CTRL_TYPE);
  267. lua_pushvalue(L, -1);
  268. websocket_ctrl->websocket_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  269. return 1;
  270. }
  271. /*
  272. 注册websocket回调
  273. @api wsc:on(cb)
  274. @function cb websocket回调,参数包括websocket_client, event, data, payload
  275. @return nil 无返回值
  276. @usage
  277. wsc:on(function(websocket_client, event, data, payload)
  278. -- 打印各种事件
  279. log.info("websocket", "event", event, data, payload)
  280. end)
  281. --[[
  282. event的值有:
  283. conack 连接服务器成功,已经收到websocket协议头部信息,通信已建立
  284. recv 收到服务器下发的信息, data, payload 不为nil
  285. sent send函数发送的消息,服务器在TCP协议层已确认收到
  286. disconnect 服务器连接已断开
  287. error 发生错误, data为错误类型, payload为错误码
  288. 其中 sent/disconnect 事件在 2023.04.01 新增
  289. ]]
  290. */
  291. static int l_websocket_on(lua_State *L)
  292. {
  293. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  294. if (websocket_ctrl->websocket_cb_id != 0)
  295. {
  296. luaL_unref(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  297. websocket_ctrl->websocket_cb_id = 0;
  298. }
  299. if (lua_isfunction(L, 2))
  300. {
  301. lua_pushvalue(L, 2);
  302. websocket_ctrl->websocket_cb_id = luaL_ref(L, LUA_REGISTRYINDEX);
  303. }
  304. return 0;
  305. }
  306. /*
  307. 连接服务器
  308. @api wsc:connect()
  309. @return boolean 发起成功返回true, 否则返回false
  310. @usage
  311. -- 开始建立连接
  312. wsc:connect()
  313. -- 本函数仅代表发起成功, 后续仍需根据ready函数判断websocket是否连接正常
  314. */
  315. static int l_websocket_connect(lua_State *L)
  316. {
  317. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  318. int ret = luat_websocket_connect(websocket_ctrl);
  319. if (ret)
  320. {
  321. LLOGE("socket connect ret=%d\n", ret);
  322. luat_websocket_close_socket(websocket_ctrl);
  323. lua_pushboolean(L, 0);
  324. return 1;
  325. }
  326. lua_pushboolean(L, 1);
  327. return 1;
  328. }
  329. /*
  330. 自动重连
  331. @api wsc:autoreconn(reconnect, reconnect_time)
  332. @bool 是否自动重连
  333. @int 自动重连周期 单位ms 默认3000ms
  334. @usage
  335. wsc:autoreconn(true)
  336. */
  337. static int l_websocket_autoreconn(lua_State *L)
  338. {
  339. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  340. luat_websocket_autoreconn(websocket_ctrl, lua_toboolean(L, 2),luaL_optinteger(L, 3, 3000));
  341. return 0;
  342. }
  343. /*
  344. 发布消息
  345. @api wsc:send(data, fin, opt)
  346. @string 待发送的数据,必填
  347. @int 是否为最后一帧,默认1,即马上设置为最后一帧, 也就是单帧发送
  348. @int 操作码, 默认为字符串帧0, 可选1
  349. @return bool 成功返回true,否则为false或者nil
  350. @usage
  351. -- 简单发送数据
  352. wsc:send("123")
  353. -- 分段发送数据, 最后要用1(即FIN帧结束)
  354. wsc:send("123", 0)
  355. wsc:send("456", 0)
  356. wsc:send("789", 1)
  357. */
  358. static int l_websocket_send(lua_State *L)
  359. {
  360. size_t payload_len = 0;
  361. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  362. const char *payload = NULL;
  363. luat_zbuff_t *buff = NULL;
  364. int ret = 0;
  365. if (lua_isstring(L, 2))
  366. {
  367. payload = luaL_checklstring(L, 2, &payload_len);
  368. }
  369. else if (luaL_testudata(L, 2, LUAT_ZBUFF_TYPE))
  370. {
  371. buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE));
  372. payload = (const char *)buff->addr;
  373. payload_len = buff->used;
  374. }
  375. else
  376. {
  377. LLOGD("only support string or zbuff");
  378. return 0;
  379. }
  380. luat_websocket_pkg_t pkg = {
  381. .FIN = 1,
  382. .OPT_CODE = 0x01,
  383. .plen = payload_len,
  384. .payload = payload};
  385. if (lua_isinteger(L, 3) && lua_tointeger(L, 3) == 1) {
  386. pkg.OPT_CODE = 0x02;
  387. }
  388. if (websocket_ctrl->websocket_state != 1) {
  389. LLOGI("not ready yet");
  390. lua_pushboolean(L, 0);
  391. return 1;
  392. }
  393. websocket_ctrl->frame_wait ++;
  394. ret = luat_websocket_send_frame(websocket_ctrl, &pkg);
  395. if (ret < 0) {
  396. websocket_ctrl->frame_wait --;// 发送失败
  397. }
  398. lua_pushboolean(L, ret == 0 ? 1 : 0);
  399. return 1;
  400. }
  401. /*
  402. websocket客户端关闭(关闭后资源释放无法再使用)
  403. @api wsc:close()
  404. @usage
  405. wsc:close()
  406. */
  407. static int l_websocket_close(lua_State *L)
  408. {
  409. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  410. // websocket_disconnect(&(websocket_ctrl->broker));
  411. luat_websocket_close_socket(websocket_ctrl);
  412. if (websocket_ctrl->websocket_cb_id != 0)
  413. {
  414. luaL_unref(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb_id);
  415. websocket_ctrl->websocket_cb_id = 0;
  416. }
  417. luat_websocket_release_socket(websocket_ctrl);
  418. return 0;
  419. }
  420. /*
  421. websocket客户端是否就绪
  422. @api wsc:ready()
  423. @return bool 客户端是否就绪
  424. @usage
  425. local stat = wsc:ready()
  426. */
  427. static int l_websocket_ready(lua_State *L)
  428. {
  429. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  430. lua_pushboolean(L, websocket_ctrl->websocket_state > 0 ? 1 : 0);
  431. return 1;
  432. }
  433. /*
  434. 设置额外的headers
  435. @api wsc:headers(headers)
  436. @table/string 可以是table,也可以是字符串
  437. @return bool 客户端是否就绪
  438. @usage
  439. -- table形式
  440. wsc:headers({
  441. Auth="Basic ABCDEFGG"
  442. })
  443. -- 字符串形式
  444. wsc:headers("Auth: Basic ABCDERG\r\n")
  445. */
  446. static int l_websocket_headers(lua_State *L)
  447. {
  448. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  449. if (!lua_istable(L, 2) && !lua_isstring(L, 2)) {
  450. return 0;
  451. }
  452. #define WS_HEADER_MAX (1024)
  453. char* buff = luat_heap_malloc(WS_HEADER_MAX);
  454. memset(buff, 0, WS_HEADER_MAX);
  455. if (lua_istable(L, 2)) {
  456. size_t name_sz = 0;
  457. size_t value_sz = 0;
  458. lua_pushnil(L);
  459. while (lua_next(L, 2) != 0) {
  460. const char *name = lua_tolstring(L, -2, &name_sz);
  461. const char *value = lua_tolstring(L, -1, &value_sz);
  462. if (name_sz == 0 || value_sz == 0 || name_sz + value_sz > 256) {
  463. LLOGW("bad header %s %s", name, value);
  464. luat_heap_free(buff);
  465. return 0;
  466. }
  467. memcpy(buff + strlen(buff), name, name_sz);
  468. memcpy(buff + strlen(buff), ":", 1);
  469. if (WS_HEADER_MAX - strlen(buff) < value_sz * 2) {
  470. LLOGW("bad header %s %s, too large", name, value);
  471. luat_heap_free(buff);
  472. return 0;
  473. }
  474. for (size_t i = 0; i < value_sz; i++)
  475. {
  476. switch (value[i])
  477. {
  478. case '*':
  479. case '-':
  480. case '.':
  481. case '_':
  482. case ' ':
  483. sprintf_(buff + strlen(buff), "%%%02X", value[i]);
  484. break;
  485. default:
  486. buff[strlen(buff)] = value[i];
  487. break;
  488. }
  489. }
  490. lua_pop(L, 1);
  491. memcpy(buff + strlen(buff), "\r\n", 2);
  492. }
  493. }
  494. else {
  495. size_t len = 0;
  496. const char* data = luaL_checklstring(L, 2, &len);
  497. if (len > 1023) {
  498. LLOGW("headers too large size %d", len);
  499. luat_heap_free(buff);
  500. return 0;
  501. }
  502. memcpy(buff, data, len);
  503. }
  504. luat_websocket_set_headers(websocket_ctrl, buff);
  505. lua_pushboolean(L, 1);
  506. return 1;
  507. }
  508. static int _websocket_struct_newindex(lua_State *L);
  509. void luat_websocket_struct_init(lua_State *L)
  510. {
  511. luaL_newmetatable(L, LUAT_WEBSOCKET_CTRL_TYPE);
  512. lua_pushcfunction(L, _websocket_struct_newindex);
  513. lua_setfield(L, -2, "__index");
  514. lua_pop(L, 1);
  515. }
  516. #include "rotable2.h"
  517. const rotable_Reg_t reg_websocket[] =
  518. {
  519. {"create", ROREG_FUNC(l_websocket_create)},
  520. {"on", ROREG_FUNC(l_websocket_on)},
  521. {"connect", ROREG_FUNC(l_websocket_connect)},
  522. {"autoreconn", ROREG_FUNC(l_websocket_autoreconn)},
  523. {"send", ROREG_FUNC(l_websocket_send)},
  524. {"close", ROREG_FUNC(l_websocket_close)},
  525. {"ready", ROREG_FUNC(l_websocket_ready)},
  526. {"headers", ROREG_FUNC(l_websocket_headers)},
  527. {"debug", ROREG_FUNC(l_websocket_set_debug)},
  528. {NULL, ROREG_INT(0)}
  529. };
  530. int _websocket_struct_newindex(lua_State *L)
  531. {
  532. const rotable_Reg_t *reg = reg_websocket;
  533. const char *key = luaL_checkstring(L, 2);
  534. while (1)
  535. {
  536. if (reg->name == NULL)
  537. return 0;
  538. if (!strcmp(reg->name, key))
  539. {
  540. lua_pushcfunction(L, reg->value.value.func);
  541. return 1;
  542. }
  543. reg++;
  544. }
  545. // return 0;
  546. }
  547. #ifndef LUAT_USE_NETWORK
  548. static const rotable_Reg_t reg_websocket_emtry[] = {
  549. {NULL, ROREG_INT(0)}
  550. };
  551. #endif
  552. LUAMOD_API int luaopen_websocket(lua_State *L)
  553. {
  554. #ifdef LUAT_USE_NETWORK
  555. luat_newlib2(L, reg_websocket);
  556. luat_websocket_struct_init(L);
  557. return 1;
  558. #else
  559. LLOGE("websocket require network enable!!");
  560. luat_newlib2(L, reg_websocket_emtry);
  561. return 1;
  562. #endif
  563. }