Message ID | 1520420366-6564-2-git-send-email-sbabic@denx.de |
---|---|
State | Changes Requested |
Headers | show |
Series | [V3,1/2] mongoose: split upload_handler | expand |
Hi Stefano, Am 07.03.2018 um 11:59 schrieb Stefano Babic: > V1 version of REST-API is still more suitable for M2M > updates together with the SWU forwarder handler. > However, it is not currently possible to switch > between them and the API must be selected at compile time, > and this requires different binaries. > > Let the user decides which version of API should run > with a command line parameter. > > Signed-off-by: Stefano Babic <sbabic@denx.de> > CC: Sami Hartikainen <sami.hartikainen@teleste.com> > --- > > Changes since V2: > - use enum for API > - drop V1 website support and let just the two URLs: > handle_post_request > getstatus.json > this to guarantee the compatibility with SWU forwarder and > proprietary applications using (or abusing of..) the REST-API > For the Webserver itself, just V2 should be used. > Changes since V1: > - Drop MONGOOSE_WEB_API_V1 and MONGOOSE_WEB_API_V2 > - Add help for "api" > - Add "api" parameter to configuration file > - "/upload" URL just in case of v2 > - print API version when Webserver is started > > mongoose/Config.in | 18 -- > mongoose/Makefile | 2 - > mongoose/mongoose_interface.c | 414 ++++++++++++++++++++---------------------- > 3 files changed, 193 insertions(+), 241 deletions(-) > > diff --git a/mongoose/Config.in b/mongoose/Config.in > index a001247..29cac90 100644 > --- a/mongoose/Config.in > +++ b/mongoose/Config.in > @@ -18,24 +18,6 @@ config MONGOOSE > > endchoice > > -choice > - prompt "Web Application Interface" > - default MONGOOSE_WEB_API_V1 > - help > - Choose the bootloader > - > -config MONGOOSE_WEB_API_V1 > - bool "Version 1 (deprecated)" > - help > - Support for version 1 > - > -config MONGOOSE_WEB_API_V2 > - bool "Version 2" > - help > - Support for version 2 > - > -endchoice > - > config MONGOOSEIPV6 > bool "IPv6 support" > default y > diff --git a/mongoose/Makefile b/mongoose/Makefile > index 77a616c..dc2d3d3 100644 > --- a/mongoose/Makefile > +++ b/mongoose/Makefile > @@ -1,9 +1,7 @@ > ifneq ($(CONFIG_WEBSERVER),) > ifneq ($(CONFIG_MONGOOSE),) > KBUILD_CFLAGS += -DMG_ENABLE_HTTP_STREAMING_MULTIPART=1 > -ifneq ($(CONFIG_MONGOOSE_WEB_API_V2),) > KBUILD_CFLAGS += -DMG_ENABLE_HTTP_WEBSOCKET=1 -DMG_ENABLE_THREADS=1 > -endif > ifneq ($(CONFIG_MONGOOSEIPV6),) > KBUILD_CFLAGS += -DMG_ENABLE_IPV6=1 > endif > diff --git a/mongoose/mongoose_interface.c b/mongoose/mongoose_interface.c > index 0e22671..9ef9070 100644 > --- a/mongoose/mongoose_interface.c > +++ b/mongoose/mongoose_interface.c > @@ -32,6 +32,11 @@ > #define MG_PORT "8080" > #define MG_ROOT "." > > +enum MONGOOSE_API_VERSION { > + MONGOOSE_API_V1, > + MONGOOSE_API_V2 > +}; > + > struct mongoose_options { > char *root; > char *listing; > @@ -40,6 +45,7 @@ struct mongoose_options { > char *ssl_cert; > char *ssl_key; > #endif > + enum MONGOOSE_API_VERSION api_version; > }; > > struct file_upload_state { > @@ -50,7 +56,9 @@ struct file_upload_state { > static struct mg_serve_http_opts s_http_server_opts; > static void upload_handler(struct mg_connection *nc, int ev, void *p); > > -#if defined(CONFIG_MONGOOSE_WEB_API_V2) > +/* > + * These functions are for V2 of the protocol > + */ > #define enum_string(x) [x] = #x > static const char *get_status_string(unsigned int status) > { > @@ -111,210 +119,7 @@ static size_t snescape(char *dst, size_t n, const char *src) > > return len; > } > -#endif > - > -#if defined(CONFIG_MONGOOSE_WEB_API_V1) > -static void upload_handler_v1(struct mg_connection *nc, int ev, void *p) > -{ > - struct mg_str *filename, *data; > - struct http_message *hm; > - size_t length; > - char buf[16]; > - int fd; > - > - switch (ev) { > - case MG_EV_HTTP_REQUEST: > - hm = (struct http_message *) p; > - > - filename = mg_get_http_header(hm, "X_FILENAME"); > - if (filename == NULL) { > - mg_http_send_error(nc, 403, NULL); > - return; > - } > - > - data = mg_get_http_header(hm, "Content-length"); > - if (data == NULL || data->len >= ARRAY_SIZE(buf)) { > - mg_http_send_error(nc, 403, NULL); > - return; > - } > - > - memcpy(buf, data->p, data->len); > - buf[data->len] = '\0'; > - length = strtoul(data->p, NULL, 10); > - if (length == 0) { > - mg_http_send_error(nc, 403, NULL); > - return; > - } > - > - fd = ipc_inst_start_ext(SOURCE_WEBSERVER, filename->len, filename->p); > - ipc_send_data(fd, (char *) hm->body.p, hm->body.len); > - ipc_end(fd); > - > - mg_send_response_line(nc, 200, > - "Content-Type: text/plain\r\n" > - "Connection: close"); > - mg_send(nc, "\r\n", 2); > - mg_printf(nc, "Ok, %.*s - %d bytes.\r\n", (int) filename->len, filename->p, (int) length); > - nc->flags |= MG_F_SEND_AND_CLOSE; > - break; > - default: > - upload_handler(nc, ev, p); > - break; > - } > -} > -#endif > - > -static void upload_handler(struct mg_connection *nc, int ev, void *p) > -{ > - struct mg_http_multipart_part *mp; > - struct file_upload_state *fus; > - > - switch (ev) { > - case MG_EV_HTTP_PART_BEGIN: > - mp = (struct mg_http_multipart_part *) p; > - > - fus = (struct file_upload_state *) calloc(1, sizeof(*fus)); > - if (fus == NULL) { > - mg_http_send_error(nc, 500, "Out of memory"); > - break; > - } > - > - fus->fd = ipc_inst_start_ext(SOURCE_WEBSERVER, strlen(mp->file_name), mp->file_name); > - if (fus->fd < 0) { > - mg_http_send_error(nc, 500, "Failed to queue command"); > - free(fus); > - break; > - } > - > - mp->user_data = fus; > - > - break; > - > - case MG_EV_HTTP_PART_DATA: > - mp = (struct mg_http_multipart_part *) p; > - fus = (struct file_upload_state *) mp->user_data; > - > - if (!fus) > - break; > - > - ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len); > - fus->len += mp->data.len; > - > - break; > - > - case MG_EV_HTTP_PART_END: > - mp = (struct mg_http_multipart_part *) p; > - fus = (struct file_upload_state *) mp->user_data; > - > - if (!fus) > - break; > - > - ipc_end(fus->fd); > - > - mg_send_response_line(nc, 200, > - "Content-Type: text/plain\r\n" > - "Connection: close"); > - mg_send(nc, "\r\n", 2); > - mg_printf(nc, "Ok, %s - %d bytes.\r\n", mp->file_name, (int) fus->len); > - nc->flags |= MG_F_SEND_AND_CLOSE; > - > - mp->user_data = NULL; > - free(fus); > - break; > - } > -} > > -#if defined(CONFIG_MONGOOSE_WEB_API_V1) > -static void recovery_status(struct mg_connection *nc, int ev, void *ev_data) > -{ > - ipc_message ipc; > - int ret; > - char buf[4096]; > - > - (void)ev; > - (void)ev_data; > - > - ret = ipc_get_status(&ipc); > - > - if (ret) { > - mg_http_send_error(nc, 500, NULL); > - return; > - } > - > - snprintf(buf, sizeof(buf), > - "{\r\n" > - "\t\"Status\" : \"%d\",\r\n" > - "\t\"Msg\" : \"%s\",\r\n" > - "\t\"Error\" : \"%d\",\r\n" > - "\t\"LastResult\" : \"%d\"\r\n" > - "}\r\n", > - ipc.data.status.current, > - strlen(ipc.data.status.desc) ? ipc.data.status.desc : "", > - ipc.data.status.error, > - ipc.data.status.last_result); > - > - mg_send_head(nc, 200, strlen(buf), > - "Cache: no-cache\r\n" > - "Content-Type: text/plain"); > - > - mg_send(nc, buf, strlen(buf)); > - > - nc->flags |= MG_F_SEND_AND_CLOSE; > -} > - > -static void reboot_target(struct mg_connection *nc, int ev, void *ev_data) > -{ > - struct http_message *hm = (struct http_message *) ev_data; > - int ret; > - > - (void)ev; > - > - if(mg_vcasecmp(&hm->method, "POST") == 0) { > - ret = system("reboot"); > - if (ret) { > - mg_http_send_error(nc, 500, > - "Device cannot be reboot, internal fault."); > - return; > - } > - > - mg_http_send_error(nc, 200, "Device will reboot now."); > - } > - else { > - mg_send_response_line(nc, 200, > - "Content-Type: text/html\r\n" > - "Connection: close"); > - mg_send(nc, "\r\n", 2); > - mg_printf(nc, > - "<form method='POST' action=''>" > - "<input type='submit' value='Reboot'>" > - "</form>"); > - nc->flags |= MG_F_SEND_AND_CLOSE; > - } > -} > - > -static void post_update_cmd(struct mg_connection *nc, int ev, void *ev_data) > -{ > - ipc_message msg = {}; > - > - (void)ev; > - (void)ev_data; > - > - int ret = ipc_postupdate(&msg); > - mg_send_response_line(nc, 200, "Content-Type: application/json"); > - mg_send(nc, "\r\n", 2); > - mg_printf(nc, > - "{\r\n" > - "\t\"code\": %d,\r\n" > - "\t\"error\": \"%s\",\r\n" > - "\t\"detail\": \"%s\"\r\n" > - "}", > - (ret == 0) ? 200 : 501, > - (ret == 0) ? "" : "Internal server error", > - (ret == 0) ? "" : "Failed to queue command"); > - > - nc->flags |= MG_F_SEND_AND_CLOSE; > -} > -#elif defined(CONFIG_MONGOOSE_WEB_API_V2) > static void restart_handler(struct mg_connection *nc, int ev, void *ev_data) > { > struct http_message *hm = (struct http_message *) ev_data; > @@ -470,7 +275,158 @@ static void *broadcast_progress_thread(void *data) > > return NULL; > } > -#endif > + > +/* > + * These functions are for V1 of the protocol > + */ > +static void upload_handler_v1(struct mg_connection *nc, int ev, void *p) > +{ > + struct mg_str *filename, *data; > + struct http_message *hm; > + size_t length; > + char buf[16]; > + int fd; > + > + switch (ev) { > + case MG_EV_HTTP_REQUEST: > + hm = (struct http_message *) p; > + > + filename = mg_get_http_header(hm, "X_FILENAME"); > + if (filename == NULL) { > + mg_http_send_error(nc, 403, NULL); > + return; > + } > + > + data = mg_get_http_header(hm, "Content-length"); > + if (data == NULL || data->len >= ARRAY_SIZE(buf)) { > + mg_http_send_error(nc, 403, NULL); > + return; > + } > + > + memcpy(buf, data->p, data->len); > + buf[data->len] = '\0'; > + length = strtoul(data->p, NULL, 10); > + if (length == 0) { > + mg_http_send_error(nc, 403, NULL); > + return; > + } > + > + fd = ipc_inst_start_ext(SOURCE_WEBSERVER, filename->len, filename->p); > + ipc_send_data(fd, (char *) hm->body.p, hm->body.len); > + ipc_end(fd); > + > + mg_send_response_line(nc, 200, > + "Content-Type: text/plain\r\n" > + "Connection: close"); > + mg_send(nc, "\r\n", 2); > + mg_printf(nc, "Ok, %.*s - %d bytes.\r\n", (int) filename->len, filename->p, (int) length); > + nc->flags |= MG_F_SEND_AND_CLOSE; > + break; > + default: > + upload_handler(nc, ev, p); > + break; > + } > +} > + > +static void recovery_status(struct mg_connection *nc, int ev, void *ev_data) > +{ > + ipc_message ipc; > + int ret; > + char buf[4096]; > + > + (void)ev; > + (void)ev_data; > + > + ret = ipc_get_status(&ipc); > + > + if (ret) { > + mg_http_send_error(nc, 500, NULL); > + return; > + } > + > + snprintf(buf, sizeof(buf), > + "{\r\n" > + "\t\"Status\" : \"%d\",\r\n" > + "\t\"Msg\" : \"%s\",\r\n" > + "\t\"Error\" : \"%d\",\r\n" > + "\t\"LastResult\" : \"%d\"\r\n" > + "}\r\n", > + ipc.data.status.current, > + strlen(ipc.data.status.desc) ? ipc.data.status.desc : "", > + ipc.data.status.error, > + ipc.data.status.last_result); > + > + mg_send_head(nc, 200, strlen(buf), > + "Cache: no-cache\r\n" > + "Content-Type: text/plain"); > + > + mg_send(nc, buf, strlen(buf)); > + > + nc->flags |= MG_F_SEND_AND_CLOSE; > +} > + > +/* > + * Code common to V1 and V2 > + */ > +static void upload_handler(struct mg_connection *nc, int ev, void *p) > +{ > + struct mg_http_multipart_part *mp; > + struct file_upload_state *fus; > + > + switch (ev) { > + case MG_EV_HTTP_PART_BEGIN: > + mp = (struct mg_http_multipart_part *) p; > + > + fus = (struct file_upload_state *) calloc(1, sizeof(*fus)); > + if (fus == NULL) { > + mg_http_send_error(nc, 500, "Out of memory"); > + break; > + } > + > + fus->fd = ipc_inst_start_ext(SOURCE_WEBSERVER, strlen(mp->file_name), mp->file_name); > + if (fus->fd < 0) { > + mg_http_send_error(nc, 500, "Failed to queue command"); > + free(fus); > + break; > + } > + > + mp->user_data = fus; > + > + break; > + > + case MG_EV_HTTP_PART_DATA: > + mp = (struct mg_http_multipart_part *) p; > + fus = (struct file_upload_state *) mp->user_data; > + > + if (!fus) > + break; > + > + ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len); > + fus->len += mp->data.len; > + > + break; > + > + case MG_EV_HTTP_PART_END: > + mp = (struct mg_http_multipart_part *) p; > + fus = (struct file_upload_state *) mp->user_data; > + > + if (!fus) > + break; > + > + ipc_end(fus->fd); > + > + mg_send_response_line(nc, 200, > + "Content-Type: text/plain\r\n" > + "Connection: close"); > + mg_send(nc, "\r\n", 2); > + mg_printf(nc, "Ok, %s - %d bytes.\r\n", mp->file_name, (int) fus->len); > + nc->flags |= MG_F_SEND_AND_CLOSE; > + > + mp->user_data = NULL; > + free(fus); > + break; > + } > +} > > static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) > { > @@ -510,6 +466,12 @@ static int mongoose_settings(void *elem, void __attribute__ ((__unused__)) *dat > opts->ssl_key = strdup(tmp); > } > #endif > + GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "api", tmp); > + if (strlen(tmp)) { > + opts->api_version = (!strcmp(tmp, "1")) ? > + MONGOOSE_API_V1 : > + MONGOOSE_API_V2; > + } > return 0; > } > > @@ -523,6 +485,7 @@ static struct option long_options[] = { > {"ssl-key", required_argument, NULL, 'K'}, > #endif > {"document-root", required_argument, NULL, 'r'}, > + {"api-version", required_argument, NULL, 'a'}, > {NULL, 0, NULL, 0} > }; > > @@ -538,7 +501,8 @@ void mongoose_print_help(void) > "\t -C, --ssl-cert <cert> : ssl certificate to present to clients\n" > "\t -K, --ssl-key <key> : key corresponding to the ssl certificate\n" > #endif > - "\t -r, --document-root <path> : path to document root directory (default: %s)\n", > + "\t -r, --document-root <path> : path to document root directory (default: %s)\n" > + "\t -a, --api-version [1|2] : set Web protocol API to v1 (legacy) or v2 (default v2)\n", > MG_LISTING, MG_PORT, MG_ROOT); > } > > @@ -556,12 +520,14 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) > int choice = 0; > > memset(&opts, 0, sizeof(opts)); > + /* Set default API version */ > + opts.api_version = MONGOOSE_API_V2; > if (cfgfname) { > read_module_settings(cfgfname, "webserver", mongoose_settings, &opts); > } > > optind = 1; > - while ((choice = getopt_long(argc, argv, "lp:sC:K:r:", > + while ((choice = getopt_long(argc, argv, "lp:sC:K:r:a:", > long_options, NULL)) != -1) { > switch (choice) { > case 'l': > @@ -589,6 +555,11 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) > free(opts.root); > opts.root = strdup(optarg); > break; > + case 'a': > + opts.api_version = (!strcmp(optarg, "1")) ? > + MONGOOSE_API_V1 : > + MONGOOSE_API_V2; > + break; > case '?': > default: > return -EINVAL; > @@ -619,22 +590,23 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) > } > > mg_set_protocol_http_websocket(nc); > - mg_register_http_endpoint(nc, "/upload", MG_CB(upload_handler, NULL)); > -#if defined(CONFIG_MONGOOSE_WEB_API_V1) > - mg_register_http_endpoint(nc, "/handle_post_request", MG_CB(upload_handler_v1, NULL)); > - mg_register_http_endpoint(nc, "/getstatus.json", MG_CB(recovery_status, NULL)); > - mg_register_http_endpoint(nc, "/rebootTarget", MG_CB(reboot_target, NULL)); > - mg_register_http_endpoint(nc, "/postUpdateCommand", MG_CB(post_update_cmd, NULL)); > -#endif > -#if defined(CONFIG_MONGOOSE_WEB_API_V2) > - mg_register_http_endpoint(nc, "/restart", restart_handler); > - mg_start_thread(broadcast_message_thread, &mgr); > - mg_start_thread(broadcast_progress_thread, &mgr); > -#endif > + switch (opts.api_version) { > + case MONGOOSE_API_V1: > + mg_register_http_endpoint(nc, "/handle_post_request", MG_CB(upload_handler_v1, NULL)); > + mg_register_http_endpoint(nc, "/getstatus.json", MG_CB(recovery_status, NULL)); > + break; > + case MONGOOSE_API_V2: > + mg_register_http_endpoint(nc, "/restart", restart_handler); > + mg_register_http_endpoint(nc, "/upload", MG_CB(upload_handler, NULL)); > + mg_start_thread(broadcast_message_thread, &mgr); > + mg_start_thread(broadcast_progress_thread, &mgr); > + break; > + } > > - printf("Mongoose web server version %s with pid %d started on port(s) %s with web root [%s]\n", > + printf("Mongoose web server version %s with pid %d started on port(s) %s with web root [%s] and API %s\n", > MG_VERSION, getpid(), s_http_port, > - s_http_server_opts.document_root); > + s_http_server_opts.document_root, > + (opts.api_version == MONGOOSE_API_V1) ? "v1" : "v2"); > > for (;;) { > mg_mgr_poll(&mgr, 100); If you like you could disable the http file server for version 1. Therefore you have to skip the mg_serve_http function in the ev_handler function or add an empty ev_handler_v1 function. Best regards Stefan
Hi Stefan, On 07/03/2018 20:33, Stefan Herbrechtsmeier wrote: > Hi Stefano, > [snip] >> for (;;) { >> mg_mgr_poll(&mgr, 100); > > If you like you could disable the http file server for version 1. I like it, it is a good idea, thanks for the tip ! > Therefore you have to skip the mg_serve_http function in the ev_handler > function or add an empty ev_handler_v1 function. I'll do and I post a V4. Best regards, Stefano
diff --git a/mongoose/Config.in b/mongoose/Config.in index a001247..29cac90 100644 --- a/mongoose/Config.in +++ b/mongoose/Config.in @@ -18,24 +18,6 @@ config MONGOOSE endchoice -choice - prompt "Web Application Interface" - default MONGOOSE_WEB_API_V1 - help - Choose the bootloader - -config MONGOOSE_WEB_API_V1 - bool "Version 1 (deprecated)" - help - Support for version 1 - -config MONGOOSE_WEB_API_V2 - bool "Version 2" - help - Support for version 2 - -endchoice - config MONGOOSEIPV6 bool "IPv6 support" default y diff --git a/mongoose/Makefile b/mongoose/Makefile index 77a616c..dc2d3d3 100644 --- a/mongoose/Makefile +++ b/mongoose/Makefile @@ -1,9 +1,7 @@ ifneq ($(CONFIG_WEBSERVER),) ifneq ($(CONFIG_MONGOOSE),) KBUILD_CFLAGS += -DMG_ENABLE_HTTP_STREAMING_MULTIPART=1 -ifneq ($(CONFIG_MONGOOSE_WEB_API_V2),) KBUILD_CFLAGS += -DMG_ENABLE_HTTP_WEBSOCKET=1 -DMG_ENABLE_THREADS=1 -endif ifneq ($(CONFIG_MONGOOSEIPV6),) KBUILD_CFLAGS += -DMG_ENABLE_IPV6=1 endif diff --git a/mongoose/mongoose_interface.c b/mongoose/mongoose_interface.c index 0e22671..9ef9070 100644 --- a/mongoose/mongoose_interface.c +++ b/mongoose/mongoose_interface.c @@ -32,6 +32,11 @@ #define MG_PORT "8080" #define MG_ROOT "." +enum MONGOOSE_API_VERSION { + MONGOOSE_API_V1, + MONGOOSE_API_V2 +}; + struct mongoose_options { char *root; char *listing; @@ -40,6 +45,7 @@ struct mongoose_options { char *ssl_cert; char *ssl_key; #endif + enum MONGOOSE_API_VERSION api_version; }; struct file_upload_state { @@ -50,7 +56,9 @@ struct file_upload_state { static struct mg_serve_http_opts s_http_server_opts; static void upload_handler(struct mg_connection *nc, int ev, void *p); -#if defined(CONFIG_MONGOOSE_WEB_API_V2) +/* + * These functions are for V2 of the protocol + */ #define enum_string(x) [x] = #x static const char *get_status_string(unsigned int status) { @@ -111,210 +119,7 @@ static size_t snescape(char *dst, size_t n, const char *src) return len; } -#endif - -#if defined(CONFIG_MONGOOSE_WEB_API_V1) -static void upload_handler_v1(struct mg_connection *nc, int ev, void *p) -{ - struct mg_str *filename, *data; - struct http_message *hm; - size_t length; - char buf[16]; - int fd; - - switch (ev) { - case MG_EV_HTTP_REQUEST: - hm = (struct http_message *) p; - - filename = mg_get_http_header(hm, "X_FILENAME"); - if (filename == NULL) { - mg_http_send_error(nc, 403, NULL); - return; - } - - data = mg_get_http_header(hm, "Content-length"); - if (data == NULL || data->len >= ARRAY_SIZE(buf)) { - mg_http_send_error(nc, 403, NULL); - return; - } - - memcpy(buf, data->p, data->len); - buf[data->len] = '\0'; - length = strtoul(data->p, NULL, 10); - if (length == 0) { - mg_http_send_error(nc, 403, NULL); - return; - } - - fd = ipc_inst_start_ext(SOURCE_WEBSERVER, filename->len, filename->p); - ipc_send_data(fd, (char *) hm->body.p, hm->body.len); - ipc_end(fd); - - mg_send_response_line(nc, 200, - "Content-Type: text/plain\r\n" - "Connection: close"); - mg_send(nc, "\r\n", 2); - mg_printf(nc, "Ok, %.*s - %d bytes.\r\n", (int) filename->len, filename->p, (int) length); - nc->flags |= MG_F_SEND_AND_CLOSE; - break; - default: - upload_handler(nc, ev, p); - break; - } -} -#endif - -static void upload_handler(struct mg_connection *nc, int ev, void *p) -{ - struct mg_http_multipart_part *mp; - struct file_upload_state *fus; - - switch (ev) { - case MG_EV_HTTP_PART_BEGIN: - mp = (struct mg_http_multipart_part *) p; - - fus = (struct file_upload_state *) calloc(1, sizeof(*fus)); - if (fus == NULL) { - mg_http_send_error(nc, 500, "Out of memory"); - break; - } - - fus->fd = ipc_inst_start_ext(SOURCE_WEBSERVER, strlen(mp->file_name), mp->file_name); - if (fus->fd < 0) { - mg_http_send_error(nc, 500, "Failed to queue command"); - free(fus); - break; - } - - mp->user_data = fus; - - break; - - case MG_EV_HTTP_PART_DATA: - mp = (struct mg_http_multipart_part *) p; - fus = (struct file_upload_state *) mp->user_data; - - if (!fus) - break; - - ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len); - fus->len += mp->data.len; - - break; - - case MG_EV_HTTP_PART_END: - mp = (struct mg_http_multipart_part *) p; - fus = (struct file_upload_state *) mp->user_data; - - if (!fus) - break; - - ipc_end(fus->fd); - - mg_send_response_line(nc, 200, - "Content-Type: text/plain\r\n" - "Connection: close"); - mg_send(nc, "\r\n", 2); - mg_printf(nc, "Ok, %s - %d bytes.\r\n", mp->file_name, (int) fus->len); - nc->flags |= MG_F_SEND_AND_CLOSE; - - mp->user_data = NULL; - free(fus); - break; - } -} -#if defined(CONFIG_MONGOOSE_WEB_API_V1) -static void recovery_status(struct mg_connection *nc, int ev, void *ev_data) -{ - ipc_message ipc; - int ret; - char buf[4096]; - - (void)ev; - (void)ev_data; - - ret = ipc_get_status(&ipc); - - if (ret) { - mg_http_send_error(nc, 500, NULL); - return; - } - - snprintf(buf, sizeof(buf), - "{\r\n" - "\t\"Status\" : \"%d\",\r\n" - "\t\"Msg\" : \"%s\",\r\n" - "\t\"Error\" : \"%d\",\r\n" - "\t\"LastResult\" : \"%d\"\r\n" - "}\r\n", - ipc.data.status.current, - strlen(ipc.data.status.desc) ? ipc.data.status.desc : "", - ipc.data.status.error, - ipc.data.status.last_result); - - mg_send_head(nc, 200, strlen(buf), - "Cache: no-cache\r\n" - "Content-Type: text/plain"); - - mg_send(nc, buf, strlen(buf)); - - nc->flags |= MG_F_SEND_AND_CLOSE; -} - -static void reboot_target(struct mg_connection *nc, int ev, void *ev_data) -{ - struct http_message *hm = (struct http_message *) ev_data; - int ret; - - (void)ev; - - if(mg_vcasecmp(&hm->method, "POST") == 0) { - ret = system("reboot"); - if (ret) { - mg_http_send_error(nc, 500, - "Device cannot be reboot, internal fault."); - return; - } - - mg_http_send_error(nc, 200, "Device will reboot now."); - } - else { - mg_send_response_line(nc, 200, - "Content-Type: text/html\r\n" - "Connection: close"); - mg_send(nc, "\r\n", 2); - mg_printf(nc, - "<form method='POST' action=''>" - "<input type='submit' value='Reboot'>" - "</form>"); - nc->flags |= MG_F_SEND_AND_CLOSE; - } -} - -static void post_update_cmd(struct mg_connection *nc, int ev, void *ev_data) -{ - ipc_message msg = {}; - - (void)ev; - (void)ev_data; - - int ret = ipc_postupdate(&msg); - mg_send_response_line(nc, 200, "Content-Type: application/json"); - mg_send(nc, "\r\n", 2); - mg_printf(nc, - "{\r\n" - "\t\"code\": %d,\r\n" - "\t\"error\": \"%s\",\r\n" - "\t\"detail\": \"%s\"\r\n" - "}", - (ret == 0) ? 200 : 501, - (ret == 0) ? "" : "Internal server error", - (ret == 0) ? "" : "Failed to queue command"); - - nc->flags |= MG_F_SEND_AND_CLOSE; -} -#elif defined(CONFIG_MONGOOSE_WEB_API_V2) static void restart_handler(struct mg_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; @@ -470,7 +275,158 @@ static void *broadcast_progress_thread(void *data) return NULL; } -#endif + +/* + * These functions are for V1 of the protocol + */ +static void upload_handler_v1(struct mg_connection *nc, int ev, void *p) +{ + struct mg_str *filename, *data; + struct http_message *hm; + size_t length; + char buf[16]; + int fd; + + switch (ev) { + case MG_EV_HTTP_REQUEST: + hm = (struct http_message *) p; + + filename = mg_get_http_header(hm, "X_FILENAME"); + if (filename == NULL) { + mg_http_send_error(nc, 403, NULL); + return; + } + + data = mg_get_http_header(hm, "Content-length"); + if (data == NULL || data->len >= ARRAY_SIZE(buf)) { + mg_http_send_error(nc, 403, NULL); + return; + } + + memcpy(buf, data->p, data->len); + buf[data->len] = '\0'; + length = strtoul(data->p, NULL, 10); + if (length == 0) { + mg_http_send_error(nc, 403, NULL); + return; + } + + fd = ipc_inst_start_ext(SOURCE_WEBSERVER, filename->len, filename->p); + ipc_send_data(fd, (char *) hm->body.p, hm->body.len); + ipc_end(fd); + + mg_send_response_line(nc, 200, + "Content-Type: text/plain\r\n" + "Connection: close"); + mg_send(nc, "\r\n", 2); + mg_printf(nc, "Ok, %.*s - %d bytes.\r\n", (int) filename->len, filename->p, (int) length); + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + default: + upload_handler(nc, ev, p); + break; + } +} + +static void recovery_status(struct mg_connection *nc, int ev, void *ev_data) +{ + ipc_message ipc; + int ret; + char buf[4096]; + + (void)ev; + (void)ev_data; + + ret = ipc_get_status(&ipc); + + if (ret) { + mg_http_send_error(nc, 500, NULL); + return; + } + + snprintf(buf, sizeof(buf), + "{\r\n" + "\t\"Status\" : \"%d\",\r\n" + "\t\"Msg\" : \"%s\",\r\n" + "\t\"Error\" : \"%d\",\r\n" + "\t\"LastResult\" : \"%d\"\r\n" + "}\r\n", + ipc.data.status.current, + strlen(ipc.data.status.desc) ? ipc.data.status.desc : "", + ipc.data.status.error, + ipc.data.status.last_result); + + mg_send_head(nc, 200, strlen(buf), + "Cache: no-cache\r\n" + "Content-Type: text/plain"); + + mg_send(nc, buf, strlen(buf)); + + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +/* + * Code common to V1 and V2 + */ +static void upload_handler(struct mg_connection *nc, int ev, void *p) +{ + struct mg_http_multipart_part *mp; + struct file_upload_state *fus; + + switch (ev) { + case MG_EV_HTTP_PART_BEGIN: + mp = (struct mg_http_multipart_part *) p; + + fus = (struct file_upload_state *) calloc(1, sizeof(*fus)); + if (fus == NULL) { + mg_http_send_error(nc, 500, "Out of memory"); + break; + } + + fus->fd = ipc_inst_start_ext(SOURCE_WEBSERVER, strlen(mp->file_name), mp->file_name); + if (fus->fd < 0) { + mg_http_send_error(nc, 500, "Failed to queue command"); + free(fus); + break; + } + + mp->user_data = fus; + + break; + + case MG_EV_HTTP_PART_DATA: + mp = (struct mg_http_multipart_part *) p; + fus = (struct file_upload_state *) mp->user_data; + + if (!fus) + break; + + ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len); + fus->len += mp->data.len; + + break; + + case MG_EV_HTTP_PART_END: + mp = (struct mg_http_multipart_part *) p; + fus = (struct file_upload_state *) mp->user_data; + + if (!fus) + break; + + ipc_end(fus->fd); + + mg_send_response_line(nc, 200, + "Content-Type: text/plain\r\n" + "Connection: close"); + mg_send(nc, "\r\n", 2); + mg_printf(nc, "Ok, %s - %d bytes.\r\n", mp->file_name, (int) fus->len); + nc->flags |= MG_F_SEND_AND_CLOSE; + + mp->user_data = NULL; + free(fus); + break; + } +} static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { @@ -510,6 +466,12 @@ static int mongoose_settings(void *elem, void __attribute__ ((__unused__)) *dat opts->ssl_key = strdup(tmp); } #endif + GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "api", tmp); + if (strlen(tmp)) { + opts->api_version = (!strcmp(tmp, "1")) ? + MONGOOSE_API_V1 : + MONGOOSE_API_V2; + } return 0; } @@ -523,6 +485,7 @@ static struct option long_options[] = { {"ssl-key", required_argument, NULL, 'K'}, #endif {"document-root", required_argument, NULL, 'r'}, + {"api-version", required_argument, NULL, 'a'}, {NULL, 0, NULL, 0} }; @@ -538,7 +501,8 @@ void mongoose_print_help(void) "\t -C, --ssl-cert <cert> : ssl certificate to present to clients\n" "\t -K, --ssl-key <key> : key corresponding to the ssl certificate\n" #endif - "\t -r, --document-root <path> : path to document root directory (default: %s)\n", + "\t -r, --document-root <path> : path to document root directory (default: %s)\n" + "\t -a, --api-version [1|2] : set Web protocol API to v1 (legacy) or v2 (default v2)\n", MG_LISTING, MG_PORT, MG_ROOT); } @@ -556,12 +520,14 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) int choice = 0; memset(&opts, 0, sizeof(opts)); + /* Set default API version */ + opts.api_version = MONGOOSE_API_V2; if (cfgfname) { read_module_settings(cfgfname, "webserver", mongoose_settings, &opts); } optind = 1; - while ((choice = getopt_long(argc, argv, "lp:sC:K:r:", + while ((choice = getopt_long(argc, argv, "lp:sC:K:r:a:", long_options, NULL)) != -1) { switch (choice) { case 'l': @@ -589,6 +555,11 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) free(opts.root); opts.root = strdup(optarg); break; + case 'a': + opts.api_version = (!strcmp(optarg, "1")) ? + MONGOOSE_API_V1 : + MONGOOSE_API_V2; + break; case '?': default: return -EINVAL; @@ -619,22 +590,23 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) } mg_set_protocol_http_websocket(nc); - mg_register_http_endpoint(nc, "/upload", MG_CB(upload_handler, NULL)); -#if defined(CONFIG_MONGOOSE_WEB_API_V1) - mg_register_http_endpoint(nc, "/handle_post_request", MG_CB(upload_handler_v1, NULL)); - mg_register_http_endpoint(nc, "/getstatus.json", MG_CB(recovery_status, NULL)); - mg_register_http_endpoint(nc, "/rebootTarget", MG_CB(reboot_target, NULL)); - mg_register_http_endpoint(nc, "/postUpdateCommand", MG_CB(post_update_cmd, NULL)); -#endif -#if defined(CONFIG_MONGOOSE_WEB_API_V2) - mg_register_http_endpoint(nc, "/restart", restart_handler); - mg_start_thread(broadcast_message_thread, &mgr); - mg_start_thread(broadcast_progress_thread, &mgr); -#endif + switch (opts.api_version) { + case MONGOOSE_API_V1: + mg_register_http_endpoint(nc, "/handle_post_request", MG_CB(upload_handler_v1, NULL)); + mg_register_http_endpoint(nc, "/getstatus.json", MG_CB(recovery_status, NULL)); + break; + case MONGOOSE_API_V2: + mg_register_http_endpoint(nc, "/restart", restart_handler); + mg_register_http_endpoint(nc, "/upload", MG_CB(upload_handler, NULL)); + mg_start_thread(broadcast_message_thread, &mgr); + mg_start_thread(broadcast_progress_thread, &mgr); + break; + } - printf("Mongoose web server version %s with pid %d started on port(s) %s with web root [%s]\n", + printf("Mongoose web server version %s with pid %d started on port(s) %s with web root [%s] and API %s\n", MG_VERSION, getpid(), s_http_port, - s_http_server_opts.document_root); + s_http_server_opts.document_root, + (opts.api_version == MONGOOSE_API_V1) ? "v1" : "v2"); for (;;) { mg_mgr_poll(&mgr, 100);
V1 version of REST-API is still more suitable for M2M updates together with the SWU forwarder handler. However, it is not currently possible to switch between them and the API must be selected at compile time, and this requires different binaries. Let the user decides which version of API should run with a command line parameter. Signed-off-by: Stefano Babic <sbabic@denx.de> CC: Sami Hartikainen <sami.hartikainen@teleste.com> --- Changes since V2: - use enum for API - drop V1 website support and let just the two URLs: handle_post_request getstatus.json this to guarantee the compatibility with SWU forwarder and proprietary applications using (or abusing of..) the REST-API For the Webserver itself, just V2 should be used. Changes since V1: - Drop MONGOOSE_WEB_API_V1 and MONGOOSE_WEB_API_V2 - Add help for "api" - Add "api" parameter to configuration file - "/upload" URL just in case of v2 - print API version when Webserver is started mongoose/Config.in | 18 -- mongoose/Makefile | 2 - mongoose/mongoose_interface.c | 414 ++++++++++++++++++++---------------------- 3 files changed, 193 insertions(+), 241 deletions(-)