]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/examples/embedded_c/embedded_c.c
buildsys: switch source download to quincy
[ceph.git] / ceph / src / civetweb / examples / embedded_c / embedded_c.c
1 /*
2 * Copyright (c) 2013-2017 the CivetWeb developers
3 * Copyright (c) 2013 No Face Press, LLC
4 * License http://opensource.org/licenses/mit-license.php MIT License
5 */
6
7 /* Simple example program on how to use CivetWeb embedded into a C program. */
8 #ifdef _WIN32
9 #include <windows.h>
10 #else
11 #include <unistd.h>
12 #endif
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17
18 #include "civetweb.h"
19
20
21 #define DOCUMENT_ROOT "."
22 #ifdef NO_SSL
23 #ifdef USE_IPV6
24 #define PORT "[::]:8888,8884"
25 #else
26 #define PORT "8888,8884"
27 #endif
28 #else
29 #ifdef USE_IPV6
30 #define PORT "[::]:8888r,[::]:8843s,8884"
31 #else
32 #define PORT "8888r,8843s,8884"
33 #endif
34 #endif
35 #define EXAMPLE_URI "/example"
36 #define EXIT_URI "/exit"
37 int exitNow = 0;
38
39
40 int
41 ExampleHandler(struct mg_connection *conn, void *cbdata)
42 {
43 mg_printf(conn,
44 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
45 "close\r\n\r\n");
46 mg_printf(conn, "<html><body>");
47 mg_printf(conn, "<h2>This is an example text from a C handler</h2>");
48 mg_printf(
49 conn,
50 "<p>To see a page from the A handler <a href=\"A\">click A</a></p>");
51 mg_printf(conn,
52 "<p>To see a page from the A handler <a href=\"A/A\">click "
53 "A/A</a></p>");
54 mg_printf(conn,
55 "<p>To see a page from the A/B handler <a "
56 "href=\"A/B\">click A/B</a></p>");
57 mg_printf(conn,
58 "<p>To see a page from the B handler (0) <a "
59 "href=\"B\">click B</a></p>");
60 mg_printf(conn,
61 "<p>To see a page from the B handler (1) <a "
62 "href=\"B/A\">click B/A</a></p>");
63 mg_printf(conn,
64 "<p>To see a page from the B handler (2) <a "
65 "href=\"B/B\">click B/B</a></p>");
66 mg_printf(conn,
67 "<p>To see a page from the *.foo handler <a "
68 "href=\"xy.foo\">click xy.foo</a></p>");
69 mg_printf(conn,
70 "<p>To see a page from the close handler <a "
71 "href=\"close\">click close</a></p>");
72 mg_printf(conn,
73 "<p>To see a page from the FileHandler handler <a "
74 "href=\"form\">click form</a> (the starting point of the "
75 "<b>form</b> test)</p>");
76 mg_printf(conn,
77 "<p>To see a page from the CookieHandler handler <a "
78 "href=\"cookie\">click cookie</a></p>");
79 mg_printf(conn,
80 "<p>To see a page from the PostResponser handler <a "
81 "href=\"postresponse\">click post response</a></p>");
82 mg_printf(conn,
83 "<p>To see an example for parsing files on the fly <a "
84 "href=\"on_the_fly_form\">click form</a> (form for "
85 "uploading files)</p>");
86
87 #ifdef USE_WEBSOCKET
88 mg_printf(conn,
89 "<p>To test the websocket handler <a href=\"/websocket\">click "
90 "websocket</a></p>");
91 #endif
92
93 mg_printf(conn,
94 "<p>To test the authentication handler <a href=\"/auth\">click "
95 "auth</a></p>");
96
97 mg_printf(conn, "<p>To exit <a href=\"%s\">click exit</a></p>", EXIT_URI);
98 mg_printf(conn, "</body></html>\n");
99 return 1;
100 }
101
102
103 int
104 ExitHandler(struct mg_connection *conn, void *cbdata)
105 {
106 mg_printf(conn,
107 "HTTP/1.1 200 OK\r\nContent-Type: "
108 "text/plain\r\nConnection: close\r\n\r\n");
109 mg_printf(conn, "Server will shut down.\n");
110 mg_printf(conn, "Bye!\n");
111 exitNow = 1;
112 return 1;
113 }
114
115
116 int
117 AHandler(struct mg_connection *conn, void *cbdata)
118 {
119 mg_printf(conn,
120 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
121 "close\r\n\r\n");
122 mg_printf(conn, "<html><body>");
123 mg_printf(conn, "<h2>This is the A handler!!!</h2>");
124 mg_printf(conn, "</body></html>\n");
125 return 1;
126 }
127
128
129 int
130 ABHandler(struct mg_connection *conn, void *cbdata)
131 {
132 mg_printf(conn,
133 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
134 "close\r\n\r\n");
135 mg_printf(conn, "<html><body>");
136 mg_printf(conn, "<h2>This is the AB handler!!!</h2>");
137 mg_printf(conn, "</body></html>\n");
138 return 1;
139 }
140
141
142 int
143 BXHandler(struct mg_connection *conn, void *cbdata)
144 {
145 /* Handler may access the request info using mg_get_request_info */
146 const struct mg_request_info *req_info = mg_get_request_info(conn);
147
148 mg_printf(conn,
149 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
150 "close\r\n\r\n");
151 mg_printf(conn, "<html><body>");
152 mg_printf(conn, "<h2>This is the BX handler %p!!!</h2>", cbdata);
153 mg_printf(conn, "<p>The actual uri is %s</p>", req_info->local_uri);
154 mg_printf(conn, "</body></html>\n");
155 return 1;
156 }
157
158
159 int
160 FooHandler(struct mg_connection *conn, void *cbdata)
161 {
162 /* Handler may access the request info using mg_get_request_info */
163 const struct mg_request_info *req_info = mg_get_request_info(conn);
164
165 mg_printf(conn,
166 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
167 "close\r\n\r\n");
168 mg_printf(conn, "<html><body>");
169 mg_printf(conn, "<h2>This is the Foo handler!!!</h2>");
170 mg_printf(conn,
171 "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>",
172 req_info->request_method,
173 req_info->local_uri,
174 req_info->http_version);
175 mg_printf(conn, "</body></html>\n");
176 return 1;
177 }
178
179
180 int
181 CloseHandler(struct mg_connection *conn, void *cbdata)
182 {
183 /* Handler may access the request info using mg_get_request_info */
184 const struct mg_request_info *req_info = mg_get_request_info(conn);
185
186 mg_printf(conn,
187 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
188 "close\r\n\r\n");
189 mg_printf(conn, "<html><body>");
190 mg_printf(conn,
191 "<h2>This handler will close the connection in a second</h2>");
192 #ifdef _WIN32
193 Sleep(1000);
194 #else
195 sleep(1);
196 #endif
197 mg_printf(conn, "bye");
198 printf("CloseHandler: close connection\n");
199 mg_close_connection(conn);
200 printf("CloseHandler: wait 10 sec\n");
201 #ifdef _WIN32
202 Sleep(10000);
203 #else
204 sleep(10);
205 #endif
206 printf("CloseHandler: return from function\n");
207 return 1;
208 }
209
210
211 int
212 FileHandler(struct mg_connection *conn, void *cbdata)
213 {
214 /* In this handler, we ignore the req_info and send the file "fileName". */
215 const char *fileName = (const char *)cbdata;
216
217 mg_send_file(conn, fileName);
218 return 1;
219 }
220
221
222 int
223 field_found(const char *key,
224 const char *filename,
225 char *path,
226 size_t pathlen,
227 void *user_data)
228 {
229 struct mg_connection *conn = (struct mg_connection *)user_data;
230
231 mg_printf(conn, "\r\n\r\n%s:\r\n", key);
232
233 if (filename && *filename) {
234 #ifdef _WIN32
235 _snprintf(path, pathlen, "D:\\tmp\\%s", filename);
236 #else
237 snprintf(path, pathlen, "/tmp/%s", filename);
238 #endif
239 return FORM_FIELD_STORAGE_STORE;
240 }
241 return FORM_FIELD_STORAGE_GET;
242 }
243
244
245 int
246 field_get(const char *key, const char *value, size_t valuelen, void *user_data)
247 {
248 struct mg_connection *conn = (struct mg_connection *)user_data;
249
250 if (key[0]) {
251 mg_printf(conn, "%s = ", key);
252 }
253 mg_write(conn, value, valuelen);
254
255 return 0;
256 }
257
258
259 int
260 field_stored(const char *path, long long file_size, void *user_data)
261 {
262 struct mg_connection *conn = (struct mg_connection *)user_data;
263
264 mg_printf(conn,
265 "stored as %s (%lu bytes)\r\n\r\n",
266 path,
267 (unsigned long)file_size);
268
269 return 0;
270 }
271
272
273 int
274 FormHandler(struct mg_connection *conn, void *cbdata)
275 {
276 /* Handler may access the request info using mg_get_request_info */
277 const struct mg_request_info *req_info = mg_get_request_info(conn);
278 int ret;
279 struct mg_form_data_handler fdh = {field_found, field_get, field_stored, 0};
280
281 /* It would be possible to check the request info here before calling
282 * mg_handle_form_request. */
283 (void)req_info;
284
285 mg_printf(conn,
286 "HTTP/1.1 200 OK\r\nContent-Type: "
287 "text/plain\r\nConnection: close\r\n\r\n");
288 fdh.user_data = (void *)conn;
289
290 /* Call the form handler */
291 mg_printf(conn, "Form data:");
292 ret = mg_handle_form_request(conn, &fdh);
293 mg_printf(conn, "\r\n%i fields found", ret);
294
295 return 1;
296 }
297
298
299 int
300 FileUploadForm(struct mg_connection *conn, void *cbdata)
301 {
302 mg_printf(conn,
303 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
304 "close\r\n\r\n");
305
306 mg_printf(conn, "<!DOCTYPE html>\n");
307 mg_printf(conn, "<html>\n<head>\n");
308 mg_printf(conn, "<meta charset=\"UTF-8\">\n");
309 mg_printf(conn, "<title>File upload</title>\n");
310 mg_printf(conn, "</head>\n<body>\n");
311 mg_printf(conn,
312 "<form action=\"%s\" method=\"POST\" "
313 "enctype=\"multipart/form-data\">\n",
314 (const char *)cbdata);
315 mg_printf(conn, "<input type=\"file\" name=\"filesin\" multiple>\n");
316 mg_printf(conn, "<input type=\"submit\" value=\"Submit\">\n");
317 mg_printf(conn, "</form>\n</body>\n</html>\n");
318 return 1;
319 }
320
321 #define MD5_STATIC static
322 #include "../src/md5.inl"
323
324 struct tfile_checksum {
325 char name[128];
326 unsigned long long length;
327 md5_state_t chksum;
328 };
329
330 #define MAX_FILES (10)
331
332 struct tfiles_checksums {
333 int index;
334 struct tfile_checksum file[MAX_FILES];
335 };
336
337
338 int
339 field_disp_read_on_the_fly(const char *key,
340 const char *filename,
341 char *path,
342 size_t pathlen,
343 void *user_data)
344 {
345 struct tfiles_checksums *context = (struct tfiles_checksums *)user_data;
346
347 (void)key;
348 (void)path;
349 (void)pathlen;
350
351 if (context->index < MAX_FILES) {
352 context->index++;
353 strncpy(context->file[context->index - 1].name, filename, 128);
354 context->file[context->index - 1].name[127] = 0;
355 context->file[context->index - 1].length = 0;
356 md5_init(&(context->file[context->index - 1].chksum));
357 return FORM_FIELD_STORAGE_GET;
358 }
359 return FORM_FIELD_STORAGE_ABORT;
360 }
361
362
363 int
364 field_get_checksum(const char *key,
365 const char *value,
366 size_t valuelen,
367 void *user_data)
368 {
369 struct tfiles_checksums *context = (struct tfiles_checksums *)user_data;
370 (void)key;
371
372 context->file[context->index - 1].length += valuelen;
373 md5_append(&(context->file[context->index - 1].chksum),
374 (const md5_byte_t *)value,
375 valuelen);
376
377 return 0;
378 }
379
380
381 int
382 CheckSumHandler(struct mg_connection *conn, void *cbdata)
383 {
384 /* Handler may access the request info using mg_get_request_info */
385 const struct mg_request_info *req_info = mg_get_request_info(conn);
386 int i, j, ret;
387 struct tfiles_checksums chksums;
388 md5_byte_t digest[16];
389 struct mg_form_data_handler fdh = {field_disp_read_on_the_fly,
390 field_get_checksum,
391 0,
392 (void *)&chksums};
393
394 /* It would be possible to check the request info here before calling
395 * mg_handle_form_request. */
396 (void)req_info;
397
398 memset(&chksums, 0, sizeof(chksums));
399
400 mg_printf(conn,
401 "HTTP/1.1 200 OK\r\n"
402 "Content-Type: text/plain\r\n"
403 "Connection: close\r\n\r\n");
404
405 /* Call the form handler */
406 mg_printf(conn, "File checksums:");
407 ret = mg_handle_form_request(conn, &fdh);
408 for (i = 0; i < chksums.index; i++) {
409 md5_finish(&(chksums.file[i].chksum), digest);
410 /* Visual Studio 2010+ support llu */
411 mg_printf(conn,
412 "\r\n%s %llu ",
413 chksums.file[i].name,
414 chksums.file[i].length);
415 for (j = 0; j < 16; j++) {
416 mg_printf(conn, "%02x", (unsigned int)digest[j]);
417 }
418 }
419 mg_printf(conn, "\r\n%i files\r\n", ret);
420
421 return 1;
422 }
423
424
425 int
426 CookieHandler(struct mg_connection *conn, void *cbdata)
427 {
428 /* Handler may access the request info using mg_get_request_info */
429 const struct mg_request_info *req_info = mg_get_request_info(conn);
430 const char *cookie = mg_get_header(conn, "Cookie");
431 char first_str[64], count_str[64];
432 int count;
433
434 (void)mg_get_cookie(cookie, "first", first_str, sizeof(first_str));
435 (void)mg_get_cookie(cookie, "count", count_str, sizeof(count_str));
436
437 mg_printf(conn, "HTTP/1.1 200 OK\r\nConnection: close\r\n");
438 if (first_str[0] == 0) {
439 time_t t = time(0);
440 struct tm *ptm = localtime(&t);
441 mg_printf(conn,
442 "Set-Cookie: first=%04i-%02i-%02iT%02i:%02i:%02i\r\n",
443 ptm->tm_year + 1900,
444 ptm->tm_mon + 1,
445 ptm->tm_mday,
446 ptm->tm_hour,
447 ptm->tm_min,
448 ptm->tm_sec);
449 }
450 count = (count_str[0] == 0) ? 0 : atoi(count_str);
451 mg_printf(conn, "Set-Cookie: count=%i\r\n", count + 1);
452 mg_printf(conn, "Content-Type: text/html\r\n\r\n");
453
454 mg_printf(conn, "<html><body>");
455 mg_printf(conn, "<h2>This is the CookieHandler.</h2>");
456 mg_printf(conn, "<p>The actual uri is %s</p>", req_info->local_uri);
457
458 if (first_str[0] == 0) {
459 mg_printf(conn, "<p>This is the first time, you opened this page</p>");
460 } else {
461 mg_printf(conn, "<p>You opened this page %i times before.</p>", count);
462 mg_printf(conn, "<p>You first opened this page on %s.</p>", first_str);
463 }
464
465 mg_printf(conn, "</body></html>\n");
466 return 1;
467 }
468
469
470 int
471 PostResponser(struct mg_connection *conn, void *cbdata)
472 {
473 long long r_total = 0;
474 int r, s;
475
476 char buf[2048];
477
478 const struct mg_request_info *ri = mg_get_request_info(conn);
479
480 if (strcmp(ri->request_method, "POST")) {
481 char buf[1024];
482 int ret = mg_get_request_link(conn, buf, sizeof(buf));
483
484 mg_printf(conn,
485 "HTTP/1.1 405 Method Not Allowed\r\nConnection: close\r\n");
486 mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
487 mg_printf(conn,
488 "%s method not allowed in the POST handler\n",
489 ri->request_method);
490 if (ret >= 0) {
491 mg_printf(conn,
492 "use a web tool to send a POST request to %s\n",
493 buf);
494 }
495 return 1;
496 }
497
498 if (ri->content_length >= 0) {
499 /* We know the content length in advance */
500 } else {
501 /* We must read until we find the end (chunked encoding
502 * or connection close), indicated my mg_read returning 0 */
503 }
504
505 mg_printf(conn,
506 "HTTP/1.1 200 OK\r\nConnection: "
507 "close\r\nTransfer-Encoding: chunked\r\n");
508 mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
509
510 r = mg_read(conn, buf, sizeof(buf));
511 while (r > 0) {
512 r_total += r;
513 s = mg_send_chunk(conn, buf, r);
514 if (r != s) {
515 /* Send error */
516 break;
517 }
518 r = mg_read(conn, buf, sizeof(buf));
519 }
520 mg_printf(conn, "0\r\n");
521
522 return 1;
523 }
524
525
526 int
527 AuthStartHandler(struct mg_connection *conn, void *cbdata)
528 {
529 static unsigned long long firstload = 0;
530 const char *passfile = "password_example_file.txt";
531 const char *realm = "password_example";
532 const char *user = "user";
533 char passwd[64];
534
535 if (firstload == 0) {
536
537 /* Set a random password (4 digit number - bad idea from a security
538 * point of view, but this is an API demo, not a security tutorial),
539 * and store it in some directory within the document root (extremely
540 * bad idea, but this is still not a security tutorial).
541 * The reason we create a new password every time the server starts
542 * is just for demonstration - we don't want the browser to store the
543 * password, so when we repeat the test we start with a new password.
544 */
545 firstload = (unsigned long long)time(NULL);
546 sprintf(passwd, "%04u", (unsigned int)(firstload % 10000));
547 mg_modify_passwords_file(passfile, realm, user, passwd);
548
549 /* Just tell the user the new password generated for this test. */
550 mg_printf(conn,
551 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
552 "close\r\n\r\n");
553
554 mg_printf(conn, "<!DOCTYPE html>\n");
555 mg_printf(conn, "<html>\n<head>\n");
556 mg_printf(conn, "<meta charset=\"UTF-8\">\n");
557 mg_printf(conn, "<title>Auth handlerexample</title>\n");
558 mg_printf(conn, "</head>\n");
559
560 mg_printf(conn, "<body>\n");
561 mg_printf(conn,
562 "<p>The first time you visit this page, it's free!</p>\n");
563 mg_printf(conn,
564 "<p>Next time, use username \"%s\" and password \"%s\"</p>\n",
565 user,
566 passwd);
567 mg_printf(conn, "</body>\n</html>\n");
568
569 return 1;
570 }
571
572 if (mg_check_digest_access_authentication(conn, realm, passfile) <= 0) {
573 /* No valid authorization */
574 mg_send_digest_access_authentication_request(conn, realm);
575 return 1;
576 }
577
578 mg_printf(conn,
579 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
580 "close\r\n\r\n");
581
582 mg_printf(conn, "<!DOCTYPE html>\n");
583 mg_printf(conn, "<html>\n<head>\n");
584 mg_printf(conn, "<meta charset=\"UTF-8\">\n");
585 mg_printf(conn, "<title>Auth handlerexample</title>\n");
586 mg_printf(conn, "</head>\n");
587
588 mg_printf(conn, "<body>\n");
589 mg_printf(conn, "<p>This is the password protected contents</p>\n");
590 mg_printf(conn, "</body>\n</html>\n");
591
592 return 1;
593 }
594
595
596 int
597 WebSocketStartHandler(struct mg_connection *conn, void *cbdata)
598 {
599 mg_printf(conn,
600 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
601 "close\r\n\r\n");
602
603 mg_printf(conn, "<!DOCTYPE html>\n");
604 mg_printf(conn, "<html>\n<head>\n");
605 mg_printf(conn, "<meta charset=\"UTF-8\">\n");
606 mg_printf(conn, "<title>Embedded websocket example</title>\n");
607
608 #ifdef USE_WEBSOCKET
609 /* mg_printf(conn, "<script type=\"text/javascript\"><![CDATA[\n"); ...
610 * xhtml style */
611 mg_printf(conn, "<script>\n");
612 mg_printf(
613 conn,
614 "function load() {\n"
615 " var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n"
616 " connection = new WebSocket(wsproto + '//' + window.location.host + "
617 "'/websocket');\n"
618 " websock_text_field = "
619 "document.getElementById('websock_text_field');\n"
620 " connection.onmessage = function (e) {\n"
621 " websock_text_field.innerHTML=e.data;\n"
622 " }\n"
623 " connection.onerror = function (error) {\n"
624 " alert('WebSocket error');\n"
625 " connection.close();\n"
626 " }\n"
627 "}\n");
628 /* mg_printf(conn, "]]></script>\n"); ... xhtml style */
629 mg_printf(conn, "</script>\n");
630 mg_printf(conn, "</head>\n<body onload=\"load()\">\n");
631 mg_printf(
632 conn,
633 "<div id='websock_text_field'>No websocket connection yet</div>\n");
634 #else
635 mg_printf(conn, "</head>\n<body>\n");
636 mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n");
637 #endif
638 mg_printf(conn, "</body>\n</html>\n");
639
640 return 1;
641 }
642
643
644 #ifdef USE_WEBSOCKET
645
646 /* MAX_WS_CLIENTS defines how many clients can connect to a websocket at the
647 * same time. The value 5 is very small and used here only for demonstration;
648 * it can be easily tested to connect more than MAX_WS_CLIENTS clients.
649 * A real server should use a much higher number, or better use a dynamic list
650 * of currently connected websocket clients. */
651 #define MAX_WS_CLIENTS (5)
652
653 struct t_ws_client {
654 struct mg_connection *conn;
655 int state;
656 } static ws_clients[MAX_WS_CLIENTS];
657
658
659 #define ASSERT(x) \
660 { \
661 if (!(x)) { \
662 fprintf(stderr, \
663 "Assertion failed in line %u\n", \
664 (unsigned)__LINE__); \
665 } \
666 }
667
668
669 int
670 WebSocketConnectHandler(const struct mg_connection *conn, void *cbdata)
671 {
672 struct mg_context *ctx = mg_get_context(conn);
673 int reject = 1;
674 int i;
675
676 mg_lock_context(ctx);
677 for (i = 0; i < MAX_WS_CLIENTS; i++) {
678 if (ws_clients[i].conn == NULL) {
679 ws_clients[i].conn = (struct mg_connection *)conn;
680 ws_clients[i].state = 1;
681 mg_set_user_connection_data(ws_clients[i].conn,
682 (void *)(ws_clients + i));
683 reject = 0;
684 break;
685 }
686 }
687 mg_unlock_context(ctx);
688
689 fprintf(stdout,
690 "Websocket client %s\r\n\r\n",
691 (reject ? "rejected" : "accepted"));
692 return reject;
693 }
694
695
696 void
697 WebSocketReadyHandler(struct mg_connection *conn, void *cbdata)
698 {
699 const char *text = "Hello from the websocket ready handler";
700 struct t_ws_client *client = mg_get_user_connection_data(conn);
701
702 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, text, strlen(text));
703 fprintf(stdout, "Greeting message sent to websocket client\r\n\r\n");
704 ASSERT(client->conn == conn);
705 ASSERT(client->state == 1);
706
707 client->state = 2;
708 }
709
710
711 int
712 WebsocketDataHandler(struct mg_connection *conn,
713 int bits,
714 char *data,
715 size_t len,
716 void *cbdata)
717 {
718 struct t_ws_client *client = mg_get_user_connection_data(conn);
719 ASSERT(client->conn == conn);
720 ASSERT(client->state >= 1);
721
722 fprintf(stdout, "Websocket got %lu bytes of ", (unsigned long)len);
723 switch (((unsigned char)bits) & 0x0F) {
724 case WEBSOCKET_OPCODE_CONTINUATION:
725 fprintf(stdout, "continuation");
726 break;
727 case WEBSOCKET_OPCODE_TEXT:
728 fprintf(stdout, "text");
729 break;
730 case WEBSOCKET_OPCODE_BINARY:
731 fprintf(stdout, "binary");
732 break;
733 case WEBSOCKET_OPCODE_CONNECTION_CLOSE:
734 fprintf(stdout, "close");
735 break;
736 case WEBSOCKET_OPCODE_PING:
737 fprintf(stdout, "ping");
738 break;
739 case WEBSOCKET_OPCODE_PONG:
740 fprintf(stdout, "pong");
741 break;
742 default:
743 fprintf(stdout, "unknown(%1xh)", ((unsigned char)bits) & 0x0F);
744 break;
745 }
746 fprintf(stdout, " data:\r\n");
747 fwrite(data, len, 1, stdout);
748 fprintf(stdout, "\r\n\r\n");
749
750 return 1;
751 }
752
753
754 void
755 WebSocketCloseHandler(const struct mg_connection *conn, void *cbdata)
756 {
757 struct mg_context *ctx = mg_get_context(conn);
758 struct t_ws_client *client = mg_get_user_connection_data(conn);
759 ASSERT(client->conn == conn);
760 ASSERT(client->state >= 1);
761
762 mg_lock_context(ctx);
763 client->state = 0;
764 client->conn = NULL;
765 mg_unlock_context(ctx);
766
767 fprintf(stdout,
768 "Client droped from the set of webserver connections\r\n\r\n");
769 }
770
771
772 void
773 InformWebsockets(struct mg_context *ctx)
774 {
775 static unsigned long cnt = 0;
776 char text[32];
777 int i;
778
779 sprintf(text, "%lu", ++cnt);
780
781 mg_lock_context(ctx);
782 for (i = 0; i < MAX_WS_CLIENTS; i++) {
783 if (ws_clients[i].state == 2) {
784 mg_websocket_write(ws_clients[i].conn,
785 WEBSOCKET_OPCODE_TEXT,
786 text,
787 strlen(text));
788 }
789 }
790 mg_unlock_context(ctx);
791 }
792 #endif
793
794
795 #ifdef USE_SSL_DH
796 #include "openssl/ssl.h"
797 #include "openssl/dh.h"
798 #include "openssl/ec.h"
799 #include "openssl/evp.h"
800 #include "openssl/ecdsa.h"
801
802 DH *
803 get_dh2236()
804 {
805 static unsigned char dh2236_p[] = {
806 0x0E, 0x97, 0x6E, 0x6A, 0x88, 0x84, 0xD2, 0xD7, 0x55, 0x6A, 0x17, 0xB7,
807 0x81, 0x9A, 0x98, 0xBC, 0x7E, 0xD1, 0x6A, 0x44, 0xB1, 0x18, 0xE6, 0x25,
808 0x3A, 0x62, 0x35, 0xF0, 0x41, 0x91, 0xE2, 0x16, 0x43, 0x9D, 0x8F, 0x7D,
809 0x5D, 0xDA, 0x85, 0x47, 0x25, 0xC4, 0xBA, 0x68, 0x0A, 0x87, 0xDC, 0x2C,
810 0x33, 0xF9, 0x75, 0x65, 0x17, 0xCB, 0x8B, 0x80, 0xFE, 0xE0, 0xA8, 0xAF,
811 0xC7, 0x9E, 0x82, 0xBE, 0x6F, 0x1F, 0x00, 0x04, 0xBD, 0x69, 0x50, 0x8D,
812 0x9C, 0x3C, 0x41, 0x69, 0x21, 0x4E, 0x86, 0xC8, 0x2B, 0xCC, 0x07, 0x4D,
813 0xCF, 0xE4, 0xA2, 0x90, 0x8F, 0x66, 0xA9, 0xEF, 0xF7, 0xFC, 0x6F, 0x5F,
814 0x06, 0x22, 0x00, 0xCB, 0xCB, 0xC3, 0x98, 0x3F, 0x06, 0xB9, 0xEC, 0x48,
815 0x3B, 0x70, 0x6E, 0x94, 0xE9, 0x16, 0xE1, 0xB7, 0x63, 0x2E, 0xAB, 0xB2,
816 0xF3, 0x84, 0xB5, 0x3D, 0xD7, 0x74, 0xF1, 0x6A, 0xD1, 0xEF, 0xE8, 0x04,
817 0x18, 0x76, 0xD2, 0xD6, 0xB0, 0xB7, 0x71, 0xB6, 0x12, 0x8F, 0xD1, 0x33,
818 0xAB, 0x49, 0xAB, 0x09, 0x97, 0x35, 0x9D, 0x4B, 0xBB, 0x54, 0x22, 0x6E,
819 0x1A, 0x33, 0x18, 0x02, 0x8A, 0xF4, 0x7C, 0x0A, 0xCE, 0x89, 0x75, 0x2D,
820 0x10, 0x68, 0x25, 0xA9, 0x6E, 0xCD, 0x97, 0x49, 0xED, 0xAE, 0xE6, 0xA7,
821 0xB0, 0x07, 0x26, 0x25, 0x60, 0x15, 0x2B, 0x65, 0x88, 0x17, 0xF2, 0x5D,
822 0x2C, 0xF6, 0x2A, 0x7A, 0x8C, 0xAD, 0xB6, 0x0A, 0xA2, 0x57, 0xB0, 0xC1,
823 0x0E, 0x5C, 0xA8, 0xA1, 0x96, 0x58, 0x9A, 0x2B, 0xD4, 0xC0, 0x8A, 0xCF,
824 0x91, 0x25, 0x94, 0xB4, 0x14, 0xA7, 0xE4, 0xE2, 0x1B, 0x64, 0x5F, 0xD2,
825 0xCA, 0x70, 0x46, 0xD0, 0x2C, 0x95, 0x6B, 0x9A, 0xFB, 0x83, 0xF9, 0x76,
826 0xE6, 0xD4, 0xA4, 0xA1, 0x2B, 0x2F, 0xF5, 0x1D, 0xE4, 0x06, 0xAF, 0x7D,
827 0x22, 0xF3, 0x04, 0x30, 0x2E, 0x4C, 0x64, 0x12, 0x5B, 0xB0, 0x55, 0x3E,
828 0xC0, 0x5E, 0x56, 0xCB, 0x99, 0xBC, 0xA8, 0xD9, 0x23, 0xF5, 0x57, 0x40,
829 0xF0, 0x52, 0x85, 0x9B,
830 };
831 static unsigned char dh2236_g[] = {
832 0x02,
833 };
834 DH *dh;
835
836 if ((dh = DH_new()) == NULL)
837 return (NULL);
838 dh->p = BN_bin2bn(dh2236_p, sizeof(dh2236_p), NULL);
839 dh->g = BN_bin2bn(dh2236_g, sizeof(dh2236_g), NULL);
840 if ((dh->p == NULL) || (dh->g == NULL)) {
841 DH_free(dh);
842 return (NULL);
843 }
844 return (dh);
845 }
846 #endif
847
848
849 #ifndef NO_SSL
850 int
851 init_ssl(void *ssl_context, void *user_data)
852 {
853 /* Add application specific SSL initialization */
854 struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_context;
855
856 #ifdef USE_SSL_DH
857 /* example from https://github.com/civetweb/civetweb/issues/347 */
858 DH *dh = get_dh2236();
859 if (!dh)
860 return -1;
861 if (1 != SSL_CTX_set_tmp_dh(ctx, dh))
862 return -1;
863 DH_free(dh);
864
865 EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
866 if (!ecdh)
867 return -1;
868 if (1 != SSL_CTX_set_tmp_ecdh(ctx, ecdh))
869 return -1;
870 EC_KEY_free(ecdh);
871
872 printf("ECDH ciphers initialized\n");
873 #endif
874 return 0;
875 }
876 #endif
877
878
879 int
880 log_message(const struct mg_connection *conn, const char *message)
881 {
882 puts(message);
883 return 1;
884 }
885
886
887 int
888 main(int argc, char *argv[])
889 {
890 const char *options[] = {
891 "document_root",
892 DOCUMENT_ROOT,
893 "listening_ports",
894 PORT,
895 "request_timeout_ms",
896 "10000",
897 "error_log_file",
898 "error.log",
899 #ifdef USE_WEBSOCKET
900 "websocket_timeout_ms",
901 "3600000",
902 #endif
903 #ifndef NO_SSL
904 "ssl_certificate",
905 "../../resources/cert/server.pem",
906 "ssl_protocol_version",
907 "3",
908 "ssl_cipher_list",
909 #ifdef USE_SSL_DH
910 "ECDHE-RSA-AES256-GCM-SHA384:DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256",
911 #else
912 "DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256",
913 #endif
914 #endif
915 "enable_auth_domain_check",
916 "no",
917 0};
918 struct mg_callbacks callbacks;
919 struct mg_context *ctx;
920 struct mg_server_ports ports[32];
921 int port_cnt, n;
922 int err = 0;
923
924 /* Check if libcivetweb has been built with all required features. */
925 #ifdef USE_IPV6
926 if (!mg_check_feature(8)) {
927 fprintf(stderr,
928 "Error: Embedded example built with IPv6 support, "
929 "but civetweb library build without.\n");
930 err = 1;
931 }
932 #endif
933 #ifdef USE_WEBSOCKET
934 if (!mg_check_feature(16)) {
935 fprintf(stderr,
936 "Error: Embedded example built with websocket support, "
937 "but civetweb library build without.\n");
938 err = 1;
939 }
940 #endif
941 #ifndef NO_SSL
942 if (!mg_check_feature(2)) {
943 fprintf(stderr,
944 "Error: Embedded example built with SSL support, "
945 "but civetweb library build without.\n");
946 err = 1;
947 }
948 #endif
949 if (err) {
950 fprintf(stderr, "Cannot start CivetWeb - inconsistent build.\n");
951 return EXIT_FAILURE;
952 }
953
954 /* Start CivetWeb web server */
955 memset(&callbacks, 0, sizeof(callbacks));
956 #ifndef NO_SSL
957 callbacks.init_ssl = init_ssl;
958 #endif
959 callbacks.log_message = log_message;
960 ctx = mg_start(&callbacks, 0, options);
961
962 /* Check return value: */
963 if (ctx == NULL) {
964 fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n");
965 return EXIT_FAILURE;
966 }
967
968 /* Add handler EXAMPLE_URI, to explain the example */
969 mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);
970 mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);
971
972 /* Add handler for /A* and special handler for /A/B */
973 mg_set_request_handler(ctx, "/A", AHandler, 0);
974 mg_set_request_handler(ctx, "/A/B", ABHandler, 0);
975
976 /* Add handler for /B, /B/A, /B/B but not for /B* */
977 mg_set_request_handler(ctx, "/B$", BXHandler, (void *)0);
978 mg_set_request_handler(ctx, "/B/A$", BXHandler, (void *)1);
979 mg_set_request_handler(ctx, "/B/B$", BXHandler, (void *)2);
980
981 /* Add handler for all files with .foo extention */
982 mg_set_request_handler(ctx, "**.foo$", FooHandler, 0);
983
984 /* Add handler for /close extention */
985 mg_set_request_handler(ctx, "/close", CloseHandler, 0);
986
987 /* Add handler for /form (serve a file outside the document root) */
988 mg_set_request_handler(ctx,
989 "/form",
990 FileHandler,
991 (void *)"../../test/form.html");
992
993 /* Add handler for form data */
994 mg_set_request_handler(ctx,
995 "/handle_form.embedded_c.example.callback",
996 FormHandler,
997 (void *)0);
998
999 /* Add a file upload handler for parsing files on the fly */
1000 mg_set_request_handler(ctx,
1001 "/on_the_fly_form",
1002 FileUploadForm,
1003 (void *)"/on_the_fly_form.md5.callback");
1004 mg_set_request_handler(ctx,
1005 "/on_the_fly_form.md5.callback",
1006 CheckSumHandler,
1007 (void *)0);
1008
1009 /* Add handler for /cookie example */
1010 mg_set_request_handler(ctx, "/cookie", CookieHandler, 0);
1011
1012 /* Add handler for /postresponse example */
1013 mg_set_request_handler(ctx, "/postresponse", PostResponser, 0);
1014
1015 /* Add HTTP site to open a websocket connection */
1016 mg_set_request_handler(ctx, "/websocket", WebSocketStartHandler, 0);
1017
1018 /* Add HTTP site with auth */
1019 mg_set_request_handler(ctx, "/auth", AuthStartHandler, 0);
1020
1021
1022 #ifdef USE_WEBSOCKET
1023 /* WS site for the websocket connection */
1024 mg_set_websocket_handler(ctx,
1025 "/websocket",
1026 WebSocketConnectHandler,
1027 WebSocketReadyHandler,
1028 WebsocketDataHandler,
1029 WebSocketCloseHandler,
1030 0);
1031 #endif
1032
1033 /* List all listening ports */
1034 memset(ports, 0, sizeof(ports));
1035 port_cnt = mg_get_server_ports(ctx, 32, ports);
1036 printf("\n%i listening ports:\n\n", port_cnt);
1037
1038 for (n = 0; n < port_cnt && n < 32; n++) {
1039 const char *proto = ports[n].is_ssl ? "https" : "http";
1040 const char *host;
1041
1042 if ((ports[n].protocol & 1) == 1) {
1043 /* IPv4 */
1044 host = "127.0.0.1";
1045 printf("Browse files at %s://%s:%i/\n", proto, host, ports[n].port);
1046 printf("Run example at %s://%s:%i%s\n",
1047 proto,
1048 host,
1049 ports[n].port,
1050 EXAMPLE_URI);
1051 printf(
1052 "Exit at %s://%s:%i%s\n", proto, host, ports[n].port, EXIT_URI);
1053 printf("\n");
1054 }
1055
1056 if ((ports[n].protocol & 2) == 2) {
1057 /* IPv6 */
1058 host = "[::1]";
1059 printf("Browse files at %s://%s:%i/\n", proto, host, ports[n].port);
1060 printf("Run example at %s://%s:%i%s\n",
1061 proto,
1062 host,
1063 ports[n].port,
1064 EXAMPLE_URI);
1065 printf(
1066 "Exit at %s://%s:%i%s\n", proto, host, ports[n].port, EXIT_URI);
1067 printf("\n");
1068 }
1069 }
1070
1071 /* Wait until the server should be closed */
1072 while (!exitNow) {
1073 #ifdef _WIN32
1074 Sleep(1000);
1075 #else
1076 sleep(1);
1077 #endif
1078 #ifdef USE_WEBSOCKET
1079 InformWebsockets(ctx);
1080 #endif
1081 }
1082
1083 /* Stop the server */
1084 mg_stop(ctx);
1085 printf("Server stopped.\n");
1086 printf("Bye!\n");
1087
1088 return EXIT_SUCCESS;
1089 }