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