embedded_c.c 35 KB

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