From c20e48179f16c062bd7d18c96b86ea29bf67859f Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Fri, 13 May 2022 22:15:33 +0100 Subject: [PATCH] Un-break the test and untie serving code in dash example --- examples/complete/Makefile | 2 +- examples/complete/main.c | 129 +---------------------------------- examples/complete/web.c | 133 +++++++++++++++++++++++++++++++++++++ src/fs.h | 4 -- src/fs_packed.c | 3 + 5 files changed, 139 insertions(+), 132 deletions(-) create mode 100644 examples/complete/web.c diff --git a/examples/complete/Makefile b/examples/complete/Makefile index eeececa6..a99908b0 100644 --- a/examples/complete/Makefile +++ b/examples/complete/Makefile @@ -1,5 +1,5 @@ PROG ?= example -SOURCES ?= ../../mongoose.c main.c packed_fs.c +SOURCES ?= ../../mongoose.c main.c web.c packed_fs.c CFLAGS ?= -I../.. -DMG_ENABLE_PACKED_FS=1 $(EXTRA) FILES_TO_EMBED ?= $(wildcard web_root/*) ROOT ?= $(realpath $(CURDIR)/../../..) diff --git a/examples/complete/main.c b/examples/complete/main.c index c041c2a2..40f5e92d 100644 --- a/examples/complete/main.c +++ b/examples/complete/main.c @@ -3,136 +3,11 @@ #include "mongoose.h" -// Authenticated user. -// A user can be authenticated by: -// - a name:pass pair -// - a token -// When a user is shown a login screen, she enters a user:pass. If successful, -// a server returns user info which includes token. From that point on, -// client can use token for authentication. Tokens could be refreshed/changed -// on a server side, forcing clients to re-login. -struct user { - const char *name, *pass, *token; -}; - -// This is a configuration structure we're going to show on a dashboard -static struct config { - int value1; - char *value2; -} s_config = {123, NULL}; - -// Update config structure. Return true if changed, false otherwise -static bool update_config(struct mg_http_message *hm, struct config *cfg) { - bool changed = false; - char buf[256]; - if (mg_http_get_var(&hm->body, "value1", buf, sizeof(buf)) > 0) { - cfg->value1 = atoi(buf); - changed = true; - } - if (mg_http_get_var(&hm->body, "value2", buf, sizeof(buf)) > 0) { - free(cfg->value2); - cfg->value2 = strdup(buf); - changed = true; - } - return changed; -} - -// Parse HTTP requests, return authenticated user or NULL -static struct user *getuser(struct mg_http_message *hm) { - // In production, make passwords strong and tokens randomly generated - // In this example, user list is kept in RAM. In production, it can - // be backed by file, database, or some other method. - static struct user users[] = { - {"admin", "pass0", "admin_token"}, - {"user1", "pass1", "user1_token"}, - {"user2", "pass2", "user2_token"}, - {NULL, NULL, NULL}, - }; - char user[256], pass[256]; - struct user *u; - mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass)); - if (user[0] != '\0' && pass[0] != '\0') { - // Both user and password is set, search by user/password - for (u = users; u->name != NULL; u++) - if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) return u; - } else if (user[0] == '\0') { - // Only password is set, search by token - for (u = users; u->name != NULL; u++) - if (strcmp(pass, u->token) == 0) return u; - } - return NULL; -} - -// Notify all config watchers about the config change -static void send_notification(struct mg_mgr *mgr, const char *name, - const char *data) { - struct mg_connection *c; - for (c = mgr->conns; c != NULL; c = c->next) { - if (c->label[0] == 'W') - mg_http_printf_chunk(c, "{\"name\": \"%s\", \"data\": \"%s\"}", name, - data == NULL ? "" : data); - } -} - -// HTTP request handler function -static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - struct user *u = getuser(hm); - // MG_INFO(("%p [%.*s] auth %s", c->fd, (int) hm->uri.len, hm->uri.ptr, - // u ? u->name : "NULL")); - if (u == NULL && mg_http_match_uri(hm, "/api/#")) { - // All URIs starting with /api/ must be authenticated - mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n"); - } else if (mg_http_match_uri(hm, "/api/config/get")) { - mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); - mg_http_printf_chunk(c, "{\"%s\":%d,\"%s\":\"%s\"}", "value1", - s_config.value1, "value2", s_config.value2); - mg_http_printf_chunk(c, ""); - } else if (mg_http_match_uri(hm, "/api/config/set")) { - // Admins only - if (strcmp(u->name, "admin") == 0) { - if (update_config(hm, &s_config)) - send_notification(fn_data, "config", NULL); - mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); - } else { - mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n"); - } - } else if (mg_http_match_uri(hm, "/api/message/send")) { - char buf[256]; - if (mg_http_get_var(&hm->body, "message", buf, sizeof(buf)) > 0) { - send_notification(fn_data, "message", buf); - } - mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); - } else if (mg_http_match_uri(hm, "/api/watch")) { - c->label[0] = 'W'; // Mark ourselves as a event listener - mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); - } else if (mg_http_match_uri(hm, "/api/login")) { - mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); - mg_http_printf_chunk(c, "{\"user\":\"%s\",\"token\":\"%s\"}\n", u->name, - u->token); - mg_http_printf_chunk(c, ""); - } else { - struct mg_http_serve_opts opts = {0}; -#if 1 - opts.root_dir = "/web_root"; - opts.fs = &mg_fs_packed; -#else - opts.root_dir = "web_root"; -#endif - mg_http_serve_dir(c, ev_data, &opts); - } - } -} +extern void run_web_server(struct mg_mgr *); int main(void) { struct mg_mgr mgr; - s_config.value2 = strdup("hello"); - mg_mgr_init(&mgr); - mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr); - for (;;) mg_mgr_poll(&mgr, 50); - mg_mgr_free(&mgr); - + run_web_server(&mgr); return 0; } diff --git a/examples/complete/web.c b/examples/complete/web.c new file mode 100644 index 00000000..573e9b31 --- /dev/null +++ b/examples/complete/web.c @@ -0,0 +1,133 @@ +// Copyright (c) 2020-2022 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" + +// Authenticated user. +// A user can be authenticated by: +// - a name:pass pair +// - a token +// When a user is shown a login screen, she enters a user:pass. If successful, +// a server returns user info which includes token. From that point on, +// client can use token for authentication. Tokens could be refreshed/changed +// on a server side, forcing clients to re-login. +struct user { + const char *name, *pass, *token; +}; + +// This is a configuration structure we're going to show on a dashboard +static struct config { + int value1; + char *value2; +} s_config = {123, NULL}; + +// Update config structure. Return true if changed, false otherwise +static bool update_config(struct mg_http_message *hm, struct config *cfg) { + bool changed = false; + char buf[256]; + if (mg_http_get_var(&hm->body, "value1", buf, sizeof(buf)) > 0) { + cfg->value1 = atoi(buf); + changed = true; + } + if (mg_http_get_var(&hm->body, "value2", buf, sizeof(buf)) > 0) { + free(cfg->value2); + cfg->value2 = strdup(buf); + changed = true; + } + return changed; +} + +// Parse HTTP requests, return authenticated user or NULL +static struct user *getuser(struct mg_http_message *hm) { + // In production, make passwords strong and tokens randomly generated + // In this example, user list is kept in RAM. In production, it can + // be backed by file, database, or some other method. + static struct user users[] = { + {"admin", "pass0", "admin_token"}, + {"user1", "pass1", "user1_token"}, + {"user2", "pass2", "user2_token"}, + {NULL, NULL, NULL}, + }; + char user[256], pass[256]; + struct user *u; + mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass)); + if (user[0] != '\0' && pass[0] != '\0') { + // Both user and password is set, search by user/password + for (u = users; u->name != NULL; u++) + if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) return u; + } else if (user[0] == '\0') { + // Only password is set, search by token + for (u = users; u->name != NULL; u++) + if (strcmp(pass, u->token) == 0) return u; + } + return NULL; +} + +// Notify all config watchers about the config change +static void send_notification(struct mg_mgr *mgr, const char *name, + const char *data) { + struct mg_connection *c; + for (c = mgr->conns; c != NULL; c = c->next) { + if (c->label[0] == 'W') + mg_http_printf_chunk(c, "{\"name\": \"%s\", \"data\": \"%s\"}", name, + data == NULL ? "" : data); + } +} + +// HTTP request handler function +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + struct user *u = getuser(hm); + // MG_INFO(("%p [%.*s] auth %s", c->fd, (int) hm->uri.len, hm->uri.ptr, + // u ? u->name : "NULL")); + if (u == NULL && mg_http_match_uri(hm, "/api/#")) { + // All URIs starting with /api/ must be authenticated + mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n"); + } else if (mg_http_match_uri(hm, "/api/config/get")) { + mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + mg_http_printf_chunk(c, "{\"%s\":%d,\"%s\":\"%s\"}", "value1", + s_config.value1, "value2", s_config.value2); + mg_http_printf_chunk(c, ""); + } else if (mg_http_match_uri(hm, "/api/config/set")) { + // Admins only + if (strcmp(u->name, "admin") == 0) { + if (update_config(hm, &s_config)) + send_notification(fn_data, "config", NULL); + mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + } else { + mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n"); + } + } else if (mg_http_match_uri(hm, "/api/message/send")) { + char buf[256]; + if (mg_http_get_var(&hm->body, "message", buf, sizeof(buf)) > 0) { + send_notification(fn_data, "message", buf); + } + mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + } else if (mg_http_match_uri(hm, "/api/watch")) { + c->label[0] = 'W'; // Mark ourselves as a event listener + mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + } else if (mg_http_match_uri(hm, "/api/login")) { + mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + mg_http_printf_chunk(c, "{\"user\":\"%s\",\"token\":\"%s\"}\n", u->name, + u->token); + mg_http_printf_chunk(c, ""); + } else { + struct mg_http_serve_opts opts = {0}; +#if 1 + opts.root_dir = "/web_root"; + opts.fs = &mg_fs_packed; +#else + opts.root_dir = "web_root"; +#endif + mg_http_serve_dir(c, ev_data, &opts); + } + } +} + +void run_web_server(struct mg_mgr *mgr) { + s_config.value2 = strdup("hello"); + mg_http_listen(mgr, "http://0.0.0.0:8000", fn, mgr); + for (;;) mg_mgr_poll(mgr, 1000); + mg_mgr_free(mgr); +} diff --git a/src/fs.h b/src/fs.h index 43e15a31..f7cce376 100644 --- a/src/fs.h +++ b/src/fs.h @@ -40,7 +40,3 @@ void mg_fs_close(struct mg_fd *fd); char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size); bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t); bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...); - -// API for the embedded (packed) filesystem -const char *mg_unpack(const char *path, size_t *size, time_t *mtime); -const char *mg_unlist(size_t no); diff --git a/src/fs_packed.c b/src/fs_packed.c index 88ddca8a..179666fb 100644 --- a/src/fs_packed.c +++ b/src/fs_packed.c @@ -7,6 +7,9 @@ struct packed_file { size_t pos; }; +const char *mg_unpack(const char *path, size_t *size, time_t *mtime); +const char *mg_unlist(size_t no); + #if MG_ENABLE_PACKED_FS #else const char *mg_unpack(const char *path, size_t *size, time_t *mtime) {