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