embedded_c.c 35 KB

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