websocket.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /* Feel free to use this example code in any way
  2. you see fit (Public Domain) */
  3. #include <sys/types.h>
  4. #ifndef _WIN32
  5. #include <sys/select.h>
  6. #include <sys/socket.h>
  7. #include <fcntl.h>
  8. #else
  9. #include <winsock2.h>
  10. #endif
  11. #include <microhttpd.h>
  12. #include <microhttpd_ws.h>
  13. #include <time.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17. #include <errno.h>
  18. #define PORT 80
  19. #define PAGE \
  20. "<!DOCTYPE html>\n" \
  21. "<html>\n" \
  22. "<head>\n" \
  23. "<meta charset=\"UTF-8\">\n" \
  24. "<title>Websocket Demo</title>\n" \
  25. "<script>\n" \
  26. "\n" \
  27. "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
  28. " + ':/" "/' +\n" \
  29. " window.location.host + '/chat';\n" \
  30. "let socket = null;\n" \
  31. "\n" \
  32. "window.onload = function(event) {\n" \
  33. " socket = new WebSocket(url);\n" \
  34. " socket.onopen = function(event) {\n" \
  35. " document.write('The websocket connection has been " \
  36. "established.<br>');\n" \
  37. "\n" \
  38. " /" "/ Send some text\n" \
  39. " socket.send('Hello from JavaScript!');\n" \
  40. " }\n" \
  41. "\n" \
  42. " socket.onclose = function(event) {\n" \
  43. " document.write('The websocket connection has been closed.<br>');\n" \
  44. " }\n" \
  45. "\n" \
  46. " socket.onerror = function(event) {\n" \
  47. " document.write('An error occurred during the websocket " \
  48. "communication.<br>');\n" \
  49. " }\n" \
  50. "\n" \
  51. " socket.onmessage = function(event) {\n" \
  52. " document.write('Websocket message received: ' + " \
  53. "event.data + '<br>');\n" \
  54. " }\n" \
  55. "}\n" \
  56. "\n" \
  57. "</script>\n" \
  58. "</head>\n" \
  59. "<body>\n" \
  60. "</body>\n" \
  61. "</html>"
  62. #define PAGE_NOT_FOUND \
  63. "404 Not Found"
  64. #define PAGE_INVALID_WEBSOCKET_REQUEST \
  65. "Invalid WebSocket request!"
  66. static void
  67. send_all (MHD_socket fd,
  68. const char *buf,
  69. size_t len);
  70. static void
  71. make_blocking (MHD_socket fd);
  72. static void
  73. upgrade_handler (void *cls,
  74. struct MHD_Connection *connection,
  75. void *req_cls,
  76. const char *extra_in,
  77. size_t extra_in_size,
  78. MHD_socket fd,
  79. struct MHD_UpgradeResponseHandle *urh)
  80. {
  81. /* make the socket blocking (operating-system-dependent code) */
  82. make_blocking (fd);
  83. /* create a websocket stream for this connection */
  84. struct MHD_WebSocketStream *ws;
  85. int result = MHD_websocket_stream_init (&ws,
  86. 0,
  87. 0);
  88. if (0 != result)
  89. {
  90. /* Couldn't create the websocket stream.
  91. * So we close the socket and leave
  92. */
  93. MHD_upgrade_action (urh,
  94. MHD_UPGRADE_ACTION_CLOSE);
  95. return;
  96. }
  97. /* Let's wait for incoming data */
  98. const size_t buf_len = 256;
  99. char buf[buf_len];
  100. ssize_t got;
  101. while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
  102. {
  103. got = recv (fd,
  104. buf,
  105. buf_len,
  106. 0);
  107. if (0 >= got)
  108. {
  109. /* the TCP/IP socket has been closed */
  110. break;
  111. }
  112. /* parse the entire received data */
  113. size_t buf_offset = 0;
  114. while (buf_offset < (size_t) got)
  115. {
  116. size_t new_offset = 0;
  117. char *frame_data = NULL;
  118. size_t frame_len = 0;
  119. int status = MHD_websocket_decode (ws,
  120. buf + buf_offset,
  121. ((size_t) got) - buf_offset,
  122. &new_offset,
  123. &frame_data,
  124. &frame_len);
  125. if (0 > status)
  126. {
  127. /* an error occurred and the connection must be closed */
  128. if (NULL != frame_data)
  129. {
  130. MHD_websocket_free (ws, frame_data);
  131. }
  132. break;
  133. }
  134. else
  135. {
  136. buf_offset += new_offset;
  137. if (0 < status)
  138. {
  139. /* the frame is complete */
  140. switch (status)
  141. {
  142. case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
  143. /* The client has sent some text.
  144. * We will display it and answer with a text frame.
  145. */
  146. if (NULL != frame_data)
  147. {
  148. printf ("Received message: %s\n", frame_data);
  149. MHD_websocket_free (ws, frame_data);
  150. frame_data = NULL;
  151. }
  152. result = MHD_websocket_encode_text (ws,
  153. "Hello",
  154. 5, /* length of "Hello" */
  155. 0,
  156. &frame_data,
  157. &frame_len,
  158. NULL);
  159. if (0 == result)
  160. {
  161. send_all (fd,
  162. frame_data,
  163. frame_len);
  164. }
  165. break;
  166. case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
  167. /* if we receive a close frame, we will respond with one */
  168. MHD_websocket_free (ws,
  169. frame_data);
  170. frame_data = NULL;
  171. result = MHD_websocket_encode_close (ws,
  172. 0,
  173. NULL,
  174. 0,
  175. &frame_data,
  176. &frame_len);
  177. if (0 == result)
  178. {
  179. send_all (fd,
  180. frame_data,
  181. frame_len);
  182. }
  183. break;
  184. case MHD_WEBSOCKET_STATUS_PING_FRAME:
  185. /* if we receive a ping frame, we will respond */
  186. /* with the corresponding pong frame */
  187. {
  188. char *pong = NULL;
  189. size_t pong_len = 0;
  190. result = MHD_websocket_encode_pong (ws,
  191. frame_data,
  192. frame_len,
  193. &pong,
  194. &pong_len);
  195. if (0 == result)
  196. {
  197. send_all (fd,
  198. pong,
  199. pong_len);
  200. }
  201. MHD_websocket_free (ws,
  202. pong);
  203. }
  204. break;
  205. default:
  206. /* Other frame types are ignored
  207. * in this minimal example.
  208. * This is valid, because they become
  209. * automatically skipped if we receive them unexpectedly
  210. */
  211. break;
  212. }
  213. }
  214. if (NULL != frame_data)
  215. {
  216. MHD_websocket_free (ws, frame_data);
  217. }
  218. }
  219. }
  220. }
  221. /* free the websocket stream */
  222. MHD_websocket_stream_free (ws);
  223. /* close the socket when it is not needed anymore */
  224. MHD_upgrade_action (urh,
  225. MHD_UPGRADE_ACTION_CLOSE);
  226. }
  227. /* This helper function is used for the case that
  228. * we need to resend some data
  229. */
  230. static void
  231. send_all (MHD_socket fd,
  232. const char *buf,
  233. size_t len)
  234. {
  235. ssize_t ret;
  236. size_t off;
  237. for (off = 0; off < len; off += ret)
  238. {
  239. ret = send (fd,
  240. &buf[off],
  241. (int) (len - off),
  242. 0);
  243. if (0 > ret)
  244. {
  245. if (EAGAIN == errno)
  246. {
  247. ret = 0;
  248. continue;
  249. }
  250. break;
  251. }
  252. if (0 == ret)
  253. break;
  254. }
  255. }
  256. /* This helper function contains operating-system-dependent code and
  257. * is used to make a socket blocking.
  258. */
  259. static void
  260. make_blocking (MHD_socket fd)
  261. {
  262. #ifndef _WIN32
  263. int flags;
  264. flags = fcntl (fd, F_GETFL);
  265. if (-1 == flags)
  266. abort ();
  267. if ((flags & ~O_NONBLOCK) != flags)
  268. if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
  269. abort ();
  270. #else /* _WIN32 */
  271. unsigned long flags = 0;
  272. if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
  273. abort ();
  274. #endif /* _WIN32 */
  275. }
  276. static enum MHD_Result
  277. access_handler (void *cls,
  278. struct MHD_Connection *connection,
  279. const char *url,
  280. const char *method,
  281. const char *version,
  282. const char *upload_data,
  283. size_t *upload_data_size,
  284. void **req_cls)
  285. {
  286. static int aptr;
  287. struct MHD_Response *response;
  288. int ret;
  289. (void) cls; /* Unused. Silent compiler warning. */
  290. (void) upload_data; /* Unused. Silent compiler warning. */
  291. (void) upload_data_size; /* Unused. Silent compiler warning. */
  292. if (0 != strcmp (method, "GET"))
  293. return MHD_NO; /* unexpected method */
  294. if (&aptr != *req_cls)
  295. {
  296. /* do never respond on first call */
  297. *req_cls = &aptr;
  298. return MHD_YES;
  299. }
  300. *req_cls = NULL; /* reset when done */
  301. if (0 == strcmp (url, "/"))
  302. {
  303. /* Default page for visiting the server */
  304. struct MHD_Response *response;
  305. response = MHD_create_response_from_buffer_static (strlen (PAGE),
  306. PAGE);
  307. ret = MHD_queue_response (connection,
  308. MHD_HTTP_OK,
  309. response);
  310. MHD_destroy_response (response);
  311. }
  312. else if (0 == strcmp (url, "/chat"))
  313. {
  314. char is_valid = 1;
  315. const char *value = NULL;
  316. char sec_websocket_accept[29];
  317. if (0 != MHD_websocket_check_http_version (version))
  318. {
  319. is_valid = 0;
  320. }
  321. value = MHD_lookup_connection_value (connection,
  322. MHD_HEADER_KIND,
  323. MHD_HTTP_HEADER_CONNECTION);
  324. if (0 != MHD_websocket_check_connection_header (value))
  325. {
  326. is_valid = 0;
  327. }
  328. value = MHD_lookup_connection_value (connection,
  329. MHD_HEADER_KIND,
  330. MHD_HTTP_HEADER_UPGRADE);
  331. if (0 != MHD_websocket_check_upgrade_header (value))
  332. {
  333. is_valid = 0;
  334. }
  335. value = MHD_lookup_connection_value (connection,
  336. MHD_HEADER_KIND,
  337. MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
  338. if (0 != MHD_websocket_check_version_header (value))
  339. {
  340. is_valid = 0;
  341. }
  342. value = MHD_lookup_connection_value (connection,
  343. MHD_HEADER_KIND,
  344. MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
  345. if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
  346. {
  347. is_valid = 0;
  348. }
  349. if (1 == is_valid)
  350. {
  351. /* upgrade the connection */
  352. response = MHD_create_response_for_upgrade (&upgrade_handler,
  353. NULL);
  354. MHD_add_response_header (response,
  355. MHD_HTTP_HEADER_UPGRADE,
  356. "websocket");
  357. MHD_add_response_header (response,
  358. MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
  359. sec_websocket_accept);
  360. ret = MHD_queue_response (connection,
  361. MHD_HTTP_SWITCHING_PROTOCOLS,
  362. response);
  363. MHD_destroy_response (response);
  364. }
  365. else
  366. {
  367. /* return error page */
  368. struct MHD_Response *response;
  369. response =
  370. MHD_create_response_from_buffer_static (strlen (
  371. PAGE_INVALID_WEBSOCKET_REQUEST),
  372. PAGE_INVALID_WEBSOCKET_REQUEST);
  373. ret = MHD_queue_response (connection,
  374. MHD_HTTP_BAD_REQUEST,
  375. response);
  376. MHD_destroy_response (response);
  377. }
  378. }
  379. else
  380. {
  381. struct MHD_Response *response;
  382. response =
  383. MHD_create_response_from_buffer_static (strlen (PAGE_NOT_FOUND),
  384. PAGE_NOT_FOUND);
  385. ret = MHD_queue_response (connection,
  386. MHD_HTTP_NOT_FOUND,
  387. response);
  388. MHD_destroy_response (response);
  389. }
  390. return ret;
  391. }
  392. int
  393. main (int argc,
  394. char *const *argv)
  395. {
  396. (void) argc; /* Unused. Silent compiler warning. */
  397. (void) argv; /* Unused. Silent compiler warning. */
  398. struct MHD_Daemon *daemon;
  399. daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
  400. | MHD_USE_THREAD_PER_CONNECTION
  401. | MHD_ALLOW_UPGRADE
  402. | MHD_USE_ERROR_LOG,
  403. PORT, NULL, NULL,
  404. &access_handler, NULL,
  405. MHD_OPTION_END);
  406. if (NULL == daemon)
  407. return 1;
  408. (void) getc (stdin);
  409. MHD_stop_daemon (daemon);
  410. return 0;
  411. }