embedded_c.c 35 KB

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