luat_lib_websocket.c 13 KB

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