luat_lib_websocket.c 13 KB

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