protocol_lws_raw_test.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * ws protocol handler plugin for testing raw file and raw socket
  3. *
  4. * Written in 2010-2019 by Andy Green <[email protected]>
  5. *
  6. * This file is made available under the Creative Commons CC0 1.0
  7. * Universal Public Domain Dedication.
  8. *
  9. * The person who associated a work with this deed has dedicated
  10. * the work to the public domain by waiving all of his or her rights
  11. * to the work worldwide under copyright law, including all related
  12. * and neighboring rights, to the extent allowed by law. You can copy,
  13. * modify, distribute and perform the work, even for commercial purposes,
  14. * all without asking permission.
  15. *
  16. * These test plugins are intended to be adapted for use in your code, which
  17. * may be proprietary. So unlike the library itself, they are licensed
  18. * Public Domain.
  19. *
  20. * This plugin test both raw file descriptors and raw socket descriptors. It
  21. * can test both or just one depending on how you configure it. libwebsockets-
  22. * test-server-v2.0 is set up to test both.
  23. *
  24. * RAW File Descriptor Testing
  25. * ===========================
  26. *
  27. * Enable on a vhost like this
  28. *
  29. * "protocol-lws-raw-test": {
  30. * "status": "ok",
  31. * "fifo-path": "/tmp/lws-test-raw"
  32. * },
  33. *
  34. * Then you can feed it data through the FIFO like this
  35. *
  36. * $ sudo sh -c "echo hello > /tmp/lws-test-raw"
  37. *
  38. * This plugin simply prints the data. But it does it through the lws event
  39. * loop / service poll.
  40. *
  41. *
  42. * RAW Socket Descriptor Testing
  43. * =============================
  44. *
  45. * 1) You must give the vhost the option flag
  46. * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG
  47. *
  48. * 2) Enable on a vhost like this
  49. *
  50. * "protocol-lws-raw-test": {
  51. * "status": "ok",
  52. * "raw": "1"
  53. * },
  54. *
  55. * The "raw" pvo marks this protocol as being used for RAW connections.
  56. *
  57. * 3) Run libwebsockets-test-server-v2.0 and connect to it by telnet, eg
  58. *
  59. * telnet 127.0.0.1 7681
  60. *
  61. * type something that isn't a valid HTTP method and enter, before the
  62. * connection times out. The connection will switch to RAW mode using this
  63. * protocol, and pass the unused rx as a raw RX callback.
  64. *
  65. * The test protocol echos back what was typed on telnet to telnet.
  66. */
  67. #if !defined (LWS_PLUGIN_STATIC)
  68. #define LWS_DLL
  69. #define LWS_INTERNAL
  70. #include <libwebsockets.h>
  71. #endif
  72. #include <string.h>
  73. #include <fcntl.h>
  74. struct per_vhost_data__raw_test {
  75. struct lws_context *context;
  76. struct lws_vhost *vhost;
  77. const struct lws_protocols *protocol;
  78. char fifo_path[100];
  79. int fifo;
  80. char zero_length_read;
  81. };
  82. struct per_session_data__raw_test {
  83. int number;
  84. unsigned char buf[128];
  85. int len;
  86. };
  87. static int
  88. callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, void *user,
  89. void *in, size_t len)
  90. {
  91. struct per_session_data__raw_test *pss =
  92. (struct per_session_data__raw_test *)user;
  93. struct per_vhost_data__raw_test *vhd =
  94. (struct per_vhost_data__raw_test *)
  95. lws_protocol_vh_priv_get(lws_get_vhost(wsi),
  96. lws_get_protocol(wsi));
  97. lws_sock_file_fd_type u;
  98. (void)pss;
  99. switch (reason) {
  100. case LWS_CALLBACK_PROTOCOL_INIT:
  101. vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
  102. lws_get_protocol(wsi),
  103. sizeof(struct per_vhost_data__raw_test));
  104. vhd->context = lws_get_context(wsi);
  105. vhd->protocol = lws_get_protocol(wsi);
  106. vhd->vhost = lws_get_vhost(wsi);
  107. {
  108. const struct lws_protocol_vhost_options *pvo =
  109. (const struct lws_protocol_vhost_options *)in;
  110. while (pvo) {
  111. if (!strcmp(pvo->name, "fifo-path"))
  112. lws_strncpy(vhd->fifo_path, pvo->value,
  113. sizeof(vhd->fifo_path));
  114. pvo = pvo->next;
  115. }
  116. if (vhd->fifo_path[0] == '\0') {
  117. lwsl_err("%s: Missing pvo \"fifo-path\", "
  118. "raw file fd testing disabled\n",
  119. __func__);
  120. break;
  121. }
  122. }
  123. unlink(vhd->fifo_path);
  124. if (mkfifo(vhd->fifo_path, 0666)) {
  125. lwsl_err("mkfifo failed\n");
  126. return 1;
  127. }
  128. vhd->fifo = lws_open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
  129. if (vhd->fifo == -1) {
  130. lwsl_err("opening fifo failed\n");
  131. unlink(vhd->fifo_path);
  132. return 1;
  133. }
  134. lwsl_notice("FIFO %s created\n", vhd->fifo_path);
  135. u.filefd = vhd->fifo;
  136. if (!lws_adopt_descriptor_vhost(vhd->vhost,
  137. LWS_ADOPT_RAW_FILE_DESC, u,
  138. "protocol-lws-raw-test",
  139. NULL)) {
  140. lwsl_err("Failed to adopt fifo descriptor\n");
  141. close(vhd->fifo);
  142. unlink(vhd->fifo_path);
  143. return 1;
  144. }
  145. break;
  146. case LWS_CALLBACK_PROTOCOL_DESTROY:
  147. if (!vhd)
  148. break;
  149. if (vhd->fifo >= 0) {
  150. close(vhd->fifo);
  151. unlink(vhd->fifo_path);
  152. }
  153. break;
  154. /*
  155. * Callbacks related to Raw file descriptor testing
  156. */
  157. case LWS_CALLBACK_RAW_ADOPT_FILE:
  158. lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n");
  159. break;
  160. case LWS_CALLBACK_RAW_RX_FILE:
  161. lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n");
  162. {
  163. char buf[256];
  164. int n;
  165. n = read(vhd->fifo, buf, sizeof(buf) - 1);
  166. if (n < 0) {
  167. lwsl_err("FIFO read failed\n");
  168. return 1;
  169. }
  170. /*
  171. * When nobody opened the other side of the FIFO, the
  172. * FIFO fd acts well and only signals POLLIN when
  173. * somebody opened and wrote to it.
  174. *
  175. * But if the other side of the FIFO closed it, we will
  176. * see an endless POLLIN and 0 available to read.
  177. *
  178. * The only way to handle it is to reopen the FIFO our
  179. * side and wait for a new peer. This is a quirk of
  180. * FIFOs not of LWS.
  181. */
  182. if (n == 0) { /* peer closed - reopen in close processing */
  183. vhd->zero_length_read = 1;
  184. return 1;
  185. }
  186. buf[n] = '\0';
  187. lwsl_info("read %d\n", n);
  188. puts(buf);
  189. }
  190. break;
  191. case LWS_CALLBACK_RAW_CLOSE_FILE:
  192. lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n");
  193. if (vhd->zero_length_read) {
  194. vhd->zero_length_read = 0;
  195. close(vhd->fifo);
  196. /* the wsi that adopted the fifo file is closing...
  197. * reopen the fifo and readopt
  198. */
  199. vhd->fifo = lws_open(vhd->fifo_path,
  200. O_NONBLOCK | O_RDONLY);
  201. if (vhd->fifo == -1) {
  202. lwsl_err("opening fifo failed\n");
  203. return 1;
  204. }
  205. lwsl_notice("FIFO %s reopened\n", vhd->fifo_path);
  206. u.filefd = vhd->fifo;
  207. if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u,
  208. "protocol-lws-raw-test", NULL)) {
  209. lwsl_err("Failed to adopt fifo descriptor\n");
  210. close(vhd->fifo);
  211. return 1;
  212. }
  213. }
  214. break;
  215. case LWS_CALLBACK_RAW_WRITEABLE_FILE:
  216. lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
  217. break;
  218. /*
  219. * Callbacks related to Raw socket descriptor testing
  220. */
  221. case LWS_CALLBACK_RAW_ADOPT:
  222. lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
  223. break;
  224. case LWS_CALLBACK_RAW_RX:
  225. lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
  226. if (len > sizeof(pss->buf))
  227. len = sizeof(pss->buf);
  228. memcpy(pss->buf, in, len);
  229. pss->len = len;
  230. lws_callback_on_writable(wsi);
  231. break;
  232. case LWS_CALLBACK_RAW_CLOSE:
  233. lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
  234. break;
  235. case LWS_CALLBACK_RAW_WRITEABLE:
  236. lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
  237. lws_write(wsi, pss->buf, pss->len, LWS_WRITE_HTTP);
  238. break;
  239. default:
  240. break;
  241. }
  242. return 0;
  243. }
  244. #define LWS_PLUGIN_PROTOCOL_RAW_TEST \
  245. { \
  246. "protocol-lws-raw-test", \
  247. callback_raw_test, \
  248. sizeof(struct per_session_data__raw_test), \
  249. 1024, /* rx buf size must be >= permessage-deflate rx size */ \
  250. }
  251. #if !defined (LWS_PLUGIN_STATIC)
  252. static const struct lws_protocols protocols[] = {
  253. LWS_PLUGIN_PROTOCOL_RAW_TEST
  254. };
  255. LWS_VISIBLE const lws_plugin_protocol_t lws_raw_test = {
  256. .hdr = {
  257. "lws raw test",
  258. "lws_protocol_plugin",
  259. LWS_PLUGIN_API_MAGIC
  260. },
  261. .protocols = protocols,
  262. .count_protocols = LWS_ARRAY_SIZE(protocols),
  263. .extensions = NULL,
  264. .count_extensions = 0,
  265. };
  266. #endif