websocket.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. Websockets are a genuine way to implement push notifications,
  2. where the server initiates the communication while the client can be idle.
  3. Usually a HTTP communication is half-duplex and always requested by the client,
  4. but websockets are full-duplex and only initialized by the client.
  5. In the further communication both sites can use the websocket at any time
  6. to send data to the other site.
  7. To initialize a websocket connection the client sends a special HTTP request
  8. to the server and initializes
  9. a handshake between client and server which switches from the HTTP protocol
  10. to the websocket protocol.
  11. Thus both the server as well as the client must support websockets.
  12. If proxys are used, they must support websockets too.
  13. In this chapter we take a look on server and client, but with a focus on
  14. the server with @emph{libmicrohttpd}.
  15. Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
  16. which is required for switching from the HTTP protocol.
  17. Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
  18. to support the websocket protocol.
  19. @heading Upgrading connections with libmicrohttpd
  20. To support websockets we need to enable upgrading of HTTP connections first.
  21. This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
  22. @code{MHD_start_daemon()}.
  23. @verbatim
  24. daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
  25. MHD_USE_THREAD_PER_CONNECTION |
  26. MHD_ALLOW_UPGRADE |
  27. MHD_USE_ERROR_LOG,
  28. PORT, NULL, NULL,
  29. &access_handler, NULL,
  30. MHD_OPTION_END);
  31. @end verbatim
  32. @noindent
  33. The next step is to turn a specific request into an upgraded connection.
  34. This done in our @code{access_handler} by calling
  35. @code{MHD_create_response_for_upgrade()}.
  36. An @code{upgrade_handler} will be passed to perform the low-level actions
  37. on the socket.
  38. @emph{Please note that the socket here is just a regular socket as provided
  39. by the operating system.
  40. To use it as a websocket, some more steps from the following
  41. chapters are required.}
  42. @verbatim
  43. static enum MHD_Result
  44. access_handler (void *cls,
  45. struct MHD_Connection *connection,
  46. const char *url,
  47. const char *method,
  48. const char *version,
  49. const char *upload_data,
  50. size_t *upload_data_size,
  51. void **ptr)
  52. {
  53. /* ... */
  54. /* some code to decide whether to upgrade or not */
  55. /* ... */
  56. /* create the response for upgrade */
  57. response = MHD_create_response_for_upgrade (&upgrade_handler,
  58. NULL);
  59. /* ... */
  60. /* additional headers, etc. */
  61. /* ... */
  62. ret = MHD_queue_response (connection,
  63. MHD_HTTP_SWITCHING_PROTOCOLS,
  64. response);
  65. MHD_destroy_response (response);
  66. return ret;
  67. }
  68. @end verbatim
  69. @noindent
  70. In the @code{upgrade_handler} we receive the low-level socket,
  71. which is used for the communication with the specific client.
  72. In addition to the low-level socket we get:
  73. @itemize @bullet
  74. @item
  75. Some data, which has been read too much while @emph{libmicrohttpd} was
  76. switching the protocols.
  77. This value is usually empty, because it would mean that the client
  78. has sent data before the handshake was complete.
  79. @item
  80. A @code{struct MHD_UpgradeResponseHandle} which is used to perform
  81. special actions like closing, corking or uncorking the socket.
  82. These commands are executed by passing the handle
  83. to @code{MHD_upgrade_action()}.
  84. @end itemize
  85. Depending of the flags specified while calling @code{MHD_start_deamon()}
  86. our @code{upgrade_handler} is either executed in the same thread
  87. as our daemon or in a thread specific for each connection.
  88. If it is executed in the same thread then @code{upgrade_handler} is
  89. a blocking call for our webserver and
  90. we should finish it as fast as possible (i. e. by creating a thread and
  91. passing the information there).
  92. If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
  93. @code{MHD_start_daemon()} then a separate thread is used and
  94. thus our @code{upgrade_handler} needs not to start a separate thread.
  95. An @code{upgrade_handler}, which is called with a separate thread
  96. per connection, could look like this:
  97. @verbatim
  98. static void
  99. upgrade_handler (void *cls,
  100. struct MHD_Connection *connection,
  101. void *req_cls,
  102. const char *extra_in,
  103. size_t extra_in_size,
  104. MHD_socket fd,
  105. struct MHD_UpgradeResponseHandle *urh)
  106. {
  107. /* ... */
  108. /* do something with the socket `fd` like `recv()` or `send()` */
  109. /* ... */
  110. /* close the socket when it is not needed anymore */
  111. MHD_upgrade_action (urh,
  112. MHD_UPGRADE_ACTION_CLOSE);
  113. }
  114. @end verbatim
  115. @noindent
  116. This is all you need to know for upgrading connections
  117. with @emph{libmicrohttpd}.
  118. The next chapters focus on using the websocket protocol
  119. with @emph{libmicrohttpd_ws}.
  120. @heading Websocket handshake with libmicrohttpd_ws
  121. To request a websocket connection the client must send
  122. the following information with the HTTP request:
  123. @itemize @bullet
  124. @item
  125. A @code{GET} request must be sent.
  126. @item
  127. The version of the HTTP protocol must be 1.1 or higher.
  128. @item
  129. A @code{Host} header field must be sent
  130. @item
  131. A @code{Upgrade} header field containing the keyword "websocket"
  132. (case-insensitive).
  133. Please note that the client could pass multiple protocols separated by comma.
  134. @item
  135. A @code{Connection} header field that includes the token "Upgrade"
  136. (case-insensitive).
  137. Please note that the client could pass multiple tokens separated by comma.
  138. @item
  139. A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
  140. The decoded the value is 16 bytes long
  141. and has been generated randomly by the client.
  142. @item
  143. A @code{Sec-WebSocket-Version} header field with the value "13".
  144. @end itemize
  145. Optionally the client can also send the following information:
  146. @itemize @bullet
  147. @item
  148. A @code{Origin} header field can be used to determine the source
  149. of the client (i. e. the website).
  150. @item
  151. A @code{Sec-WebSocket-Protocol} header field can contain a list
  152. of supported protocols by the client, which can be sent over the websocket.
  153. @item
  154. A @code{Sec-WebSocket-Extensions} header field which may contain extensions
  155. to the websocket protocol. The extensions must be registered by IANA.
  156. @end itemize
  157. A valid example request from the client could look like this:
  158. @verbatim
  159. GET /chat HTTP/1.1
  160. Host: server.example.com
  161. Upgrade: websocket
  162. Connection: Upgrade
  163. Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
  164. Sec-WebSocket-Version: 13
  165. @end verbatim
  166. @noindent
  167. To complete the handshake the server must respond with
  168. some specific response headers:
  169. @itemize @bullet
  170. @item
  171. The HTTP response code @code{101 Switching Protocols} must be answered.
  172. @item
  173. An @code{Upgrade} header field containing the value "websocket" must be sent.
  174. @item
  175. A @code{Connection} header field containing the value "Upgrade" must be sent.
  176. @item
  177. A @code{Sec-WebSocket-Accept} header field containing a value, which
  178. has been calculated from the @code{Sec-WebSocket-Key} request header field,
  179. must be sent.
  180. @end itemize
  181. Optionally the server may send following headers:
  182. @itemize @bullet
  183. @item
  184. A @code{Sec-WebSocket-Protocol} header field containing a protocol
  185. of the list specified in the corresponding request header field.
  186. @item
  187. A @code{Sec-WebSocket-Extension} header field containing all used extensions
  188. of the list specified in the corresponding request header field.
  189. @end itemize
  190. A valid websocket HTTP response could look like this:
  191. @verbatim
  192. HTTP/1.1 101 Switching Protocols
  193. Upgrade: websocket
  194. Connection: Upgrade
  195. Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  196. @end verbatim
  197. @noindent
  198. To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
  199. some helper functions for the @code{access_handler} callback function:
  200. @itemize @bullet
  201. @item
  202. @code{MHD_websocket_check_http_version()} checks whether the HTTP version
  203. is 1.1 or above.
  204. @item
  205. @code{MHD_websocket_check_connection_header()} checks whether the value
  206. of the @code{Connection} request header field contains
  207. an "Upgrade" token (case-insensitive).
  208. @item
  209. @code{MHD_websocket_check_upgrade_header()} checks whether the value
  210. of the @code{Upgrade} request header field contains
  211. the "websocket" keyword (case-insensitive).
  212. @item
  213. @code{MHD_websocket_check_version_header()} checks whether the value
  214. of the @code{Sec-WebSocket-Version} request header field is "13".
  215. @item
  216. @code{MHD_websocket_create_accept_header()} takes the value from
  217. the @code{Sec-WebSocket-Key} request header and calculates the value
  218. for the @code{Sec-WebSocket-Accept} response header field.
  219. @end itemize
  220. The @code{access_handler} example of the previous chapter can now be
  221. extended with these helper functions to perform the websocket handshake:
  222. @verbatim
  223. static enum MHD_Result
  224. access_handler (void *cls,
  225. struct MHD_Connection *connection,
  226. const char *url,
  227. const char *method,
  228. const char *version,
  229. const char *upload_data,
  230. size_t *upload_data_size,
  231. void **ptr)
  232. {
  233. static int aptr;
  234. struct MHD_Response *response;
  235. int ret;
  236. (void) cls; /* Unused. Silent compiler warning. */
  237. (void) upload_data; /* Unused. Silent compiler warning. */
  238. (void) upload_data_size; /* Unused. Silent compiler warning. */
  239. if (0 != strcmp (method, "GET"))
  240. return MHD_NO; /* unexpected method */
  241. if (&aptr != *ptr)
  242. {
  243. /* do never respond on first call */
  244. *ptr = &aptr;
  245. return MHD_YES;
  246. }
  247. *ptr = NULL; /* reset when done */
  248. if (0 == strcmp (url, "/"))
  249. {
  250. /* Default page for visiting the server */
  251. struct MHD_Response *response = MHD_create_response_from_buffer (
  252. strlen (PAGE),
  253. PAGE,
  254. MHD_RESPMEM_PERSISTENT);
  255. ret = MHD_queue_response (connection,
  256. MHD_HTTP_OK,
  257. response);
  258. MHD_destroy_response (response);
  259. }
  260. else if (0 == strcmp (url, "/chat"))
  261. {
  262. char is_valid = 1;
  263. const char* value = NULL;
  264. char sec_websocket_accept[29];
  265. if (0 != MHD_websocket_check_http_version (version))
  266. {
  267. is_valid = 0;
  268. }
  269. value = MHD_lookup_connection_value (connection,
  270. MHD_HEADER_KIND,
  271. MHD_HTTP_HEADER_CONNECTION);
  272. if (0 != MHD_websocket_check_connection_header (value))
  273. {
  274. is_valid = 0;
  275. }
  276. value = MHD_lookup_connection_value (connection,
  277. MHD_HEADER_KIND,
  278. MHD_HTTP_HEADER_UPGRADE);
  279. if (0 != MHD_websocket_check_upgrade_header (value))
  280. {
  281. is_valid = 0;
  282. }
  283. value = MHD_lookup_connection_value (connection,
  284. MHD_HEADER_KIND,
  285. MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
  286. if (0 != MHD_websocket_check_version_header (value))
  287. {
  288. is_valid = 0;
  289. }
  290. value = MHD_lookup_connection_value (connection,
  291. MHD_HEADER_KIND,
  292. MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
  293. if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
  294. {
  295. is_valid = 0;
  296. }
  297. if (1 == is_valid)
  298. {
  299. /* upgrade the connection */
  300. response = MHD_create_response_for_upgrade (&upgrade_handler,
  301. NULL);
  302. MHD_add_response_header (response,
  303. MHD_HTTP_HEADER_CONNECTION,
  304. "Upgrade");
  305. MHD_add_response_header (response,
  306. MHD_HTTP_HEADER_UPGRADE,
  307. "websocket");
  308. MHD_add_response_header (response,
  309. MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
  310. sec_websocket_accept);
  311. ret = MHD_queue_response (connection,
  312. MHD_HTTP_SWITCHING_PROTOCOLS,
  313. response);
  314. MHD_destroy_response (response);
  315. }
  316. else
  317. {
  318. /* return error page */
  319. struct MHD_Response*response = MHD_create_response_from_buffer (
  320. strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
  321. PAGE_INVALID_WEBSOCKET_REQUEST,
  322. MHD_RESPMEM_PERSISTENT);
  323. ret = MHD_queue_response (connection,
  324. MHD_HTTP_BAD_REQUEST,
  325. response);
  326. MHD_destroy_response (response);
  327. }
  328. }
  329. else
  330. {
  331. struct MHD_Response*response = MHD_create_response_from_buffer (
  332. strlen (PAGE_NOT_FOUND),
  333. PAGE_NOT_FOUND,
  334. MHD_RESPMEM_PERSISTENT);
  335. ret = MHD_queue_response (connection,
  336. MHD_HTTP_NOT_FOUND,
  337. response);
  338. MHD_destroy_response (response);
  339. }
  340. return ret;
  341. }
  342. @end verbatim
  343. @noindent
  344. Please note that we skipped the check of the Host header field here,
  345. because we don't know the host for this example.
  346. @heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
  347. Once the websocket connection is established you can receive/send frame data
  348. with the low-level socket functions @code{recv()} and @code{send()}.
  349. The frame data which goes over the low-level socket is encoded according
  350. to the websocket protocol.
  351. To use received payload data, you need to decode the frame data first.
  352. To send payload data, you need to encode it into frame data first.
  353. @emph{libmicrohttpd_ws} provides several functions for encoding of
  354. payload data and decoding of frame data:
  355. @itemize @bullet
  356. @item
  357. @code{MHD_websocket_decode()} decodes received frame data.
  358. The payload data may be of any kind, depending upon what the client has sent.
  359. So this decode function is used for all kind of frames and returns
  360. the frame type along with the payload data.
  361. @item
  362. @code{MHD_websocket_encode_text()} encodes text.
  363. The text must be encoded with UTF-8.
  364. @item
  365. @code{MHD_websocket_encode_binary()} encodes binary data.
  366. @item
  367. @code{MHD_websocket_encode_ping()} encodes a ping request to
  368. check whether the websocket is still valid and to test latency.
  369. @item
  370. @code{MHD_websocket_encode_ping()} encodes a pong response to
  371. answer a received ping request.
  372. @item
  373. @code{MHD_websocket_encode_close()} encodes a close request.
  374. @item
  375. @code{MHD_websocket_free()} frees data returned by the encode/decode functions.
  376. @end itemize
  377. Since you could receive or send fragmented data (i. e. due to a too
  378. small buffer passed to @code{recv}) all of these encode/decode
  379. functions require a pointer to a @code{struct MHD_WebSocketStream} passed
  380. as argument.
  381. In this structure @emph{libmicrohttpd_ws} stores information
  382. about encoding/decoding of the particular websocket.
  383. For each websocket you need a unique @code{struct MHD_WebSocketStream}
  384. to encode/decode with this library.
  385. To create or destroy @code{struct MHD_WebSocketStream}
  386. we have additional functions:
  387. @itemize @bullet
  388. @item
  389. @code{MHD_websocket_stream_init()} allocates and initializes
  390. a new @code{struct MHD_WebSocketStream}.
  391. You can specify some options here to alter the behavior of the websocket stream.
  392. @item
  393. @code{MHD_websocket_stream_free()} frees a previously allocated
  394. @code{struct MHD_WebSocketStream}.
  395. @end itemize
  396. With these encode/decode functions we can improve our @code{upgrade_handler}
  397. callback function from an earlier example to a working websocket:
  398. @verbatim
  399. static void
  400. upgrade_handler (void *cls,
  401. struct MHD_Connection *connection,
  402. void *req_cls,
  403. const char *extra_in,
  404. size_t extra_in_size,
  405. MHD_socket fd,
  406. struct MHD_UpgradeResponseHandle *urh)
  407. {
  408. /* make the socket blocking (operating-system-dependent code) */
  409. make_blocking (fd);
  410. /* create a websocket stream for this connection */
  411. struct MHD_WebSocketStream* ws;
  412. int result = MHD_websocket_stream_init (&ws,
  413. 0,
  414. 0);
  415. if (0 != result)
  416. {
  417. /* Couldn't create the websocket stream.
  418. * So we close the socket and leave
  419. */
  420. MHD_upgrade_action (urh,
  421. MHD_UPGRADE_ACTION_CLOSE);
  422. return;
  423. }
  424. /* Let's wait for incoming data */
  425. const size_t buf_len = 256;
  426. char buf[buf_len];
  427. ssize_t got;
  428. while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
  429. {
  430. got = recv (fd,
  431. buf,
  432. buf_len,
  433. 0);
  434. if (0 >= got)
  435. {
  436. /* the TCP/IP socket has been closed */
  437. break;
  438. }
  439. /* parse the entire received data */
  440. size_t buf_offset = 0;
  441. while (buf_offset < (size_t) got)
  442. {
  443. size_t new_offset = 0;
  444. char *frame_data = NULL;
  445. size_t frame_len = 0;
  446. int status = MHD_websocket_decode (ws,
  447. buf + buf_offset,
  448. ((size_t) got) - buf_offset,
  449. &new_offset,
  450. &frame_data,
  451. &frame_len);
  452. if (0 > status)
  453. {
  454. /* an error occurred and the connection must be closed */
  455. if (NULL != frame_data)
  456. {
  457. MHD_websocket_free (ws, frame_data);
  458. }
  459. break;
  460. }
  461. else
  462. {
  463. buf_offset += new_offset;
  464. if (0 < status)
  465. {
  466. /* the frame is complete */
  467. switch (status)
  468. {
  469. case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
  470. /* The client has sent some text.
  471. * We will display it and answer with a text frame.
  472. */
  473. if (NULL != frame_data)
  474. {
  475. printf ("Received message: %s\n", frame_data);
  476. MHD_websocket_free (ws, frame_data);
  477. frame_data = NULL;
  478. }
  479. result = MHD_websocket_encode_text (ws,
  480. "Hello",
  481. 5, /* length of "Hello" */
  482. 0,
  483. &frame_data,
  484. &frame_len,
  485. NULL);
  486. if (0 == result)
  487. {
  488. send_all (fd,
  489. frame_data,
  490. frame_len);
  491. }
  492. break;
  493. case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
  494. /* if we receive a close frame, we will respond with one */
  495. MHD_websocket_free (ws,
  496. frame_data);
  497. frame_data = NULL;
  498. result = MHD_websocket_encode_close (ws,
  499. 0,
  500. NULL,
  501. 0,
  502. &frame_data,
  503. &frame_len);
  504. if (0 == result)
  505. {
  506. send_all (fd,
  507. frame_data,
  508. frame_len);
  509. }
  510. break;
  511. case MHD_WEBSOCKET_STATUS_PING_FRAME:
  512. /* if we receive a ping frame, we will respond */
  513. /* with the corresponding pong frame */
  514. {
  515. char *pong = NULL;
  516. size_t pong_len = 0;
  517. result = MHD_websocket_encode_pong (ws,
  518. frame_data,
  519. frame_len,
  520. &pong,
  521. &pong_len);
  522. if (0 == result)
  523. {
  524. send_all (fd,
  525. pong,
  526. pong_len);
  527. }
  528. MHD_websocket_free (ws,
  529. pong);
  530. }
  531. break;
  532. default:
  533. /* Other frame types are ignored
  534. * in this minimal example.
  535. * This is valid, because they become
  536. * automatically skipped if we receive them unexpectedly
  537. */
  538. break;
  539. }
  540. }
  541. if (NULL != frame_data)
  542. {
  543. MHD_websocket_free (ws, frame_data);
  544. }
  545. }
  546. }
  547. }
  548. /* free the websocket stream */
  549. MHD_websocket_stream_free (ws);
  550. /* close the socket when it is not needed anymore */
  551. MHD_upgrade_action (urh,
  552. MHD_UPGRADE_ACTION_CLOSE);
  553. }
  554. /* This helper function is used for the case that
  555. * we need to resend some data
  556. */
  557. static void
  558. send_all (MHD_socket fd,
  559. const char *buf,
  560. size_t len)
  561. {
  562. ssize_t ret;
  563. size_t off;
  564. for (off = 0; off < len; off += ret)
  565. {
  566. ret = send (fd,
  567. &buf[off],
  568. (int) (len - off),
  569. 0);
  570. if (0 > ret)
  571. {
  572. if (EAGAIN == errno)
  573. {
  574. ret = 0;
  575. continue;
  576. }
  577. break;
  578. }
  579. if (0 == ret)
  580. break;
  581. }
  582. }
  583. /* This helper function contains operating-system-dependent code and
  584. * is used to make a socket blocking.
  585. */
  586. static void
  587. make_blocking (MHD_socket fd)
  588. {
  589. #if defined(MHD_POSIX_SOCKETS)
  590. int flags;
  591. flags = fcntl (fd, F_GETFL);
  592. if (-1 == flags)
  593. return;
  594. if ((flags & ~O_NONBLOCK) != flags)
  595. if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
  596. abort ();
  597. #elif defined(MHD_WINSOCK_SOCKETS)
  598. unsigned long flags = 0;
  599. ioctlsocket (fd, FIONBIO, &flags);
  600. #endif /* MHD_WINSOCK_SOCKETS */
  601. }
  602. @end verbatim
  603. @noindent
  604. Please note that the websocket in this example is only half-duplex.
  605. It waits until the blocking @code{recv()} call returns and
  606. only does then something.
  607. In this example all frame types are decoded by @emph{libmicrohttpd_ws},
  608. but we only do something when a text, ping or close frame is received.
  609. Binary and pong frames are ignored in our code.
  610. This is legit, because the server is only required to implement at
  611. least support for ping frame or close frame (the other frame types
  612. could be skipped in theory, because they don't require an answer).
  613. The pong frame doesn't require an answer and whether text frames or
  614. binary frames get an answer simply belongs to your server application.
  615. So this is a valid minimal example.
  616. Until this point you've learned everything you need to basically
  617. use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
  618. These libraries offer much more functions for some specific cases.
  619. The further chapters of this tutorial focus on some specific problems
  620. and the client site programming.
  621. @heading Using full-duplex websockets
  622. To use full-duplex websockets you can simply create two threads
  623. per websocket connection.
  624. One of these threads is used for receiving data with
  625. a blocking @code{recv()} call and the other thread is triggered
  626. by the application internal codes and sends the data.
  627. A full-duplex websocket example is implemented in the example file
  628. @code{websocket_chatserver_example.c}.
  629. @heading Error handling
  630. The most functions of @emph{libmicrohttpd_ws} return a value
  631. of @code{enum MHD_WEBSOCKET_STATUS}.
  632. The values of this enumeration can be converted into an integer
  633. and have an easy interpretation:
  634. @itemize @bullet
  635. @item
  636. If the value is less than zero an error occurred and the call has failed.
  637. Check the enumeration values for more specific information.
  638. @item
  639. If the value is equal to zero, the call succeeded.
  640. @item
  641. If the value is greater than zero, the call succeeded and the value
  642. specifies the decoded frame type.
  643. Currently positive values are only returned by @code{MHD_websocket_decode()}
  644. (of the functions with this return enumeration type).
  645. @end itemize
  646. A websocket stream can also get broken when invalid frame data is received.
  647. Also the other site could send a close frame which puts the stream into
  648. a state where it may not be used for regular communication.
  649. Whether a stream has become broken, can be checked with
  650. @code{MHD_websocket_stream_is_valid()}.
  651. @heading Fragmentation
  652. In addition to the regular TCP/IP fragmentation the websocket protocol also
  653. supports fragmentation.
  654. Fragmentation could be used for continuous payload data such as video data
  655. from a webcam.
  656. Whether or not you want to receive fragmentation is specified upon
  657. initialization of the websocket stream.
  658. If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
  659. of @code{MHD_websocket_stream_init()} then you can receive fragments.
  660. If you don't pass this flag (in the most cases you just pass zero as flags)
  661. then you don't want to handle fragments on your own.
  662. @emph{libmicrohttpd_ws} removes then the fragmentation for you
  663. in the background.
  664. You only get the completely assembled frames.
  665. Upon encoding you specify whether or not you want to create a fragmented frame
  666. by passing a flag to the corresponding encode function.
  667. Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
  668. can be used for fragmentation, because the other frame types may
  669. not be fragmented.
  670. Encoding fragmented frames is independent of
  671. the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
  672. @heading Quick guide to websockets in JavaScript
  673. Websockets are supported in all modern web browsers.
  674. You initialize a websocket connection by creating an instance of
  675. the @code{WebSocket} class provided by the web browser.
  676. There are some simple rules for using websockets in the browser:
  677. @itemize @bullet
  678. @item
  679. When you initialize the instance of the websocket class you must pass an URL.
  680. The URL must either start with @code{ws://}
  681. (for not encrypted websocket protocol) or @code{wss://}
  682. (for TLS-encrypted websocket protocol).
  683. @strong{IMPORTANT:} If your website is accessed via @code{https://}
  684. then you are in a security context, which means that you are only allowed to
  685. access other secure protocols.
  686. So you can only use @code{wss://} for websocket connections then.
  687. If you try to @code{ws://} instead then your websocket connection will
  688. automatically fail.
  689. @item
  690. The WebSocket class uses events to handle the receiving of data.
  691. JavaScript is per definition a single-threaded language so
  692. the receiving events will never overlap.
  693. Sending is done directly by calling a method of the instance of
  694. the WebSocket class.
  695. @end itemize
  696. Here is a short example for receiving/sending data to the same host
  697. as the website is running on:
  698. @verbatim
  699. <!DOCTYPE html>
  700. <html>
  701. <head>
  702. <meta charset="UTF-8">
  703. <title>Websocket Demo</title>
  704. <script>
  705. let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
  706. window.location.host + '/chat';
  707. let socket = null;
  708. window.onload = function(event) {
  709. socket = new WebSocket(url);
  710. socket.onopen = function(event) {
  711. document.write('The websocket connection has been established.<br>');
  712. // Send some text
  713. socket.send('Hello from JavaScript!');
  714. }
  715. socket.onclose = function(event) {
  716. document.write('The websocket connection has been closed.<br>');
  717. }
  718. socket.onerror = function(event) {
  719. document.write('An error occurred during the websocket communication.<br>');
  720. }
  721. socket.onmessage = function(event) {
  722. document.write('Websocket message received: ' + event.data + '<br>');
  723. }
  724. }
  725. </script>
  726. </head>
  727. <body>
  728. </body>
  729. </html>
  730. @end verbatim
  731. @noindent