protocol_lws_raw_test.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * ws protocol handler plugin for testing raw file and raw socket
  3. *
  4. * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
  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 LWS_SERVER_OPTION_FALLBACK_TO_RAW
  46. *
  47. * 2) Enable on a vhost like this
  48. *
  49. * "protocol-lws-raw-test": {
  50. * "status": "ok",
  51. * "raw": "1"
  52. * },
  53. *
  54. * The "raw" pvo marks this protocol as being used for RAW connections.
  55. *
  56. * 3) Run libwebsockets-test-server-v2.0 and connect to it by telnet, eg
  57. *
  58. * telnet 127.0.0.1 7681
  59. *
  60. * type something that isn't a valid HTTP method and enter, before the
  61. * connection times out. The connection will switch to RAW mode using this
  62. * protocol, and pass the unused rx as a raw RX callback.
  63. *
  64. * The test protocol echos back what was typed on telnet to telnet.
  65. */
  66. #if !defined (LWS_PLUGIN_STATIC)
  67. #define LWS_DLL
  68. #define LWS_INTERNAL
  69. #include "../lib/libwebsockets.h"
  70. #endif
  71. #include <string.h>
  72. struct per_vhost_data__raw_test {
  73. struct lws_context *context;
  74. struct lws_vhost *vhost;
  75. const struct lws_protocols *protocol;
  76. char fifo_path[100];
  77. int fifo;
  78. char zero_length_read;
  79. };
  80. struct per_session_data__raw_test {
  81. int number;
  82. unsigned char buf[128];
  83. int len;
  84. };
  85. static int
  86. callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
  87. void *user, void *in, size_t len)
  88. {
  89. struct per_session_data__raw_test *pss =
  90. (struct per_session_data__raw_test *)user;
  91. struct per_vhost_data__raw_test *vhd =
  92. (struct per_vhost_data__raw_test *)
  93. lws_protocol_vh_priv_get(lws_get_vhost(wsi),
  94. lws_get_protocol(wsi));
  95. lws_sock_file_fd_type u;
  96. (void)pss;
  97. switch (reason) {
  98. case LWS_CALLBACK_PROTOCOL_INIT:
  99. vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
  100. lws_get_protocol(wsi),
  101. sizeof(struct per_vhost_data__raw_test));
  102. vhd->context = lws_get_context(wsi);
  103. vhd->protocol = lws_get_protocol(wsi);
  104. vhd->vhost = lws_get_vhost(wsi);
  105. {
  106. const struct lws_protocol_vhost_options *pvo =
  107. (const struct lws_protocol_vhost_options *)in;
  108. while (pvo) {
  109. if (!strcmp(pvo->name, "fifo-path"))
  110. strncpy(vhd->fifo_path, pvo->value, sizeof(vhd->fifo_path) - 1);
  111. pvo = pvo->next;
  112. }
  113. if (vhd->fifo_path[0] == '\0') {
  114. lwsl_err("%s: Missing pvo \"fifo-path\", raw file fd testing disabled\n", __func__);
  115. break;
  116. }
  117. }
  118. unlink(vhd->fifo_path);
  119. if (mkfifo(vhd->fifo_path, 0666)) {
  120. lwsl_err("mkfifo failed\n");
  121. return 1;
  122. }
  123. vhd->fifo = open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
  124. if (vhd->fifo == -1) {
  125. lwsl_err("opening fifo failed\n");
  126. unlink(vhd->fifo_path);
  127. return 1;
  128. }
  129. lwsl_notice("FIFO %s created\n", vhd->fifo_path);
  130. u.filefd = vhd->fifo;
  131. if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u,
  132. "protocol-lws-raw-test",
  133. NULL)) {
  134. lwsl_err("Failed to adopt fifo descriptor\n");
  135. close(vhd->fifo);
  136. unlink(vhd->fifo_path);
  137. return 1;
  138. }
  139. break;
  140. case LWS_CALLBACK_PROTOCOL_DESTROY:
  141. if (!vhd)
  142. break;
  143. if (vhd->fifo >- 0) {
  144. close(vhd->fifo);
  145. unlink(vhd->fifo_path);
  146. }
  147. break;
  148. /*
  149. * Callbacks related to Raw file descriptor testing
  150. */
  151. case LWS_CALLBACK_RAW_ADOPT_FILE:
  152. lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n");
  153. break;
  154. case LWS_CALLBACK_RAW_RX_FILE:
  155. lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n");
  156. {
  157. char buf[256];
  158. int n;
  159. n = read(vhd->fifo, buf, sizeof(buf) - 1);
  160. if (n < 0) {
  161. lwsl_err("FIFO read failed\n");
  162. return 1;
  163. }
  164. /*
  165. * When nobody opened the other side of the FIFO, the FIFO fd acts well and
  166. * only signals POLLIN when somebody opened and wrote to it.
  167. *
  168. * But if the other side of the FIFO closed it, we will see an endless
  169. * POLLIN and 0 available to read.
  170. *
  171. * The only way to handle it is to reopen the FIFO our side and wait for a
  172. * new peer. This is a quirk of FIFOs not of LWS.
  173. */
  174. if (n == 0) { /* peer closed - do reopen in close processing */
  175. vhd->zero_length_read = 1;
  176. return 1;
  177. }
  178. buf[n] = '\0';
  179. lwsl_info("read %d\n", n);
  180. puts(buf);
  181. }
  182. break;
  183. case LWS_CALLBACK_RAW_CLOSE_FILE:
  184. lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n");
  185. if (vhd->zero_length_read) {
  186. vhd->zero_length_read = 0;
  187. close(vhd->fifo);
  188. /* the wsi that adopted the fifo file is closing... reopen the fifo and readopt */
  189. vhd->fifo = open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
  190. if (vhd->fifo == -1) {
  191. lwsl_err("opening fifo failed\n");
  192. return 1;
  193. }
  194. lwsl_notice("FIFO %s reopened\n", vhd->fifo_path);
  195. u.filefd = vhd->fifo;
  196. if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u, "protocol-lws-raw-test", NULL)) {
  197. lwsl_err("Failed to adopt fifo descriptor\n");
  198. close(vhd->fifo);
  199. return 1;
  200. }
  201. }
  202. break;
  203. case LWS_CALLBACK_RAW_WRITEABLE_FILE:
  204. lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
  205. break;
  206. /*
  207. * Callbacks related to Raw socket descriptor testing
  208. */
  209. case LWS_CALLBACK_RAW_ADOPT:
  210. lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
  211. break;
  212. case LWS_CALLBACK_RAW_RX:
  213. lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
  214. if (len > sizeof(pss->buf))
  215. len = sizeof(pss->buf);
  216. memcpy(pss->buf, in, len);
  217. pss->len = len;
  218. lws_callback_on_writable(wsi);
  219. break;
  220. case LWS_CALLBACK_RAW_CLOSE:
  221. lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
  222. break;
  223. case LWS_CALLBACK_RAW_WRITEABLE:
  224. lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
  225. lws_write(wsi, pss->buf, pss->len, LWS_WRITE_HTTP);
  226. break;
  227. default:
  228. break;
  229. }
  230. return 0;
  231. }
  232. #define LWS_PLUGIN_PROTOCOL_RAW_TEST \
  233. { \
  234. "protocol-lws-raw-test", \
  235. callback_raw_test, \
  236. sizeof(struct per_session_data__raw_test), \
  237. 1024, /* rx buf size must be >= permessage-deflate rx size */ \
  238. }
  239. #if !defined (LWS_PLUGIN_STATIC)
  240. static const struct lws_protocols protocols[] = {
  241. LWS_PLUGIN_PROTOCOL_RAW_TEST
  242. };
  243. LWS_EXTERN LWS_VISIBLE int
  244. init_protocol_lws_raw_test(struct lws_context *context,
  245. struct lws_plugin_capability *c)
  246. {
  247. if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
  248. lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
  249. c->api_magic);
  250. return 1;
  251. }
  252. c->protocols = protocols;
  253. c->count_protocols = ARRAY_SIZE(protocols);
  254. c->extensions = NULL;
  255. c->count_extensions = 0;
  256. return 0;
  257. }
  258. LWS_EXTERN LWS_VISIBLE int
  259. destroy_protocol_lws_raw_test(struct lws_context *context)
  260. {
  261. return 0;
  262. }
  263. #endif