/* If the file's extension is .pem, convert it to DER format and put on SLFS. */ static char *sl_pem2der(const char *pem_file) { const char *pem_ext = strstr(pem_file, ".pem"); if (pem_ext == NULL || *(pem_ext + 4) != '\0') { return strdup(pem_file); } char *der_file = NULL; /* DER file must be located on SLFS, add prefix. */ int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der", (int) (pem_ext - pem_file), pem_file); if (der_file == NULL) return NULL; bool result = false; cs_stat_t st; if (mg_stat(der_file, &st) != 0) { result = pem_to_der(pem_file, der_file); LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result)); } else { /* File exists, assume it's already been converted. */ result = true; } if (result) { /* Strip the SL: prefix we added since NWP does not expect it. */ memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, l - 2 /* including \0 */); } else { MG_FREE(der_file); der_file = NULL; } return der_file; }
static int handle_lsp_request(struct mg_connection *conn, const char *path, struct file *filep, struct lua_State *ls) { void *p = NULL; lua_State *L = NULL; FILE *fp = NULL; int error = 1; // We need both mg_stat to get file size, and mg_fopen to get fd if (!mg_stat(path, filep) || (fp = mg_fopen(path, "r")) == NULL) { lsp_send_err(conn, ls, "File [%s] not found", path); } else if ((p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE, fileno(fp), 0)) == MAP_FAILED) { lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size, fileno(fp), strerror(errno)); } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) { send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed"); } else { // We're not sending HTTP headers here, Lua page must do it. if (ls == NULL) { prepare_lua_environment(conn, L); } error = lsp(conn, path, p, filep->size, L); } if (L != NULL && ls == NULL) lua_close(L); if (p != NULL) munmap(p, filep->size); fclose(fp); return error; }
// Use the global passwords file, if specified by auth_gpass option, // or search for .htpasswd in the requested directory. static FILE *open_auth_file(struct mg_connection *conn, const char *path) { char name[PATH_MAX]; const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; struct file file = STRUCT_FILE_INITIALIZER; FILE *fp = NULL; if (gpass != NULL) { // Use global passwords file fp = mg_fopen(gpass, "r"); // Important: using local struct file to test path for is_directory flag. // If filep is used, mg_stat() makes it appear as if auth file was opened. } else if (mg_stat(path, &file) && file.is_directory) { mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME); fp = mg_fopen(name, "r"); } else { // Try to find .htpasswd in requested directory. for (p = path, e = p + strlen(p) - 1; e > p; e--) if (e[0] == '/') break; mg_snprintf(name, sizeof(name), "%.*s%c%s", (int) (e - p), p, '/', PASSWORDS_FILE_NAME); fp = mg_fopen(name, "r"); } return fp; }
static void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch (ev) { case MG_EV_ACCEPT: { char addr[32]; mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); LOG(LL_INFO, ("%p conn from %s", nc, addr)); break; } case MG_EV_HTTP_REQUEST: { char addr[32]; struct http_message *hm = (struct http_message *) ev_data; cs_stat_t st; mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); LOG(LL_INFO, ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len, hm->method.p, (int) hm->uri.len, hm->uri.p)); if (mg_vcmp(&hm->uri, "/upload") == 0 || (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) { mg_send(nc, upload_form, strlen(upload_form)); nc->flags |= MG_F_SEND_AND_CLOSE; break; } struct mg_serve_http_opts opts; memset(&opts, 0, sizeof(opts)); opts.document_root = "SL:"; mg_serve_http(nc, hm, opts); break; } case MG_EV_CLOSE: { LOG(LL_INFO, ("%p closed", nc)); break; } case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { LOG(LL_INFO, ("%p switching to data mode", nc)); nc->handler = data_conn_handler; nc->ev_timer_time = mg_time(); /* Immediately */ break; } case MG_EV_TIMER: { data_collect(); nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001); } case MG_EV_HTTP_PART_BEGIN: case MG_EV_HTTP_PART_DATA: case MG_EV_HTTP_PART_END: { struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) ev_data; if (ev == MG_EV_HTTP_PART_BEGIN) { LOG(LL_INFO, ("Begin file upload: %s", mp->file_name)); } else if (ev == MG_EV_HTTP_PART_END) { LOG(LL_INFO, ("End file upload: %s", mp->file_name)); } mg_file_upload_handler(nc, ev, ev_data, upload_fname); } } }
int main(void) { struct mg_mgr mgr; struct mg_connection *nc; cs_stat_t st; mg_mgr_init(&mgr, NULL); nc = mg_bind(&mgr, s_http_port, ev_handler); if (nc == NULL) { fprintf(stderr, "Cannot bind to %s\n", s_http_port); exit(1); } // Set up HTTP server parameters mg_set_protocol_http_websocket(nc); s_http_server_opts.document_root = "web_root"; // Set up web root directory if (mg_stat(s_http_server_opts.document_root, &st) != 0) { fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n"); exit(1); } printf("Starting web server on port %s\n", s_http_port); for (;;) { static time_t last_time; time_t now = time(NULL); mg_mgr_poll(&mgr, 1000); if (now - last_time > 0) { push_data_to_all_websocket_connections(&mgr); last_time = now; } } mg_mgr_free(&mgr); return 0; }
static void verify_document_root(const char *root) { struct mgstat st; char buf[PATH_MAX]; getcwd(buf, sizeof(buf)); if (mg_stat(root, &st) != 0 || !st.is_directory) { die("Invalid root directory: [%s]: %s; current directory = [%s]", root, mg_strerror(ERRNO), buf); } }
static void test_mg_fetch(void) { static const char *options[] = { "document_root", ".", "listening_ports", "33796", NULL, }; char buf[2000], buf2[2000]; int length; struct mg_context *ctx; struct mg_request_info ri; const char *tmp_file = "temporary_file_name_for_unit_test.txt"; struct mgstat st; FILE *fp; ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL); // Failed fetch, pass invalid URL ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL); ASSERT(mg_fetch(ctx, "localhost:33796", tmp_file, buf, sizeof(buf), &ri) == NULL); ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file, buf, sizeof(buf), &ri) == NULL); // Failed fetch, pass invalid file name ASSERT(mg_fetch(ctx, "http://localhost:33796/data", "/this/file/must/not/exist/ever", buf, sizeof(buf), &ri) == NULL); // Successful fetch ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/data", tmp_file, buf, sizeof(buf), &ri)) != NULL); ASSERT(ri.num_headers == 2); ASSERT(!strcmp(ri.request_method, "HTTP/1.1")); ASSERT(!strcmp(ri.uri, "200")); ASSERT(!strcmp(ri.http_version, "OK")); ASSERT((length = ftell(fp)) == (int) strlen(fetch_data)); fseek(fp, 0, SEEK_SET); ASSERT(fread(buf2, 1, length, fp) == (size_t) length); ASSERT(memcmp(buf2, fetch_data, length) == 0); fclose(fp); // Fetch big file, mongoose.c ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/mongoose.c", tmp_file, buf, sizeof(buf), &ri)) != NULL); ASSERT(mg_stat("mongoose.c", &st) == 0); ASSERT(st.size == ftell(fp)); ASSERT(!strcmp(ri.request_method, "HTTP/1.1")); remove(tmp_file); mg_stop(ctx); }
void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) { LOG(LL_DEBUG, ("%p ev %d", nc, ev)); switch (ev) { case MG_EV_ACCEPT: { char addr[32]; mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); LOG(LL_INFO, ("Connection %p from %s", nc, addr)); break; } case MG_EV_HTTP_REQUEST: { char addr[32]; struct http_message *hm = (struct http_message *) ev_data; cs_stat_t st; mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); LOG(LL_INFO, ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len, hm->method.p, (int) hm->uri.len, hm->uri.p)); if (mg_vcmp(&hm->uri, "/upload") == 0 || (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) { mg_send(nc, upload_form, strlen(upload_form)); nc->flags |= MG_F_SEND_AND_CLOSE; break; } struct mg_serve_http_opts opts; memset(&opts, 0, sizeof(opts)); opts.document_root = "SL:"; mg_serve_http(nc, hm, opts); break; } case MG_EV_CLOSE: { LOG(LL_INFO, ("Connection %p closed", nc)); break; } case MG_EV_HTTP_PART_BEGIN: case MG_EV_HTTP_PART_DATA: case MG_EV_HTTP_PART_END: { struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) ev_data; if (ev == MG_EV_HTTP_PART_BEGIN) { LOG(LL_INFO, ("Begin file upload: %s", mp->file_name)); } else if (ev == MG_EV_HTTP_PART_END) { LOG(LL_INFO, ("End file upload: %s", mp->file_name)); } mg_file_upload_handler(nc, ev, ev_data, upload_fname); } } }
static void test_mg_stat(void) { static struct mg_context ctx; struct file file = STRUCT_FILE_INITIALIZER; ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file)); }
static void test_mg_fetch(void) { static const char *options[] = { "document_root", ".", "listening_ports", UNUSED_PORT, NULL, }; char buf[2000], buf2[2000]; int n, length; struct mg_context *ctx; struct mg_request_info ri; const char *tmp_file = "temporary_file_name_for_unit_test.txt"; struct file file; FILE *fp; ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL); // Failed fetch, pass invalid URL ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL); ASSERT(mg_fetch(ctx, "localhost:" UNUSED_PORT, tmp_file, buf, sizeof(buf), &ri) == NULL); ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file, buf, sizeof(buf), &ri) == NULL); // Failed fetch, pass invalid file name ASSERT(mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data", "/this/file/must/not/exist/ever", buf, sizeof(buf), &ri) == NULL); // Successful fetch ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data", tmp_file, buf, sizeof(buf), &ri)) != NULL); ASSERT(ri.num_headers == 2); ASSERT(!strcmp(ri.request_method, "HTTP/1.1")); ASSERT(!strcmp(ri.uri, "200")); ASSERT(!strcmp(ri.http_version, "OK")); ASSERT((length = ftell(fp)) == (int) strlen(fetch_data)); fseek(fp, 0, SEEK_SET); ASSERT(fread(buf2, 1, length, fp) == (size_t) length); ASSERT(memcmp(buf2, fetch_data, length) == 0); fclose(fp); // Fetch big file, mongoose.c ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/mongoose.c", tmp_file, buf, sizeof(buf), &ri)) != NULL); ASSERT(mg_stat(fc(ctx), "mongoose.c", &file)); ASSERT(file.size == ftell(fp)); ASSERT(!strcmp(ri.request_method, "HTTP/1.1")); // Fetch nonexistent file, /blah ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/boo", tmp_file, buf, sizeof(buf), &ri)) != NULL); ASSERT(!mg_strcasecmp(ri.uri, "404")); fclose(fp); // Fetch existing inmemory file ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/blah", tmp_file, buf, sizeof(buf), &ri)) != NULL); ASSERT(!mg_strcasecmp(ri.uri, "200")); n = ftell(fp); fseek(fp, 0, SEEK_SET); printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2); n = fread(buf2, 1, n, fp); printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2); ASSERT((size_t) ftell(fp) == (size_t) strlen(inmemory_file_data)); ASSERT(!memcmp(inmemory_file_data, buf2, ftell(fp))); fclose(fp); remove(tmp_file); mg_stop(ctx); }
static void *mongoose_callback(enum mg_event event, struct mg_connection *conn) { const struct mg_request_info *request_info = mg_get_request_info(conn); if (event == MG_INIT0) { verify_document_root(mg_get_conn_option(conn, "document_root")); return (void *)1; } #if defined(_WIN32) if (event == MG_EVENT_LOG && strstr(request_info->log_message, "cannot bind to") && !strcmp(request_info->log_severity, "error")) { if (!error_dialog_shown_previously) { MessageBoxA(NULL, request_info->log_message, "Error", MB_OK); error_dialog_shown_previously = 1; } return 0; } #endif if (event != MG_NEW_REQUEST) { // This callback currently only handles new requests return NULL; } { int file_found; struct mgstat fst; assert(request_info->phys_path); file_found = (0 == mg_stat(request_info->phys_path, &fst) && !fst.is_directory); if (file_found) { return NULL; // let mongoose handle the default of 'file exists'... } #ifdef _WIN32 // Send the systray icon as favicon if (!strcmp("/favicon.ico", request_info->uri)) { HMODULE module; HRSRC icon; DWORD len; void *data; module = GetModuleHandle(NULL); icon = FindResource(module, MAKEINTRESOURCE(IDR_FAVICON), RT_RCDATA); data = LockResource(LoadResource(module, icon)); len = SizeofResource(module, icon); mg_add_response_header(conn, 0, "Content-Type", "image/x-icon"); mg_add_response_header(conn, 0, "Cache-Control", "no-cache"); mg_add_response_header(conn, 0, "Content-Length", "%u", (unsigned int)len); //mg_add_response_header(conn, 0, "Connection", suggest_connection_header(conn)); -- not needed any longer mg_write_http_response_head(conn, 200, NULL); if ((int)len != mg_write(conn, data, len)) { mg_send_http_error(conn, 580, NULL, "not all data was written to the socket (len: %u)", (unsigned int)len); // internal error in our custom handler or client closed connection prematurely } return (void *)1; } #endif } return NULL; }
static void *mongoose_callback(enum mg_event event, struct mg_connection *conn) { const struct mg_request_info *request_info = mg_get_request_info(conn); if (event == MG_INIT0) { verify_document_root(mg_get_conn_option(conn, "document_root")); return (void *)1; } #if defined(_WIN32) if (event == MG_EVENT_LOG && strstr(request_info->log_message, "cannot bind to") && !strcmp(request_info->log_severity, "error")) { if (!error_dialog_shown_previously) { MessageBoxA(NULL, request_info->log_message, "Error", MB_OK); error_dialog_shown_previously = 1; } return 0; } #endif #if USE_BEL2125_TEST_NR_18_EVENT_HANDLER { int contentLength = 0; int dataRead = 0; char postData[BUFFER_SIZE] = { 0 }; const char* contentType = NULL; if (event == MG_NEW_REQUEST) { if (strstr(request_info->uri, "/echo") == request_info->uri) { int ie_hack = 0; // testing an assumption; turns out it doesn't matter whether headers make it into TCP stack before you expect to fetch all input data at once. int ie_hack2 = 0; contentLength = atoi(mg_get_header(conn, "Content-Length")); assert(contentLength <= BUFFER_SIZE); mg_set_response_code(conn, 200); if (ie_hack2) mg_connection_must_close(conn); // the stackoverflow suggested fix: http://stackoverflow.com/questions/3731420/why-does-ie-issue-random-xhr-408-12152-responses-using-jquery-post contentType = mg_get_header(conn, "Content-Type"); if (ie_hack) { //mg_add_response_header(conn, 0, "Connection", mg_suggest_connection_header(conn)); -- not needed any longer mg_add_response_header(conn, 0, "Content-Type", contentType); mg_add_response_header(conn, 0, "Content-Length", "%d", contentLength); mg_write_http_response_head(conn, 0, 0); // let the previous mg_set_response_code() decide for us } dataRead = mg_read(conn, postData, contentLength); if (dataRead > 0) { assert(dataRead == contentLength); if (!ie_hack) { //mg_add_response_header(conn, 0, "Connection", mg_suggest_connection_header(conn)); -- not needed any longer mg_add_response_header(conn, 0, "Content-Type", contentType); mg_add_response_header(conn, 0, "Content-Length", "%d", dataRead); mg_write_http_response_head(conn, 0, 0); // let the previous mg_set_response_code() decide for us } if (mg_write(conn, postData, dataRead) != contentLength) { mg_send_http_error(conn, 580, NULL, "not all data was written to the socket (len: %u)", (unsigned int)contentLength); // internal error in our custom handler or client closed connection prematurely } return (void*)1; } else { mg_send_http_error(conn, 500, NULL, "I/O failure during mg_read() from connection: %s", mg_strerror(ERRNO)); } } } } #endif // USE_BEL2125_TEST_NR_18_EVENT_HANDLER if (event != MG_NEW_REQUEST) { // This callback currently only handles new requests return NULL; } { int file_found; struct mgstat fst; assert(request_info->phys_path); file_found = (0 == mg_stat(request_info->phys_path, &fst) && !fst.is_directory); if (file_found) { return NULL; // let mongoose handle the default of 'file exists'... } #ifdef _WIN32 // Send the systray icon as favicon if (!strcmp("/favicon.ico", request_info->uri)) { HMODULE module; HRSRC icon; DWORD len; void *data; module = GetModuleHandle(NULL); icon = FindResource(module, MAKEINTRESOURCE(IDR_FAVICON), RT_RCDATA); data = LockResource(LoadResource(module, icon)); len = SizeofResource(module, icon); mg_add_response_header(conn, 0, "Content-Type", "image/x-icon"); mg_add_response_header(conn, 0, "Cache-Control", "no-cache"); mg_add_response_header(conn, 0, "Content-Length", "%u", (unsigned int)len); //mg_add_response_header(conn, 0, "Connection", suggest_connection_header(conn)); -- not needed any longer mg_write_http_response_head(conn, 200, NULL); if ((int)len != mg_write(conn, data, len)) { mg_send_http_error(conn, 580, NULL, "not all data was written to the socket (len: %u)", (unsigned int)len); // internal error in our custom handler or client closed connection prematurely } return (void *)1; } #endif } return NULL; }
static void test_mg_stat(void) { struct file file = STRUCT_FILE_INITIALIZER; ASSERT(!mg_stat(" does not exist ", &file)); }
static void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch (ev) { case MG_EV_ACCEPT: { char addr[32]; mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); LOG(LL_INFO, ("%p conn from %s", nc, addr)); break; } case MG_EV_HTTP_REQUEST: { char addr[32]; struct http_message *hm = (struct http_message *) ev_data; cs_stat_t st; mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); LOG(LL_INFO, ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len, hm->method.p, (int) hm->uri.len, hm->uri.p)); if (mg_vcmp(&hm->uri, "/upload") == 0 || (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) { mg_send(nc, upload_form, strlen(upload_form)); nc->flags |= MG_F_SEND_AND_CLOSE; break; } struct mg_serve_http_opts opts; memset(&opts, 0, sizeof(opts)); opts.document_root = "SL:"; mg_serve_http(nc, hm, opts); break; } case MG_EV_CLOSE: { LOG(LL_INFO, ("%p closed", nc)); break; } case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { LOG(LL_INFO, ("%p switching to data mode", nc)); nc->handler = data_conn_handler; nc->ev_timer_time = mg_time(); /* Immediately */ break; } case MG_EV_TIMER: { data_collect(); nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001); break; } /* SimpleLink FS requires pre-declaring max file size. We use Content-Length * for that purpose - it will not exactly match file size, but is guaranteed * to exceed it and should be close enough. */ case MG_EV_HTTP_MULTIPART_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; struct mg_str *cl_header = mg_get_http_header(hm, "Content-Length"); intptr_t cl = -1; if (cl_header != NULL && cl_header->len < 20) { char buf[20]; memcpy(buf, cl_header->p, cl_header->len); buf[cl_header->len] = '\0'; cl = atoi(buf); if (cl < 0) cl = -1; } nc->user_data = (void *) cl; break; } case MG_EV_HTTP_PART_BEGIN: case MG_EV_HTTP_PART_DATA: case MG_EV_HTTP_PART_END: { struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) ev_data; if (ev == MG_EV_HTTP_PART_BEGIN) { LOG(LL_INFO, ("Begin file upload: %s", mp->file_name)); } else if (ev == MG_EV_HTTP_PART_END) { LOG(LL_INFO, ("End file upload: %s", mp->file_name)); } mg_file_upload_handler(nc, ev, ev_data, upload_fname); } } }