embedded_c.c 30 KB

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