123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281 |
- /*
- * Copyright (c) 2013-2021 the CivetWeb developers
- * Copyright (c) 2013 No Face Press, LLC
- * License http://opensource.org/licenses/mit-license.php MIT License
- */
- /* Note: This example omits some error checking and input validation for a
- * better clarity/readability of the code. Example codes undergo less quality
- * management than the main source files of this project. */
- #ifdef NO_SSL
- #define TEST_WITHOUT_SSL
- #undef USE_SSL_DH
- #endif
- /* Simple example program on how to use CivetWeb embedded into a C program. */
- #ifdef _WIN32
- #include <windows.h>
- #else
- #include <unistd.h>
- #endif
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include "civetweb.h"
- #define DOCUMENT_ROOT "."
- #ifdef TEST_WITHOUT_SSL
- #ifdef USE_IPV6
- #define PORT "[::]:8888,8884"
- #else
- #define PORT "8888,8884"
- #endif
- #else
- #ifdef USE_IPV6
- #define PORT "[::]:8888r,[::]:8843s,8884"
- #else
- #define PORT "8888r,8843s,8884"
- #endif
- #endif
- #define EXAMPLE_URI "/example"
- #define EXIT_URI "/exit"
- volatile int exitNow = 0;
- int
- ExampleHandler(struct mg_connection *conn, void *cbdata)
- {
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn, "<h2>This is an example text from a C handler</h2>");
- mg_printf(
- conn,
- "<p>To see a page from the A handler <a href=\"A\">click A</a></p>");
- mg_printf(conn,
- "<p>To see a page from the A handler <a href=\"A/A\">click "
- "A/A</a></p>");
- mg_printf(conn,
- "<p>To see a page from the A/B handler <a "
- "href=\"A/B\">click A/B</a></p>");
- mg_printf(conn,
- "<p>To see a page from the B handler (0) <a "
- "href=\"B\">click B</a></p>");
- mg_printf(conn,
- "<p>To see a page from the B handler (1) <a "
- "href=\"B/A\">click B/A</a></p>");
- mg_printf(conn,
- "<p>To see a page from the B handler (2) <a "
- "href=\"B/B\">click B/B</a></p>");
- mg_printf(conn,
- "<p>To see a page from the *.foo handler <a "
- "href=\"xy.foo\">click xy.foo</a></p>");
- mg_printf(conn,
- "<p>To see a page from the close handler <a "
- "href=\"close\">click close</a></p>");
- mg_printf(conn,
- "<p>To see a page from the FileHandler handler <a "
- "href=\"form\">click form</a> (the starting point of the "
- "<b>form</b> test)</p>");
- mg_printf(conn,
- "<p>To see a page from the CookieHandler handler <a "
- "href=\"cookie\">click cookie</a></p>");
- mg_printf(conn,
- "<p>To see a page from the PostResponser handler <a "
- "href=\"postresponse\">click post response</a></p>");
- mg_printf(conn,
- "<p>To see an example for parsing files on the fly <a "
- "href=\"on_the_fly_form\">click form</a> (form for "
- "uploading files)</p>");
- #ifdef USE_WEBSOCKET
- mg_printf(conn,
- "<p>To test the websocket handler <a href=\"/websocket\">click "
- "websocket</a></p>");
- #endif
- mg_printf(conn,
- "<p>To test the authentication handler <a href=\"/auth\">click "
- "auth</a></p>");
- mg_printf(conn, "<p>To exit <a href=\"%s\">click exit</a></p>", EXIT_URI);
- mg_printf(conn, "</body></html>\n");
- return 1;
- }
- int
- ExitHandler(struct mg_connection *conn, void *cbdata)
- {
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: "
- "text/plain\r\nConnection: close\r\n\r\n");
- mg_printf(conn, "Server will shut down.\n");
- mg_printf(conn, "Bye!\n");
- exitNow = 1;
- return 1;
- }
- int
- AHandler(struct mg_connection *conn, void *cbdata)
- {
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn, "<h2>This is the A handler.</h2>");
- mg_printf(conn, "</body></html>\n");
- return 1;
- }
- int
- ABHandler(struct mg_connection *conn, void *cbdata)
- {
- const struct mg_request_info *ri = mg_get_request_info(conn);
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn, "<h2>This is the AB handler.</h2>");
- mg_printf(conn, "<ul>\n");
- mg_printf(conn, "<li>request_method = %s</li>\n", ri->request_method);
- mg_printf(conn, "<li>request_uri = %s</li>\n", ri->request_uri);
- mg_printf(conn, "<li>local_uri = %s</li>\n", ri->local_uri);
- mg_printf(conn, "<li>http_version = %s</li>\n", ri->http_version);
- mg_printf(conn, "<li>query_string = %s</li>\n", ri->query_string);
- mg_printf(conn, "<li>remote_user = %s</li>\n", ri->remote_user);
- mg_printf(conn, "<li>remote_addr = %s</li>\n", ri->remote_addr);
- mg_printf(conn, "<li>remote_port = %u</li>\n", ri->remote_port);
- mg_printf(conn, "<li>is_ssl = %i</li>\n", ri->is_ssl);
- mg_printf(conn, "<li>num_headers = %i</li>\n", ri->num_headers);
- if (ri->num_headers > 0) {
- int i;
- mg_printf(conn, "<ol>\n");
- for (i = 0; i < ri->num_headers; i++) {
- mg_printf(conn,
- "<li>%s = %s</li>\n",
- ri->http_headers[i].name,
- ri->http_headers[i].value);
- }
- mg_printf(conn, "</ol>\n");
- }
- mg_printf(conn, "</ul>\n");
- mg_printf(conn, "</body></html>\n");
- return 1;
- }
- int
- BXHandler(struct mg_connection *conn, void *cbdata)
- {
- /* Handler may access the request info using mg_get_request_info */
- const struct mg_request_info *req_info = mg_get_request_info(conn);
- const char *text = (const char *)cbdata;
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn, "<h2>This is the BX handler with argument %s.</h2>", text);
- mg_printf(conn, "<p>The actual uri is %s</p>", req_info->local_uri);
- mg_printf(conn, "</body></html>\n");
- return 1;
- }
- int
- FooHandler(struct mg_connection *conn, void *cbdata)
- {
- /* Handler may access the request info using mg_get_request_info */
- const struct mg_request_info *req_info = mg_get_request_info(conn);
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn, "<h2>This is the Foo handler!!!</h2>");
- mg_printf(conn,
- "<p>The request was:<br><pre>%s %s HTTP/%s</pre></p>",
- req_info->request_method,
- req_info->local_uri,
- req_info->http_version);
- mg_printf(conn, "</body></html>\n");
- return 1;
- }
- int
- CloseHandler(struct mg_connection *conn, void *cbdata)
- {
- /* Handler may access the request info using mg_get_request_info */
- const struct mg_request_info *req_info = mg_get_request_info(conn);
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn,
- "<h2>This handler will close the connection in a second</h2>");
- #ifdef _WIN32
- Sleep(1000);
- #else
- sleep(1);
- #endif
- mg_printf(conn, "bye");
- printf("CloseHandler: close connection\n");
- mg_close_connection(conn);
- printf("CloseHandler: wait 10 sec\n");
- #ifdef _WIN32
- Sleep(10000);
- #else
- sleep(10);
- #endif
- printf("CloseHandler: return from function\n");
- return 1;
- }
- #if !defined(NO_FILESYSTEMS)
- int
- FileHandler(struct mg_connection *conn, void *cbdata)
- {
- /* In this handler, we ignore the req_info and send the file "fileName". */
- const char *fileName = (const char *)cbdata;
- mg_send_file(conn, fileName);
- return 1;
- }
- #endif /* NO_FILESYSTEMS */
- #define MD5_STATIC static
- #include "../src/md5.inl"
- /* Stringify binary data. Output buffer must be twice as big as input,
- * because each byte takes 2 bytes in string representation */
- static void
- bin2str(char *to, const unsigned char *p, size_t len)
- {
- static const char *hex = "0123456789abcdef";
- for (; len--; p++) {
- *to++ = hex[p[0] >> 4];
- *to++ = hex[p[0] & 0x0f];
- }
- *to = '\0';
- }
- int
- field_found(const char *key,
- const char *filename,
- char *path,
- size_t pathlen,
- void *user_data)
- {
- #ifdef _WIN32
- char temppath[MAX_PATH + 2];
- DWORD temppathlen;
- #endif
- struct mg_connection *conn = (struct mg_connection *)user_data;
- mg_printf(conn, "\r\n\r\n%s:\r\n", key);
- if (filename && *filename) {
- /* According to
- * https://datatracker.ietf.org/doc/html/rfc7578#section-4.2: Do not use
- * path information present in the filename. Drop all "/" (and "\" for
- * Windows).
- */
- const char *fname = filename;
- const char *sep = strrchr(fname, '/');
- if (sep) {
- fname = sep + 1;
- }
- #ifdef _WIN32
- sep = strrchr(fname, '\\');
- if (sep) {
- fname = sep + 1;
- }
- /* For Windows: Find the directory for temporary files */
- temppathlen = GetTempPathA(sizeof(temppath), temppath);
- if (temppathlen > 0) {
- _snprintf(path, pathlen, "%s\\%s", temppath, fname);
- } else {
- _snprintf(path, pathlen, "C:\\tmp\\%s", fname);
- }
- #else
- snprintf(path, pathlen, "/tmp/%s", fname);
- #endif
- /* According to https://datatracker.ietf.org/doc/html/rfc7578#section-7:
- * Do not overwrite existing files.
- */
- {
- FILE *ftest = fopen(path, "r");
- if (!ftest) {
- return MG_FORM_FIELD_STORAGE_STORE;
- }
- fclose(ftest);
- /* This is just simple demo code. More sophisticated code could add
- * numbers to the file name to make filenames unique. However, most
- * likely file upload will not end up in the temporary path, but in
- * a user directory - multiple directories for multiple users that
- * are logged into the web service. In this case, users might want
- * to overwrite their own code. You need to adapt this example to
- * your needs.
- */
- }
- return MG_FORM_FIELD_STORAGE_SKIP;
- }
- return MG_FORM_FIELD_STORAGE_GET;
- }
- int
- field_get(const char *key, const char *value, size_t valuelen, void *user_data)
- {
- struct mg_connection *conn = (struct mg_connection *)user_data;
- if ((key != NULL) && (key[0] == '\0')) {
- /* Incorrect form data detected */
- return MG_FORM_FIELD_HANDLE_ABORT;
- }
- if ((valuelen > 0) && (value == NULL)) {
- /* Unreachable, since this call will not be generated by civetweb. */
- return MG_FORM_FIELD_HANDLE_ABORT;
- }
- if (key) {
- mg_printf(conn, "key = %s\n", key);
- }
- mg_printf(conn, "valuelen = %lu\n", valuelen);
- if (valuelen > 0) {
- /* mg_write(conn, value, valuelen); */
- md5_byte_t hash[16];
- md5_state_t ctx;
- char outputbuf[33];
- md5_init(&ctx);
- md5_append(&ctx, (const md5_byte_t *)value, valuelen);
- md5_finish(&ctx, hash);
- bin2str(outputbuf, hash, sizeof(hash));
- mg_printf(conn, "value md5 hash = %s\n", outputbuf);
- }
- #if 0 /* for debugging */
- if (!strcmp(key, "File")) {
- FILE *f = fopen("test.txt", "wb");
- if (f) {
- fwrite(value, 1, valuelen, f);
- fclose(f);
- }
- }
- #endif
- return 0;
- }
- int
- field_stored(const char *path, long long file_size, void *user_data)
- {
- struct mg_connection *conn = (struct mg_connection *)user_data;
- mg_printf(conn,
- "stored as %s (%lu bytes)\r\n\r\n",
- path,
- (unsigned long)file_size);
- return 0;
- }
- int
- FormHandler(struct mg_connection *conn, void *cbdata)
- {
- /* Handler may access the request info using mg_get_request_info */
- const struct mg_request_info *req_info = mg_get_request_info(conn);
- int ret;
- struct mg_form_data_handler fdh = {field_found, field_get, field_stored, 0};
- /* It would be possible to check the request info here before calling
- * mg_handle_form_request. */
- (void)req_info;
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: "
- "text/plain\r\nConnection: close\r\n\r\n");
- fdh.user_data = (void *)conn;
- /* Call the form handler */
- mg_printf(conn, "Form data:");
- ret = mg_handle_form_request(conn, &fdh);
- mg_printf(conn, "\r\n%i fields found", ret);
- return 1;
- }
- int
- FileUploadForm(struct mg_connection *conn, void *cbdata)
- {
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<!DOCTYPE html>\n");
- mg_printf(conn, "<html>\n<head>\n");
- mg_printf(conn, "<meta charset=\"UTF-8\">\n");
- mg_printf(conn, "<title>File upload</title>\n");
- mg_printf(conn, "</head>\n<body>\n");
- mg_printf(conn,
- "<form action=\"%s\" method=\"POST\" "
- "enctype=\"multipart/form-data\">\n",
- (const char *)cbdata);
- mg_printf(conn, "<input type=\"file\" name=\"filesin\" multiple>\n");
- mg_printf(conn, "<input type=\"submit\" value=\"Submit\">\n");
- mg_printf(conn, "</form>\n</body>\n</html>\n");
- return 1;
- }
- struct tfile_checksum {
- char name[128];
- unsigned long long length;
- md5_state_t chksum;
- };
- #define MAX_FILES (10)
- struct tfiles_checksums {
- int index;
- struct tfile_checksum file[MAX_FILES];
- };
- int
- field_disp_read_on_the_fly(const char *key,
- const char *filename,
- char *path,
- size_t pathlen,
- void *user_data)
- {
- struct tfiles_checksums *context = (struct tfiles_checksums *)user_data;
- (void)key;
- (void)path;
- (void)pathlen;
- if (context->index < MAX_FILES) {
- context->index++;
- strncpy(context->file[context->index - 1].name, filename, 128);
- context->file[context->index - 1].name[127] = 0;
- context->file[context->index - 1].length = 0;
- md5_init(&(context->file[context->index - 1].chksum));
- return MG_FORM_FIELD_STORAGE_GET;
- }
- return MG_FORM_FIELD_STORAGE_ABORT;
- }
- int
- field_get_checksum(const char *key,
- const char *value,
- size_t valuelen,
- void *user_data)
- {
- struct tfiles_checksums *context = (struct tfiles_checksums *)user_data;
- (void)key;
- context->file[context->index - 1].length += valuelen;
- md5_append(&(context->file[context->index - 1].chksum),
- (const md5_byte_t *)value,
- valuelen);
- return 0;
- }
- int
- CheckSumHandler(struct mg_connection *conn, void *cbdata)
- {
- /* Handler may access the request info using mg_get_request_info */
- const struct mg_request_info *req_info = mg_get_request_info(conn);
- int i, j, ret;
- struct tfiles_checksums chksums;
- md5_byte_t digest[16];
- struct mg_form_data_handler fdh = {field_disp_read_on_the_fly,
- field_get_checksum,
- 0,
- (void *)&chksums};
- /* It would be possible to check the request info here before calling
- * mg_handle_form_request. */
- (void)req_info;
- memset(&chksums, 0, sizeof(chksums));
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/plain\r\n"
- "Connection: close\r\n\r\n");
- /* Call the form handler */
- mg_printf(conn, "File checksums:");
- ret = mg_handle_form_request(conn, &fdh);
- for (i = 0; i < chksums.index; i++) {
- md5_finish(&(chksums.file[i].chksum), digest);
- /* Visual Studio 2010+ support llu */
- mg_printf(conn,
- "\r\n%s %llu ",
- chksums.file[i].name,
- chksums.file[i].length);
- for (j = 0; j < 16; j++) {
- mg_printf(conn, "%02x", (unsigned int)digest[j]);
- }
- }
- mg_printf(conn, "\r\n%i files\r\n", ret);
- return 1;
- }
- int
- CookieHandler(struct mg_connection *conn, void *cbdata)
- {
- /* Handler may access the request info using mg_get_request_info */
- const struct mg_request_info *req_info = mg_get_request_info(conn);
- const char *cookie = mg_get_header(conn, "Cookie");
- char first_str[64], count_str[64];
- int count;
- (void)mg_get_cookie(cookie, "first", first_str, sizeof(first_str));
- (void)mg_get_cookie(cookie, "count", count_str, sizeof(count_str));
- mg_printf(conn, "HTTP/1.1 200 OK\r\nConnection: close\r\n");
- if (first_str[0] == 0) {
- time_t t = time(0);
- struct tm *ptm = localtime(&t);
- mg_printf(conn,
- "Set-Cookie: first=%04i-%02i-%02iT%02i:%02i:%02i\r\n",
- ptm->tm_year + 1900,
- ptm->tm_mon + 1,
- ptm->tm_mday,
- ptm->tm_hour,
- ptm->tm_min,
- ptm->tm_sec);
- }
- count = (count_str[0] == 0) ? 0 : atoi(count_str);
- mg_printf(conn, "Set-Cookie: count=%i\r\n", count + 1);
- mg_printf(conn, "Content-Type: text/html\r\n\r\n");
- mg_printf(conn, "<html><body>");
- mg_printf(conn, "<h2>This is the CookieHandler.</h2>");
- mg_printf(conn, "<p>The actual uri is %s</p>", req_info->local_uri);
- if (first_str[0] == 0) {
- mg_printf(conn, "<p>This is the first time, you opened this page</p>");
- } else {
- mg_printf(conn, "<p>You opened this page %i times before.</p>", count);
- mg_printf(conn, "<p>You first opened this page on %s.</p>", first_str);
- }
- mg_printf(conn, "</body></html>\n");
- return 1;
- }
- int
- PostResponser(struct mg_connection *conn, void *cbdata)
- {
- long long r_total = 0;
- int r, s;
- char buf[2048];
- const struct mg_request_info *ri = mg_get_request_info(conn);
- if (0 != strcmp(ri->request_method, "POST")) {
- /* Not a POST request */
- int ret = mg_get_request_link(conn, buf, sizeof(buf));
- mg_printf(conn,
- "HTTP/1.1 405 Method Not Allowed\r\nConnection: close\r\n");
- mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
- mg_printf(conn,
- "%s method not allowed in the POST handler\n",
- ri->request_method);
- if (ret >= 0) {
- mg_printf(conn,
- "use a web tool to send a POST request to %s\n",
- buf);
- }
- return 1;
- }
- if (ri->content_length >= 0) {
- /* We know the content length in advance */
- } else {
- /* We must read until we find the end (chunked encoding
- * or connection close), indicated my mg_read returning 0 */
- }
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nConnection: "
- "close\r\nTransfer-Encoding: chunked\r\n");
- mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
- r = mg_read(conn, buf, sizeof(buf));
- while (r > 0) {
- r_total += r;
- s = mg_send_chunk(conn, buf, r);
- if (s <= 0) {
- /* Send error */
- break;
- }
- r = mg_read(conn, buf, sizeof(buf));
- }
- mg_printf(conn, "0\r\n");
- return 1;
- }
- #if !defined(NO_FILESYSTEMS)
- int
- AuthStartHandler(struct mg_connection *conn, void *cbdata)
- {
- static unsigned long long firstload = 0;
- const char *passfile = "password_example_file.txt";
- const char *realm = "password_example";
- const char *user = "user";
- char passwd[64];
- if (firstload == 0) {
- /* Set a random password (4 digit number - bad idea from a security
- * point of view, but this is an API demo, not a security tutorial),
- * and store it in some directory within the document root (extremely
- * bad idea, but this is still not a security tutorial).
- * The reason we create a new password every time the server starts
- * is just for demonstration - we don't want the browser to store the
- * password, so when we repeat the test we start with a new password.
- */
- firstload = (unsigned long long)time(NULL);
- sprintf(passwd, "%04u", (unsigned int)(firstload % 10000));
- mg_modify_passwords_file(passfile, realm, user, passwd);
- /* Just tell the user the new password generated for this test. */
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<!DOCTYPE html>\n");
- mg_printf(conn, "<html>\n<head>\n");
- mg_printf(conn, "<meta charset=\"UTF-8\">\n");
- mg_printf(conn, "<title>Auth handlerexample</title>\n");
- mg_printf(conn, "</head>\n");
- mg_printf(conn, "<body>\n");
- mg_printf(conn,
- "<p>The first time you visit this page, it's free!</p>\n");
- mg_printf(conn,
- "<p>Next time, use username \"%s\" and password \"%s\"</p>\n",
- user,
- passwd);
- mg_printf(conn, "</body>\n</html>\n");
- return 1;
- }
- if (mg_check_digest_access_authentication(conn, realm, passfile) <= 0) {
- /* No valid authorization */
- mg_send_digest_access_authentication_request(conn, realm);
- return 1;
- }
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<!DOCTYPE html>\n");
- mg_printf(conn, "<html>\n<head>\n");
- mg_printf(conn, "<meta charset=\"UTF-8\">\n");
- mg_printf(conn, "<title>Auth handlerexample</title>\n");
- mg_printf(conn, "</head>\n");
- mg_printf(conn, "<body>\n");
- mg_printf(conn, "<p>This is the password protected contents</p>\n");
- mg_printf(conn, "</body>\n</html>\n");
- return 1;
- }
- #endif /* NO_FILESYSTEMS */
- int
- WebSocketStartHandler(struct mg_connection *conn, void *cbdata)
- {
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
- "close\r\n\r\n");
- mg_printf(conn, "<!DOCTYPE html>\n");
- mg_printf(conn, "<html>\n<head>\n");
- mg_printf(conn, "<meta charset=\"UTF-8\">\n");
- mg_printf(conn, "<title>Embedded websocket example</title>\n");
- #ifdef USE_WEBSOCKET
- /* mg_printf(conn, "<script type=\"text/javascript\"><![CDATA[\n"); ...
- * xhtml style */
- mg_printf(conn, "<script>\n");
- mg_printf(
- conn,
- "function load() {\n"
- " var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n"
- " connection = new WebSocket(wsproto + '//' + window.location.host + "
- "'/websocket');\n"
- " websock_text_field = "
- "document.getElementById('websock_text_field');\n"
- " connection.onmessage = function (e) {\n"
- " websock_text_field.innerHTML=e.data;\n"
- " }\n"
- " connection.onerror = function (error) {\n"
- " alert('WebSocket error');\n"
- " connection.close();\n"
- " }\n"
- "}\n");
- /* mg_printf(conn, "]]></script>\n"); ... xhtml style */
- mg_printf(conn, "</script>\n");
- mg_printf(conn, "</head>\n<body onload=\"load()\">\n");
- mg_printf(
- conn,
- "<div id='websock_text_field'>No websocket connection yet</div>\n");
- #else
- mg_printf(conn, "</head>\n<body>\n");
- mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n");
- #endif
- mg_printf(conn, "</body>\n</html>\n");
- return 1;
- }
- #ifdef USE_WEBSOCKET
- /* MAX_WS_CLIENTS defines how many clients can connect to a websocket at the
- * same time. The value 5 is very small and used here only for demonstration;
- * it can be easily tested to connect more than MAX_WS_CLIENTS clients.
- * A real server should use a much higher number, or better use a dynamic list
- * of currently connected websocket clients. */
- #define MAX_WS_CLIENTS (5)
- struct t_ws_client {
- /* Handle to the connection, used for mg_read/mg_write */
- struct mg_connection *conn;
- /*
- WebSocketConnectHandler sets state to 1 ("connected")
- the connect handler can accept or reject a connection, but it cannot
- send or receive any data at this state
- WebSocketReadyHandler sets state to 2 ("ready")
- reading and writing is possible now
- WebSocketCloseHandler sets state to 0
- the websocket is about to be closed, reading and writing is no longer
- possible this callback can be used to cleanup allocated resources
- InformWebsockets is called cyclic every second, and sends some data
- (a counter value) to all websockets in state 2
- */
- int state;
- } static ws_clients[MAX_WS_CLIENTS];
- #define ASSERT(x) \
- { \
- if (!(x)) { \
- fprintf(stderr, \
- "Assertion failed in line %u\n", \
- (unsigned)__LINE__); \
- } \
- }
- int
- WebSocketConnectHandler(const struct mg_connection *conn, void *cbdata)
- {
- struct mg_context *ctx = mg_get_context(conn);
- int reject = 1;
- int i;
- mg_lock_context(ctx);
- for (i = 0; i < MAX_WS_CLIENTS; i++) {
- if (ws_clients[i].conn == NULL) {
- ws_clients[i].conn = (struct mg_connection *)conn;
- ws_clients[i].state = 1;
- mg_set_user_connection_data(ws_clients[i].conn,
- (void *)(ws_clients + i));
- reject = 0;
- break;
- }
- }
- mg_unlock_context(ctx);
- fprintf(stdout,
- "Websocket client %s\r\n\r\n",
- (reject ? "rejected" : "accepted"));
- return reject;
- }
- void
- WebSocketReadyHandler(struct mg_connection *conn, void *cbdata)
- {
- const char *text = "Hello from the websocket ready handler";
- struct t_ws_client *client = mg_get_user_connection_data(conn);
- mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, text, strlen(text));
- fprintf(stdout, "Greeting message sent to websocket client\r\n\r\n");
- ASSERT(client->conn == conn);
- ASSERT(client->state == 1);
- client->state = 2;
- }
- int
- WebsocketDataHandler(struct mg_connection *conn,
- int bits,
- char *data,
- size_t len,
- void *cbdata)
- {
- struct t_ws_client *client = mg_get_user_connection_data(conn);
- ASSERT(client->conn == conn);
- ASSERT(client->state >= 1);
- fprintf(stdout, "Websocket got %lu bytes of ", (unsigned long)len);
- switch (((unsigned char)bits) & 0x0F) {
- case MG_WEBSOCKET_OPCODE_CONTINUATION:
- fprintf(stdout, "continuation");
- break;
- case MG_WEBSOCKET_OPCODE_TEXT:
- fprintf(stdout, "text");
- break;
- case MG_WEBSOCKET_OPCODE_BINARY:
- fprintf(stdout, "binary");
- break;
- case MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE:
- fprintf(stdout, "close");
- break;
- case MG_WEBSOCKET_OPCODE_PING:
- fprintf(stdout, "ping");
- break;
- case MG_WEBSOCKET_OPCODE_PONG:
- fprintf(stdout, "pong");
- break;
- default:
- fprintf(stdout, "unknown(%1xh)", ((unsigned char)bits) & 0x0F);
- break;
- }
- fprintf(stdout, " data:\r\n");
- fwrite(data, len, 1, stdout);
- fprintf(stdout, "\r\n\r\n");
- return 1;
- }
- void
- WebSocketCloseHandler(const struct mg_connection *conn, void *cbdata)
- {
- struct mg_context *ctx = mg_get_context(conn);
- struct t_ws_client *client = mg_get_user_connection_data(conn);
- ASSERT(client->conn == conn);
- ASSERT(client->state >= 1);
- mg_lock_context(ctx);
- while (client->state == 3) {
- /* "inform" state, wait a while */
- mg_unlock_context(ctx);
- #ifdef _WIN32
- Sleep(1);
- #else
- usleep(1000);
- #endif
- mg_lock_context(ctx);
- }
- client->state = 0;
- client->conn = NULL;
- mg_unlock_context(ctx);
- fprintf(stdout,
- "Client dropped from the set of webserver connections\r\n\r\n");
- }
- void
- InformWebsockets(struct mg_context *ctx)
- {
- static unsigned long cnt = 0;
- char text[32];
- size_t textlen;
- int i;
- sprintf(text, "%lu", ++cnt);
- textlen = strlen(text);
- for (i = 0; i < MAX_WS_CLIENTS; i++) {
- int inform = 0;
- mg_lock_context(ctx);
- if (ws_clients[i].state == 2) {
- /* move to "inform" state */
- ws_clients[i].state = 3;
- inform = 1;
- }
- mg_unlock_context(ctx);
- if (inform) {
- mg_websocket_write(ws_clients[i].conn,
- MG_WEBSOCKET_OPCODE_TEXT,
- text,
- textlen);
- mg_lock_context(ctx);
- ws_clients[i].state = 2;
- mg_unlock_context(ctx);
- }
- }
- }
- #endif
- #ifdef USE_SSL_DH
- #include "openssl/dh.h"
- #include "openssl/ec.h"
- #include "openssl/ecdsa.h"
- #include "openssl/evp.h"
- #include "openssl/ssl.h"
- DH *
- get_dh2236()
- {
- static unsigned char dh2236_p[] = {
- 0x0E, 0x97, 0x6E, 0x6A, 0x88, 0x84, 0xD2, 0xD7, 0x55, 0x6A, 0x17, 0xB7,
- 0x81, 0x9A, 0x98, 0xBC, 0x7E, 0xD1, 0x6A, 0x44, 0xB1, 0x18, 0xE6, 0x25,
- 0x3A, 0x62, 0x35, 0xF0, 0x41, 0x91, 0xE2, 0x16, 0x43, 0x9D, 0x8F, 0x7D,
- 0x5D, 0xDA, 0x85, 0x47, 0x25, 0xC4, 0xBA, 0x68, 0x0A, 0x87, 0xDC, 0x2C,
- 0x33, 0xF9, 0x75, 0x65, 0x17, 0xCB, 0x8B, 0x80, 0xFE, 0xE0, 0xA8, 0xAF,
- 0xC7, 0x9E, 0x82, 0xBE, 0x6F, 0x1F, 0x00, 0x04, 0xBD, 0x69, 0x50, 0x8D,
- 0x9C, 0x3C, 0x41, 0x69, 0x21, 0x4E, 0x86, 0xC8, 0x2B, 0xCC, 0x07, 0x4D,
- 0xCF, 0xE4, 0xA2, 0x90, 0x8F, 0x66, 0xA9, 0xEF, 0xF7, 0xFC, 0x6F, 0x5F,
- 0x06, 0x22, 0x00, 0xCB, 0xCB, 0xC3, 0x98, 0x3F, 0x06, 0xB9, 0xEC, 0x48,
- 0x3B, 0x70, 0x6E, 0x94, 0xE9, 0x16, 0xE1, 0xB7, 0x63, 0x2E, 0xAB, 0xB2,
- 0xF3, 0x84, 0xB5, 0x3D, 0xD7, 0x74, 0xF1, 0x6A, 0xD1, 0xEF, 0xE8, 0x04,
- 0x18, 0x76, 0xD2, 0xD6, 0xB0, 0xB7, 0x71, 0xB6, 0x12, 0x8F, 0xD1, 0x33,
- 0xAB, 0x49, 0xAB, 0x09, 0x97, 0x35, 0x9D, 0x4B, 0xBB, 0x54, 0x22, 0x6E,
- 0x1A, 0x33, 0x18, 0x02, 0x8A, 0xF4, 0x7C, 0x0A, 0xCE, 0x89, 0x75, 0x2D,
- 0x10, 0x68, 0x25, 0xA9, 0x6E, 0xCD, 0x97, 0x49, 0xED, 0xAE, 0xE6, 0xA7,
- 0xB0, 0x07, 0x26, 0x25, 0x60, 0x15, 0x2B, 0x65, 0x88, 0x17, 0xF2, 0x5D,
- 0x2C, 0xF6, 0x2A, 0x7A, 0x8C, 0xAD, 0xB6, 0x0A, 0xA2, 0x57, 0xB0, 0xC1,
- 0x0E, 0x5C, 0xA8, 0xA1, 0x96, 0x58, 0x9A, 0x2B, 0xD4, 0xC0, 0x8A, 0xCF,
- 0x91, 0x25, 0x94, 0xB4, 0x14, 0xA7, 0xE4, 0xE2, 0x1B, 0x64, 0x5F, 0xD2,
- 0xCA, 0x70, 0x46, 0xD0, 0x2C, 0x95, 0x6B, 0x9A, 0xFB, 0x83, 0xF9, 0x76,
- 0xE6, 0xD4, 0xA4, 0xA1, 0x2B, 0x2F, 0xF5, 0x1D, 0xE4, 0x06, 0xAF, 0x7D,
- 0x22, 0xF3, 0x04, 0x30, 0x2E, 0x4C, 0x64, 0x12, 0x5B, 0xB0, 0x55, 0x3E,
- 0xC0, 0x5E, 0x56, 0xCB, 0x99, 0xBC, 0xA8, 0xD9, 0x23, 0xF5, 0x57, 0x40,
- 0xF0, 0x52, 0x85, 0x9B,
- };
- static unsigned char dh2236_g[] = {
- 0x02,
- };
- DH *dh;
- if ((dh = DH_new()) == NULL)
- return (NULL);
- dh->p = BN_bin2bn(dh2236_p, sizeof(dh2236_p), NULL);
- dh->g = BN_bin2bn(dh2236_g, sizeof(dh2236_g), NULL);
- if ((dh->p == NULL) || (dh->g == NULL)) {
- DH_free(dh);
- return (NULL);
- }
- return (dh);
- }
- #endif
- #ifndef TEST_WITHOUT_SSL
- int
- init_ssl(void *ssl_ctx, void *user_data)
- {
- /* Add application specific SSL initialization */
- struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_ctx;
- #ifdef USE_SSL_DH
- /* example from https://github.com/civetweb/civetweb/issues/347 */
- DH *dh = get_dh2236();
- if (!dh)
- return -1;
- if (1 != SSL_CTX_set_tmp_dh(ctx, dh))
- return -1;
- DH_free(dh);
- EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (!ecdh)
- return -1;
- if (1 != SSL_CTX_set_tmp_ecdh(ctx, ecdh))
- return -1;
- EC_KEY_free(ecdh);
- printf("ECDH ciphers initialized\n");
- #endif
- return 0;
- }
- #endif
- int
- log_message(const struct mg_connection *conn, const char *message)
- {
- puts(message);
- return 1;
- }
- int
- main(int argc, char *argv[])
- {
- const char *options[] = {
- #if !defined(NO_FILES)
- "document_root",
- DOCUMENT_ROOT,
- #endif
- "listening_ports",
- PORT,
- "request_timeout_ms",
- "10000",
- "error_log_file",
- "error.log",
- #ifdef USE_WEBSOCKET
- "websocket_timeout_ms",
- "3600000",
- #endif
- #ifndef TEST_WITHOUT_SSL
- "ssl_certificate",
- "../../resources/cert/server.pem",
- "ssl_protocol_version",
- "3",
- "ssl_cipher_list",
- #ifdef USE_SSL_DH
- "ECDHE-RSA-AES256-GCM-SHA384:DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256",
- #else
- "DES-CBC3-SHA:AES128-SHA:AES128-GCM-SHA256",
- #endif
- #endif
- "enable_auth_domain_check",
- "no",
- 0
- };
- struct mg_callbacks callbacks;
- struct mg_context *ctx;
- struct mg_server_port ports[32];
- int port_cnt, n;
- int err = 0;
- /* Check if libcivetweb has been built with all required features. */
- #ifdef USE_IPV6
- if (!mg_check_feature(8)) {
- fprintf(stderr,
- "Error: Embedded example built with IPv6 support, "
- "but civetweb library build without.\n");
- err = 1;
- }
- #endif
- #ifdef USE_WEBSOCKET
- if (!mg_check_feature(16)) {
- fprintf(stderr,
- "Error: Embedded example built with websocket support, "
- "but civetweb library build without.\n");
- err = 1;
- }
- #endif
- #ifndef TEST_WITHOUT_SSL
- if (!mg_check_feature(2)) {
- fprintf(stderr,
- "Error: Embedded example built with SSL support, "
- "but civetweb library build without.\n");
- err = 1;
- }
- #endif
- if (err) {
- fprintf(stderr, "Cannot start CivetWeb - inconsistent build.\n");
- return EXIT_FAILURE;
- }
- /* Start CivetWeb web server */
- memset(&callbacks, 0, sizeof(callbacks));
- #ifndef TEST_WITHOUT_SSL
- callbacks.init_ssl = init_ssl;
- #endif
- callbacks.log_message = log_message;
- ctx = mg_start(&callbacks, 0, options);
- /* Check return value: */
- if (ctx == NULL) {
- fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n");
- return EXIT_FAILURE;
- }
- /* Add handler EXAMPLE_URI, to explain the example */
- mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);
- mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);
- /* Add handler for /A* and special handler for /A/B */
- mg_set_request_handler(ctx, "/A", AHandler, 0);
- mg_set_request_handler(ctx, "/A/B", ABHandler, 0);
- /* Add handler for /B, /B/A, /B/B but not for /B* */
- mg_set_request_handler(ctx, "/B$", BXHandler, (void *)"alpha");
- mg_set_request_handler(ctx, "/B/A$", BXHandler, (void *)"beta");
- mg_set_request_handler(ctx, "/B/B$", BXHandler, (void *)"gamma");
- /* Add handler for all files with .foo extension */
- mg_set_request_handler(ctx, "**.foo$", FooHandler, 0);
- /* Add handler for /close extension */
- mg_set_request_handler(ctx, "/close", CloseHandler, 0);
- #if !defined(NO_FILESYSTEMS)
- /* Add handler for /form (serve a file outside the document root) */
- mg_set_request_handler(ctx,
- "/form",
- FileHandler,
- (void *)"../../test/form.html");
- #endif /* NO_FILESYSTEMS */
- /* Add handler for form data */
- mg_set_request_handler(ctx,
- "/handle_form.embedded_c.example.callback",
- FormHandler,
- (void *)0);
- /* Add a file upload handler for parsing files on the fly */
- mg_set_request_handler(ctx,
- "/on_the_fly_form",
- FileUploadForm,
- (void *)"/on_the_fly_form.md5.callback");
- mg_set_request_handler(ctx,
- "/on_the_fly_form.md5.callback",
- CheckSumHandler,
- (void *)0);
- /* Add handler for /cookie example */
- mg_set_request_handler(ctx, "/cookie", CookieHandler, 0);
- /* Add handler for /postresponse example */
- mg_set_request_handler(ctx, "/postresponse", PostResponser, 0);
- /* Add HTTP site to open a websocket connection */
- mg_set_request_handler(ctx, "/websocket", WebSocketStartHandler, 0);
- #if !defined(NO_FILESYSTEMS)
- /* Add HTTP site with auth */
- mg_set_request_handler(ctx, "/auth", AuthStartHandler, 0);
- #endif /* NO_FILESYSTEMS */
- #ifdef USE_WEBSOCKET
- /* WS site for the websocket connection */
- mg_set_websocket_handler(ctx,
- "/websocket",
- WebSocketConnectHandler,
- WebSocketReadyHandler,
- WebsocketDataHandler,
- WebSocketCloseHandler,
- 0);
- #endif
- /* List all listening ports */
- memset(ports, 0, sizeof(ports));
- port_cnt = mg_get_server_ports(ctx, 32, ports);
- printf("\n%i listening ports:\n\n", port_cnt);
- for (n = 0; n < port_cnt && n < 32; n++) {
- const char *proto = ports[n].is_ssl ? "https" : "http";
- const char *host;
- if ((ports[n].protocol & 1) == 1) {
- /* IPv4 */
- host = "127.0.0.1";
- printf("Browse files at %s://%s:%i/\n", proto, host, ports[n].port);
- printf("Run example at %s://%s:%i%s\n",
- proto,
- host,
- ports[n].port,
- EXAMPLE_URI);
- printf(
- "Exit at %s://%s:%i%s\n", proto, host, ports[n].port, EXIT_URI);
- printf("\n");
- }
- if ((ports[n].protocol & 2) == 2) {
- /* IPv6 */
- host = "[::1]";
- printf("Browse files at %s://%s:%i/\n", proto, host, ports[n].port);
- printf("Run example at %s://%s:%i%s\n",
- proto,
- host,
- ports[n].port,
- EXAMPLE_URI);
- printf(
- "Exit at %s://%s:%i%s\n", proto, host, ports[n].port, EXIT_URI);
- printf("\n");
- }
- }
- /* Wait until the server should be closed */
- while (!exitNow) {
- #ifdef _WIN32
- Sleep(1000);
- #else
- sleep(1);
- #endif
- #ifdef USE_WEBSOCKET
- InformWebsockets(ctx);
- #endif
- }
- /* Stop the server */
- mg_stop(ctx);
- printf("Server stopped.\n");
- printf("Bye!\n");
- return EXIT_SUCCESS;
- }
|