diff mbox series

[2/2] mongoose: set V1 or V2 at runtime

Message ID 1520243058-10526-2-git-send-email-sbabic@denx.de
State Changes Requested
Headers show
Series None | expand

Commit Message

Stefano Babic March 5, 2018, 9:44 a.m. UTC
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>
---
 mongoose/Config.in            |  16 +-
 mongoose/Makefile             |   2 -
 mongoose/mongoose_interface.c | 449 ++++++++++++++++++++++--------------------
 3 files changed, 237 insertions(+), 230 deletions(-)

Comments

Hartikainen, Sami March 5, 2018, 10:56 a.m. UTC | #1
Hi Stefano,

> From: Stefano Babic [mailto:sbabic@denx.de]
> Sent: maanantai 5. maaliskuuta 2018 11.44
> To: swupdate@googlegroups.com
> Cc: Stefano Babic <sbabic@denx.de>; Hartikainen, Sami
> <Sami.Hartikainen@teleste.com>
> Subject: [PATCH 2/2] mongoose: set V1 or V2 at runtime
> 
> 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>
> ---
>  mongoose/Config.in            |  16 +-
>  mongoose/Makefile             |   2 -
>  mongoose/mongoose_interface.c | 449 ++++++++++++++++++++++---------
> -----------
>  3 files changed, 237 insertions(+), 230 deletions(-)
> 
> diff --git a/mongoose/Config.in b/mongoose/Config.in
> index a001247..72fb010 100644
> --- a/mongoose/Config.in
> +++ b/mongoose/Config.in
> @@ -18,23 +18,11 @@ 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
> +	bool
> 
>  config MONGOOSE_WEB_API_V2
> -	bool "Version 2"
> -	help
> -	  Support for version 2
> -
> -endchoice
> +	bool

Perhaps Kconfig symbols MONGOOSE_WEB_API_V1 and MONGOOSE_WEB_API_V2
should be dropped? Or what is their purpose now?

>  config MONGOOSEIPV6
>  	bool "IPv6 support"
> 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 423a2dc..e3cd9a1 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
> +	int api_version;
>  };
> 
>  struct file_upload_state {
> @@ -48,8 +54,11 @@ 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)
>  {
> @@ -110,9 +119,166 @@ static size_t snescape(char *dst, size_t n, const char
> *src)
> 
>  	return len;
>  }
> -#endif
> 
> -#if defined(CONFIG_MONGOOSE_WEB_API_V1)
> +static void restart_handler(struct mg_connection *nc, int ev, void
> *ev_data)
> +{
> +	struct http_message *hm = (struct http_message *) ev_data;
> +	ipc_message msg = {};
> +
> +	(void)ev;
> +
> +	if(mg_vcasecmp(&hm->method, "POST") != 0) {
> +		mg_http_send_error(nc, 405, "Method Not Allowed");
> +		return;
> +	}
> +
> +	int ret = ipc_postupdate(&msg);
> +	if (ret) {
> +		mg_http_send_error(nc, 500, "Failed to queue command");
> +		return;
> +	}
> +
> +	mg_http_send_error(nc, 201, "Device will reboot now.");
> +}
> +
> +static void broadcast_callback(struct mg_connection *nc, int ev, void
> *ev_data)
> +{
> +	char *buf = (char *) ev_data;
> +
> +	if (ev != MG_EV_POLL)
> +		return;
> +
> +	if (!(nc->flags & MG_F_IS_WEBSOCKET))
> +		return;
> +
> +	mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf,
> strlen(buf));
> +}
> +
> +static void broadcast(struct mg_mgr *mgr, char *str)
> +{
> +	mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1);
> +}
> +
> +static void *broadcast_message_thread(void *data)
> +{
> +	for (;;) {
> +		ipc_message msg;
> +		int ret = ipc_get_status(&msg);
> +
> +		if (!ret && strlen(msg.data.status.desc) != 0) {
> +			struct mg_mgr *mgr = (struct mg_mgr *) data;
> +			char text[4096];
> +			char str[4160];
> +
> +			snescape(text, sizeof(text), msg.data.status.desc);
> +
> +			snprintf(str, sizeof(str),
> +				"{\r\n"
> +				"\t\"type\": \"message\",\r\n"
> +				"\t\"level\": \"%d\",\r\n"
> +				"\t\"text\": \"%s\"\r\n"
> +				"}\r\n",
> +				(msg.data.status.error) ? 3 : 6, /* RFC 5424 */
> +				text);
> +
> +			broadcast(mgr, str);
> +			continue;
> +		}
> +
> +		usleep(50 * 1000);
> +	}
> +
> +	return NULL;
> +}
> +
> +static void *broadcast_progress_thread(void *data)
> +{
> +	int status = -1;
> +	int source = -1;
> +	int step = 0;
> +	int percent = 0;
> +	int fd = -1;
> +
> +	for (;;) {
> +		struct mg_mgr *mgr = (struct mg_mgr *) data;
> +		struct progress_msg msg;
> +		char str[512];
> +		int ret;
> +
> +		if (fd < 0)
> +			fd = progress_ipc_connect(true);
> +
> +		ret = progress_ipc_receive(&fd, &msg);
> +		if (ret != sizeof(msg))
> +			return NULL;
> +
> +		if (msg.status != status || msg.status == FAILURE) {
> +			status = msg.status;
> +
> +			snprintf(str, sizeof(str),
> +				"{\r\n"
> +				"\t\"type\": \"status\",\r\n"
> +				"\t\"status\": \"%s\"\r\n"
> +				"}\r\n",
> +				get_status_string(msg.status));
> +			broadcast(mgr, str);
> +		}
> +
> +		if (msg.source != source) {
> +			source = msg.source;
> +
> +			snprintf(str, sizeof(str),
> +				"{\r\n"
> +				"\t\"type\": \"source\",\r\n"
> +				"\t\"source\": \"%s\"\r\n"
> +				"}\r\n",
> +				get_source_string(msg.source));
> +			broadcast(mgr, str);
> +		}
> +
> +		if (msg.status == SUCCESS && msg.source ==
> SOURCE_WEBSERVER) {
> +			ipc_message ipc = {};
> +
> +			ipc_postupdate(&ipc);
> +		}
> +
> +		if (msg.infolen) {
> +			snprintf(str, sizeof(str),
> +				"{\r\n"
> +				"\t\"type\": \"info\",\r\n"
> +				"\t\"source\": \"%s\"\r\n"
> +				"}\r\n",
> +				msg.info);
> +			broadcast(mgr, str);
> +		}
> +
> +		if ((msg.cur_step != step || msg.cur_percent != percent) &&
> +				msg.cur_step) {
> +			step = msg.cur_step;
> +			percent = msg.cur_percent;
> +
> +			snprintf(str, sizeof(str),
> +				"{\r\n"
> +				"\t\"type\": \"step\",\r\n"
> +				"\t\"number\": \"%d\",\r\n"
> +				"\t\"step\": \"%d\",\r\n"
> +				"\t\"name\": \"%s\",\r\n"
> +				"\t\"percent\": \"%d\"\r\n"
> +				"}\r\n",
> +				msg.nsteps,
> +				msg.cur_step,
> +				msg.cur_step ? msg.cur_image: "",
> +				msg.cur_percent);
> +			broadcast(mgr, str);
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * 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;
> @@ -161,69 +327,7 @@ static void upload_handler_v1(struct mg_connection
> *nc, int ev, void *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;
> @@ -313,163 +417,70 @@ static void post_update_cmd(struct
> mg_connection *nc, int ev, void *ev_data)
> 
>  	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;
> -	ipc_message msg = {};
> -
> -	(void)ev;
> -
> -	if(mg_vcasecmp(&hm->method, "POST") != 0) {
> -		mg_http_send_error(nc, 405, "Method Not Allowed");
> -		return;
> -	}
> -
> -	int ret = ipc_postupdate(&msg);
> -	if (ret) {
> -		mg_http_send_error(nc, 500, "Failed to queue command");
> -		return;
> -	}
> -
> -	mg_http_send_error(nc, 201, "Device will reboot now.");
> -}
> -
> -static void broadcast_callback(struct mg_connection *nc, int ev, void
> *ev_data)
> -{
> -	char *buf = (char *) ev_data;
> -
> -	if (ev != MG_EV_POLL)
> -		return;
> -
> -	if (!(nc->flags & MG_F_IS_WEBSOCKET))
> -		return;
> -
> -	mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf,
> strlen(buf));
> -}
> 
> -static void broadcast(struct mg_mgr *mgr, char *str)
> -{
> -	mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1);
> -}
> 
> -static void *broadcast_message_thread(void *data)
> +/*
> + * Code common to V1 and V2
> + */
> +static void upload_handler(struct mg_connection *nc, int ev, void *p)
>  {
> -	for (;;) {
> -		ipc_message msg;
> -		int ret = ipc_get_status(&msg);
> -
> -		if (!ret && strlen(msg.data.status.desc) != 0) {
> -			struct mg_mgr *mgr = (struct mg_mgr *) data;
> -			char text[4096];
> -			char str[4160];
> -
> -			snescape(text, sizeof(text), msg.data.status.desc);
> +	struct mg_http_multipart_part *mp;
> +	struct file_upload_state *fus;
> 
> -			snprintf(str, sizeof(str),
> -				"{\r\n"
> -				"\t\"type\": \"message\",\r\n"
> -				"\t\"level\": \"%d\",\r\n"
> -				"\t\"text\": \"%s\"\r\n"
> -				"}\r\n",
> -				(msg.data.status.error) ? 3 : 6, /* RFC 5424 */
> -				text);
> +	switch (ev) {
> +	case MG_EV_HTTP_PART_BEGIN:
> +		mp = (struct mg_http_multipart_part *) p;
> 
> -			broadcast(mgr, str);
> -			continue;
> +		fus = (struct file_upload_state *) calloc(1, sizeof(*fus));
> +		if (fus == NULL) {
> +			mg_http_send_error(nc, 500, "Out of memory");
> +			break;
>  		}
> 
> -		usleep(50 * 1000);
> -	}
> -
> -	return NULL;
> -}
> -
> -static void *broadcast_progress_thread(void *data)
> -{
> -	int status = -1;
> -	int source = -1;
> -	int step = 0;
> -	int percent = 0;
> -	int fd = -1;
> -
> -	for (;;) {
> -		struct mg_mgr *mgr = (struct mg_mgr *) data;
> -		struct progress_msg msg;
> -		char str[512];
> -		int ret;
> +		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;
> +		}
> 
> -		if (fd < 0)
> -			fd = progress_ipc_connect(true);
> +		mp->user_data = fus;
> 
> -		ret = progress_ipc_receive(&fd, &msg);
> -		if (ret != sizeof(msg))
> -			return NULL;
> +		break;
> 
> -		if (msg.status != status || msg.status == FAILURE) {
> -			status = msg.status;
> +	case MG_EV_HTTP_PART_DATA:
> +		mp = (struct mg_http_multipart_part *) p;
> +		fus = (struct file_upload_state *) mp->user_data;
> 
> -			snprintf(str, sizeof(str),
> -				"{\r\n"
> -				"\t\"type\": \"status\",\r\n"
> -				"\t\"status\": \"%s\"\r\n"
> -				"}\r\n",
> -				get_status_string(msg.status));
> -			broadcast(mgr, str);
> -		}
> +		if (!fus)
> +			break;
> 
> -		if (msg.source != source) {
> -			source = msg.source;
> +		ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len);
> +		fus->len += mp->data.len;
> 
> -			snprintf(str, sizeof(str),
> -				"{\r\n"
> -				"\t\"type\": \"source\",\r\n"
> -				"\t\"source\": \"%s\"\r\n"
> -				"}\r\n",
> -				get_source_string(msg.source));
> -			broadcast(mgr, str);
> -		}
> +		break;
> 
> -		if (msg.status == SUCCESS && msg.source ==
> SOURCE_WEBSERVER) {
> -			ipc_message ipc = {};
> +	case MG_EV_HTTP_PART_END:
> +		mp = (struct mg_http_multipart_part *) p;
> +		fus = (struct file_upload_state *) mp->user_data;
> 
> -			ipc_postupdate(&ipc);
> -		}
> +		if (!fus)
> +			break;
> 
> -		if (msg.infolen) {
> -			snprintf(str, sizeof(str),
> -				"{\r\n"
> -				"\t\"type\": \"info\",\r\n"
> -				"\t\"source\": \"%s\"\r\n"
> -				"}\r\n",
> -				msg.info);
> -			broadcast(mgr, str);
> -		}
> +		ipc_end(fus->fd);
> 
> -		if ((msg.cur_step != step || msg.cur_percent != percent) &&
> -				msg.cur_step) {
> -			step = msg.cur_step;
> -			percent = msg.cur_percent;
> +		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;
> 
> -			snprintf(str, sizeof(str),
> -				"{\r\n"
> -				"\t\"type\": \"step\",\r\n"
> -				"\t\"number\": \"%d\",\r\n"
> -				"\t\"step\": \"%d\",\r\n"
> -				"\t\"name\": \"%s\",\r\n"
> -				"\t\"percent\": \"%d\"\r\n"
> -				"}\r\n",
> -				msg.nsteps,
> -				msg.cur_step,
> -				msg.cur_step ? msg.cur_image: "",
> -				msg.cur_percent);
> -			broadcast(mgr, str);
> -		}
> +		mp->user_data = NULL;
> +		free(fus);
> +		break;
>  	}
> -
> -	return NULL;
>  }
> -#endif
> 
>  static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
>  {
> @@ -522,6 +533,7 @@ static struct option long_options[] = {
>  	{"ssl-key", required_argument, NULL, 'K'},
>  #endif
>  	{"document-root", required_argument, NULL, 'r'},
> +	{"api", required_argument, NULL, 'a'},
>  	{NULL, 0, NULL, 0}
>  };
> 

The new "api" option documentation is missing from the following mongoose_print_help
function.

While at it, please add support for choosing the api via config file option (in the mongoose_settings
function).

> @@ -555,12 +567,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':
> @@ -588,6 +602,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, "v1")) ?
> +						MONGOOSE_API_V1 :
> +						MONGOOSE_API_V2;
> +			break;
>  		case '?':
>  		default:
>  			return -EINVAL;
> @@ -619,17 +638,19 @@ 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));

The "/upload" handler should be registered for api V2.
 
> -#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));
> +		mg_register_http_endpoint(nc, "/rebootTarget",
> MG_CB(reboot_target, NULL));
> +		mg_register_http_endpoint(nc, "/postUpdateCommand",
> MG_CB(post_update_cmd, NULL));
> +		break;
> +	case MONGOOSE_API_V2:
> +		mg_register_http_endpoint(nc, "/restart", restart_handler);
> +		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",
>  		MG_VERSION, getpid(), s_http_port,
> --
> 2.7.4

Br, Sami
Stefano Babic March 5, 2018, 11:07 a.m. UTC | #2
On 05/03/2018 11:56, Sami.Hartikainen@teleste.com wrote:
> Hi Stefano,
> 
>> From: Stefano Babic [mailto:sbabic@denx.de]
>> Sent: maanantai 5. maaliskuuta 2018 11.44
>> To: swupdate@googlegroups.com
>> Cc: Stefano Babic <sbabic@denx.de>; Hartikainen, Sami
>> <Sami.Hartikainen@teleste.com>
>> Subject: [PATCH 2/2] mongoose: set V1 or V2 at runtime
>>
>> 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>
>> ---
>>  mongoose/Config.in            |  16 +-
>>  mongoose/Makefile             |   2 -
>>  mongoose/mongoose_interface.c | 449 ++++++++++++++++++++++---------
>> -----------
>>  3 files changed, 237 insertions(+), 230 deletions(-)
>>
>> diff --git a/mongoose/Config.in b/mongoose/Config.in
>> index a001247..72fb010 100644
>> --- a/mongoose/Config.in
>> +++ b/mongoose/Config.in
>> @@ -18,23 +18,11 @@ 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
>> +	bool
>>
>>  config MONGOOSE_WEB_API_V2
>> -	bool "Version 2"
>> -	help
>> -	  Support for version 2
>> -
>> -endchoice
>> +	bool
> 
> Perhaps Kconfig symbols MONGOOSE_WEB_API_V1 and MONGOOSE_WEB_API_V2
> should be dropped? Or what is their purpose now?

Just forgotten to drop them. Fix in V2.

> 
>>  config MONGOOSEIPV6
>>  	bool "IPv6 support"
>> 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 423a2dc..e3cd9a1 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
>> +	int api_version;
>>  };
>>
>>  struct file_upload_state {
>> @@ -48,8 +54,11 @@ 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)
>>  {
>> @@ -110,9 +119,166 @@ static size_t snescape(char *dst, size_t n, const char
>> *src)
>>
>>  	return len;
>>  }
>> -#endif
>>
>> -#if defined(CONFIG_MONGOOSE_WEB_API_V1)
>> +static void restart_handler(struct mg_connection *nc, int ev, void
>> *ev_data)
>> +{
>> +	struct http_message *hm = (struct http_message *) ev_data;
>> +	ipc_message msg = {};
>> +
>> +	(void)ev;
>> +
>> +	if(mg_vcasecmp(&hm->method, "POST") != 0) {
>> +		mg_http_send_error(nc, 405, "Method Not Allowed");
>> +		return;
>> +	}
>> +
>> +	int ret = ipc_postupdate(&msg);
>> +	if (ret) {
>> +		mg_http_send_error(nc, 500, "Failed to queue command");
>> +		return;
>> +	}
>> +
>> +	mg_http_send_error(nc, 201, "Device will reboot now.");
>> +}
>> +
>> +static void broadcast_callback(struct mg_connection *nc, int ev, void
>> *ev_data)
>> +{
>> +	char *buf = (char *) ev_data;
>> +
>> +	if (ev != MG_EV_POLL)
>> +		return;
>> +
>> +	if (!(nc->flags & MG_F_IS_WEBSOCKET))
>> +		return;
>> +
>> +	mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf,
>> strlen(buf));
>> +}
>> +
>> +static void broadcast(struct mg_mgr *mgr, char *str)
>> +{
>> +	mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1);
>> +}
>> +
>> +static void *broadcast_message_thread(void *data)
>> +{
>> +	for (;;) {
>> +		ipc_message msg;
>> +		int ret = ipc_get_status(&msg);
>> +
>> +		if (!ret && strlen(msg.data.status.desc) != 0) {
>> +			struct mg_mgr *mgr = (struct mg_mgr *) data;
>> +			char text[4096];
>> +			char str[4160];
>> +
>> +			snescape(text, sizeof(text), msg.data.status.desc);
>> +
>> +			snprintf(str, sizeof(str),
>> +				"{\r\n"
>> +				"\t\"type\": \"message\",\r\n"
>> +				"\t\"level\": \"%d\",\r\n"
>> +				"\t\"text\": \"%s\"\r\n"
>> +				"}\r\n",
>> +				(msg.data.status.error) ? 3 : 6, /* RFC 5424 */
>> +				text);
>> +
>> +			broadcast(mgr, str);
>> +			continue;
>> +		}
>> +
>> +		usleep(50 * 1000);
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +static void *broadcast_progress_thread(void *data)
>> +{
>> +	int status = -1;
>> +	int source = -1;
>> +	int step = 0;
>> +	int percent = 0;
>> +	int fd = -1;
>> +
>> +	for (;;) {
>> +		struct mg_mgr *mgr = (struct mg_mgr *) data;
>> +		struct progress_msg msg;
>> +		char str[512];
>> +		int ret;
>> +
>> +		if (fd < 0)
>> +			fd = progress_ipc_connect(true);
>> +
>> +		ret = progress_ipc_receive(&fd, &msg);
>> +		if (ret != sizeof(msg))
>> +			return NULL;
>> +
>> +		if (msg.status != status || msg.status == FAILURE) {
>> +			status = msg.status;
>> +
>> +			snprintf(str, sizeof(str),
>> +				"{\r\n"
>> +				"\t\"type\": \"status\",\r\n"
>> +				"\t\"status\": \"%s\"\r\n"
>> +				"}\r\n",
>> +				get_status_string(msg.status));
>> +			broadcast(mgr, str);
>> +		}
>> +
>> +		if (msg.source != source) {
>> +			source = msg.source;
>> +
>> +			snprintf(str, sizeof(str),
>> +				"{\r\n"
>> +				"\t\"type\": \"source\",\r\n"
>> +				"\t\"source\": \"%s\"\r\n"
>> +				"}\r\n",
>> +				get_source_string(msg.source));
>> +			broadcast(mgr, str);
>> +		}
>> +
>> +		if (msg.status == SUCCESS && msg.source ==
>> SOURCE_WEBSERVER) {
>> +			ipc_message ipc = {};
>> +
>> +			ipc_postupdate(&ipc);
>> +		}
>> +
>> +		if (msg.infolen) {
>> +			snprintf(str, sizeof(str),
>> +				"{\r\n"
>> +				"\t\"type\": \"info\",\r\n"
>> +				"\t\"source\": \"%s\"\r\n"
>> +				"}\r\n",
>> +				msg.info);
>> +			broadcast(mgr, str);
>> +		}
>> +
>> +		if ((msg.cur_step != step || msg.cur_percent != percent) &&
>> +				msg.cur_step) {
>> +			step = msg.cur_step;
>> +			percent = msg.cur_percent;
>> +
>> +			snprintf(str, sizeof(str),
>> +				"{\r\n"
>> +				"\t\"type\": \"step\",\r\n"
>> +				"\t\"number\": \"%d\",\r\n"
>> +				"\t\"step\": \"%d\",\r\n"
>> +				"\t\"name\": \"%s\",\r\n"
>> +				"\t\"percent\": \"%d\"\r\n"
>> +				"}\r\n",
>> +				msg.nsteps,
>> +				msg.cur_step,
>> +				msg.cur_step ? msg.cur_image: "",
>> +				msg.cur_percent);
>> +			broadcast(mgr, str);
>> +		}
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +/*
>> + * 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;
>> @@ -161,69 +327,7 @@ static void upload_handler_v1(struct mg_connection
>> *nc, int ev, void *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;
>> @@ -313,163 +417,70 @@ static void post_update_cmd(struct
>> mg_connection *nc, int ev, void *ev_data)
>>
>>  	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;
>> -	ipc_message msg = {};
>> -
>> -	(void)ev;
>> -
>> -	if(mg_vcasecmp(&hm->method, "POST") != 0) {
>> -		mg_http_send_error(nc, 405, "Method Not Allowed");
>> -		return;
>> -	}
>> -
>> -	int ret = ipc_postupdate(&msg);
>> -	if (ret) {
>> -		mg_http_send_error(nc, 500, "Failed to queue command");
>> -		return;
>> -	}
>> -
>> -	mg_http_send_error(nc, 201, "Device will reboot now.");
>> -}
>> -
>> -static void broadcast_callback(struct mg_connection *nc, int ev, void
>> *ev_data)
>> -{
>> -	char *buf = (char *) ev_data;
>> -
>> -	if (ev != MG_EV_POLL)
>> -		return;
>> -
>> -	if (!(nc->flags & MG_F_IS_WEBSOCKET))
>> -		return;
>> -
>> -	mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf,
>> strlen(buf));
>> -}
>>
>> -static void broadcast(struct mg_mgr *mgr, char *str)
>> -{
>> -	mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1);
>> -}
>>
>> -static void *broadcast_message_thread(void *data)
>> +/*
>> + * Code common to V1 and V2
>> + */
>> +static void upload_handler(struct mg_connection *nc, int ev, void *p)
>>  {
>> -	for (;;) {
>> -		ipc_message msg;
>> -		int ret = ipc_get_status(&msg);
>> -
>> -		if (!ret && strlen(msg.data.status.desc) != 0) {
>> -			struct mg_mgr *mgr = (struct mg_mgr *) data;
>> -			char text[4096];
>> -			char str[4160];
>> -
>> -			snescape(text, sizeof(text), msg.data.status.desc);
>> +	struct mg_http_multipart_part *mp;
>> +	struct file_upload_state *fus;
>>
>> -			snprintf(str, sizeof(str),
>> -				"{\r\n"
>> -				"\t\"type\": \"message\",\r\n"
>> -				"\t\"level\": \"%d\",\r\n"
>> -				"\t\"text\": \"%s\"\r\n"
>> -				"}\r\n",
>> -				(msg.data.status.error) ? 3 : 6, /* RFC 5424 */
>> -				text);
>> +	switch (ev) {
>> +	case MG_EV_HTTP_PART_BEGIN:
>> +		mp = (struct mg_http_multipart_part *) p;
>>
>> -			broadcast(mgr, str);
>> -			continue;
>> +		fus = (struct file_upload_state *) calloc(1, sizeof(*fus));
>> +		if (fus == NULL) {
>> +			mg_http_send_error(nc, 500, "Out of memory");
>> +			break;
>>  		}
>>
>> -		usleep(50 * 1000);
>> -	}
>> -
>> -	return NULL;
>> -}
>> -
>> -static void *broadcast_progress_thread(void *data)
>> -{
>> -	int status = -1;
>> -	int source = -1;
>> -	int step = 0;
>> -	int percent = 0;
>> -	int fd = -1;
>> -
>> -	for (;;) {
>> -		struct mg_mgr *mgr = (struct mg_mgr *) data;
>> -		struct progress_msg msg;
>> -		char str[512];
>> -		int ret;
>> +		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;
>> +		}
>>
>> -		if (fd < 0)
>> -			fd = progress_ipc_connect(true);
>> +		mp->user_data = fus;
>>
>> -		ret = progress_ipc_receive(&fd, &msg);
>> -		if (ret != sizeof(msg))
>> -			return NULL;
>> +		break;
>>
>> -		if (msg.status != status || msg.status == FAILURE) {
>> -			status = msg.status;
>> +	case MG_EV_HTTP_PART_DATA:
>> +		mp = (struct mg_http_multipart_part *) p;
>> +		fus = (struct file_upload_state *) mp->user_data;
>>
>> -			snprintf(str, sizeof(str),
>> -				"{\r\n"
>> -				"\t\"type\": \"status\",\r\n"
>> -				"\t\"status\": \"%s\"\r\n"
>> -				"}\r\n",
>> -				get_status_string(msg.status));
>> -			broadcast(mgr, str);
>> -		}
>> +		if (!fus)
>> +			break;
>>
>> -		if (msg.source != source) {
>> -			source = msg.source;
>> +		ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len);
>> +		fus->len += mp->data.len;
>>
>> -			snprintf(str, sizeof(str),
>> -				"{\r\n"
>> -				"\t\"type\": \"source\",\r\n"
>> -				"\t\"source\": \"%s\"\r\n"
>> -				"}\r\n",
>> -				get_source_string(msg.source));
>> -			broadcast(mgr, str);
>> -		}
>> +		break;
>>
>> -		if (msg.status == SUCCESS && msg.source ==
>> SOURCE_WEBSERVER) {
>> -			ipc_message ipc = {};
>> +	case MG_EV_HTTP_PART_END:
>> +		mp = (struct mg_http_multipart_part *) p;
>> +		fus = (struct file_upload_state *) mp->user_data;
>>
>> -			ipc_postupdate(&ipc);
>> -		}
>> +		if (!fus)
>> +			break;
>>
>> -		if (msg.infolen) {
>> -			snprintf(str, sizeof(str),
>> -				"{\r\n"
>> -				"\t\"type\": \"info\",\r\n"
>> -				"\t\"source\": \"%s\"\r\n"
>> -				"}\r\n",
>> -				msg.info);
>> -			broadcast(mgr, str);
>> -		}
>> +		ipc_end(fus->fd);
>>
>> -		if ((msg.cur_step != step || msg.cur_percent != percent) &&
>> -				msg.cur_step) {
>> -			step = msg.cur_step;
>> -			percent = msg.cur_percent;
>> +		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;
>>
>> -			snprintf(str, sizeof(str),
>> -				"{\r\n"
>> -				"\t\"type\": \"step\",\r\n"
>> -				"\t\"number\": \"%d\",\r\n"
>> -				"\t\"step\": \"%d\",\r\n"
>> -				"\t\"name\": \"%s\",\r\n"
>> -				"\t\"percent\": \"%d\"\r\n"
>> -				"}\r\n",
>> -				msg.nsteps,
>> -				msg.cur_step,
>> -				msg.cur_step ? msg.cur_image: "",
>> -				msg.cur_percent);
>> -			broadcast(mgr, str);
>> -		}
>> +		mp->user_data = NULL;
>> +		free(fus);
>> +		break;
>>  	}
>> -
>> -	return NULL;
>>  }
>> -#endif
>>
>>  static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
>>  {
>> @@ -522,6 +533,7 @@ static struct option long_options[] = {
>>  	{"ssl-key", required_argument, NULL, 'K'},
>>  #endif
>>  	{"document-root", required_argument, NULL, 'r'},
>> +	{"api", required_argument, NULL, 'a'},
>>  	{NULL, 0, NULL, 0}
>>  };
>>
> 
> The new "api" option documentation is missing from the following mongoose_print_help
> function.

ok

> 
> While at it, please add support for choosing the api via config file option (in the mongoose_settings
> function).

ok

> 
>> @@ -555,12 +567,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':
>> @@ -588,6 +602,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, "v1")) ?
>> +						MONGOOSE_API_V1 :
>> +						MONGOOSE_API_V2;
>> +			break;
>>  		case '?':
>>  		default:
>>  			return -EINVAL;
>> @@ -619,17 +638,19 @@ 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));
> 
> The "/upload" handler should be registered for api V2.

Fix in V2

>  
>> -#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));
>> +		mg_register_http_endpoint(nc, "/rebootTarget",
>> MG_CB(reboot_target, NULL));
>> +		mg_register_http_endpoint(nc, "/postUpdateCommand",
>> MG_CB(post_update_cmd, NULL));
>> +		break;
>> +	case MONGOOSE_API_V2:
>> +		mg_register_http_endpoint(nc, "/restart", restart_handler);
>> +		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",
>>  		MG_VERSION, getpid(), s_http_port,
>> --
>> 2.7.4
> 

Best regards,
Stefano Babic
Hartikainen, Sami March 5, 2018, 11:18 a.m. UTC | #3
Hi Stefano,

> >>  	printf("Mongoose web server version %s with pid %d started on
> >> port(s) %s with web root [%s]\n",
> >>  		MG_VERSION, getpid(), s_http_port,

Could the above startup message be extended to show the enabled API version?

Also, I shortly tested this patchset with direct connection and through reverse proxy; both
V1 and V2 appear to work as expected.

Have not tested with swuforwarder handler.

Br, Sami
diff mbox series

Patch

diff --git a/mongoose/Config.in b/mongoose/Config.in
index a001247..72fb010 100644
--- a/mongoose/Config.in
+++ b/mongoose/Config.in
@@ -18,23 +18,11 @@  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
+	bool
 
 config MONGOOSE_WEB_API_V2
-	bool "Version 2"
-	help
-	  Support for version 2
-
-endchoice
+	bool
 
 config MONGOOSEIPV6
 	bool "IPv6 support"
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 423a2dc..e3cd9a1 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
+	int api_version;
 };
 
 struct file_upload_state {
@@ -48,8 +54,11 @@  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)
 {
@@ -110,9 +119,166 @@  static size_t snescape(char *dst, size_t n, const char *src)
 
 	return len;
 }
-#endif
 
-#if defined(CONFIG_MONGOOSE_WEB_API_V1)
+static void restart_handler(struct mg_connection *nc, int ev, void *ev_data)
+{
+	struct http_message *hm = (struct http_message *) ev_data;
+	ipc_message msg = {};
+
+	(void)ev;
+
+	if(mg_vcasecmp(&hm->method, "POST") != 0) {
+		mg_http_send_error(nc, 405, "Method Not Allowed");
+		return;
+	}
+
+	int ret = ipc_postupdate(&msg);
+	if (ret) {
+		mg_http_send_error(nc, 500, "Failed to queue command");
+		return;
+	}
+
+	mg_http_send_error(nc, 201, "Device will reboot now.");
+}
+
+static void broadcast_callback(struct mg_connection *nc, int ev, void *ev_data)
+{
+	char *buf = (char *) ev_data;
+
+	if (ev != MG_EV_POLL)
+		return;
+
+	if (!(nc->flags & MG_F_IS_WEBSOCKET))
+		return;
+
+	mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf, strlen(buf));
+}
+
+static void broadcast(struct mg_mgr *mgr, char *str)
+{
+	mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1);
+}
+
+static void *broadcast_message_thread(void *data)
+{
+	for (;;) {
+		ipc_message msg;
+		int ret = ipc_get_status(&msg);
+
+		if (!ret && strlen(msg.data.status.desc) != 0) {
+			struct mg_mgr *mgr = (struct mg_mgr *) data;
+			char text[4096];
+			char str[4160];
+
+			snescape(text, sizeof(text), msg.data.status.desc);
+
+			snprintf(str, sizeof(str),
+				"{\r\n"
+				"\t\"type\": \"message\",\r\n"
+				"\t\"level\": \"%d\",\r\n"
+				"\t\"text\": \"%s\"\r\n"
+				"}\r\n",
+				(msg.data.status.error) ? 3 : 6, /* RFC 5424 */
+				text);
+
+			broadcast(mgr, str);
+			continue;
+		}
+
+		usleep(50 * 1000);
+	}
+
+	return NULL;
+}
+
+static void *broadcast_progress_thread(void *data)
+{
+	int status = -1;
+	int source = -1;
+	int step = 0;
+	int percent = 0;
+	int fd = -1;
+
+	for (;;) {
+		struct mg_mgr *mgr = (struct mg_mgr *) data;
+		struct progress_msg msg;
+		char str[512];
+		int ret;
+
+		if (fd < 0)
+			fd = progress_ipc_connect(true);
+
+		ret = progress_ipc_receive(&fd, &msg);
+		if (ret != sizeof(msg))
+			return NULL;
+
+		if (msg.status != status || msg.status == FAILURE) {
+			status = msg.status;
+
+			snprintf(str, sizeof(str),
+				"{\r\n"
+				"\t\"type\": \"status\",\r\n"
+				"\t\"status\": \"%s\"\r\n"
+				"}\r\n",
+				get_status_string(msg.status));
+			broadcast(mgr, str);
+		}
+
+		if (msg.source != source) {
+			source = msg.source;
+
+			snprintf(str, sizeof(str),
+				"{\r\n"
+				"\t\"type\": \"source\",\r\n"
+				"\t\"source\": \"%s\"\r\n"
+				"}\r\n",
+				get_source_string(msg.source));
+			broadcast(mgr, str);
+		}
+
+		if (msg.status == SUCCESS && msg.source == SOURCE_WEBSERVER) {
+			ipc_message ipc = {};
+
+			ipc_postupdate(&ipc);
+		}
+
+		if (msg.infolen) {
+			snprintf(str, sizeof(str),
+				"{\r\n"
+				"\t\"type\": \"info\",\r\n"
+				"\t\"source\": \"%s\"\r\n"
+				"}\r\n",
+				msg.info);
+			broadcast(mgr, str);
+		}
+
+		if ((msg.cur_step != step || msg.cur_percent != percent) &&
+				msg.cur_step) {
+			step = msg.cur_step;
+			percent = msg.cur_percent;
+
+			snprintf(str, sizeof(str),
+				"{\r\n"
+				"\t\"type\": \"step\",\r\n"
+				"\t\"number\": \"%d\",\r\n"
+				"\t\"step\": \"%d\",\r\n"
+				"\t\"name\": \"%s\",\r\n"
+				"\t\"percent\": \"%d\"\r\n"
+				"}\r\n",
+				msg.nsteps,
+				msg.cur_step,
+				msg.cur_step ? msg.cur_image: "",
+				msg.cur_percent);
+			broadcast(mgr, str);
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * 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;
@@ -161,69 +327,7 @@  static void upload_handler_v1(struct mg_connection *nc, int ev, void *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;
@@ -313,163 +417,70 @@  static void post_update_cmd(struct mg_connection *nc, int ev, void *ev_data)
 
 	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;
-	ipc_message msg = {};
-
-	(void)ev;
-
-	if(mg_vcasecmp(&hm->method, "POST") != 0) {
-		mg_http_send_error(nc, 405, "Method Not Allowed");
-		return;
-	}
-
-	int ret = ipc_postupdate(&msg);
-	if (ret) {
-		mg_http_send_error(nc, 500, "Failed to queue command");
-		return;
-	}
-
-	mg_http_send_error(nc, 201, "Device will reboot now.");
-}
-
-static void broadcast_callback(struct mg_connection *nc, int ev, void *ev_data)
-{
-	char *buf = (char *) ev_data;
-
-	if (ev != MG_EV_POLL)
-		return;
-
-	if (!(nc->flags & MG_F_IS_WEBSOCKET))
-		return;
-
-	mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf, strlen(buf));
-}
 
-static void broadcast(struct mg_mgr *mgr, char *str)
-{
-	mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1);
-}
 
-static void *broadcast_message_thread(void *data)
+/*
+ * Code common to V1 and V2
+ */
+static void upload_handler(struct mg_connection *nc, int ev, void *p)
 {
-	for (;;) {
-		ipc_message msg;
-		int ret = ipc_get_status(&msg);
-
-		if (!ret && strlen(msg.data.status.desc) != 0) {
-			struct mg_mgr *mgr = (struct mg_mgr *) data;
-			char text[4096];
-			char str[4160];
-
-			snescape(text, sizeof(text), msg.data.status.desc);
+	struct mg_http_multipart_part *mp;
+	struct file_upload_state *fus;
 
-			snprintf(str, sizeof(str),
-				"{\r\n"
-				"\t\"type\": \"message\",\r\n"
-				"\t\"level\": \"%d\",\r\n"
-				"\t\"text\": \"%s\"\r\n"
-				"}\r\n",
-				(msg.data.status.error) ? 3 : 6, /* RFC 5424 */
-				text);
+	switch (ev) {
+	case MG_EV_HTTP_PART_BEGIN:
+		mp = (struct mg_http_multipart_part *) p;
 
-			broadcast(mgr, str);
-			continue;
+		fus = (struct file_upload_state *) calloc(1, sizeof(*fus));
+		if (fus == NULL) {
+			mg_http_send_error(nc, 500, "Out of memory");
+			break;
 		}
 
-		usleep(50 * 1000);
-	}
-
-	return NULL;
-}
-
-static void *broadcast_progress_thread(void *data)
-{
-	int status = -1;
-	int source = -1;
-	int step = 0;
-	int percent = 0;
-	int fd = -1;
-
-	for (;;) {
-		struct mg_mgr *mgr = (struct mg_mgr *) data;
-		struct progress_msg msg;
-		char str[512];
-		int ret;
+		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;
+		}
 
-		if (fd < 0)
-			fd = progress_ipc_connect(true);
+		mp->user_data = fus;
 
-		ret = progress_ipc_receive(&fd, &msg);
-		if (ret != sizeof(msg))
-			return NULL;
+		break;
 
-		if (msg.status != status || msg.status == FAILURE) {
-			status = msg.status;
+	case MG_EV_HTTP_PART_DATA:
+		mp = (struct mg_http_multipart_part *) p;
+		fus = (struct file_upload_state *) mp->user_data;
 
-			snprintf(str, sizeof(str),
-				"{\r\n"
-				"\t\"type\": \"status\",\r\n"
-				"\t\"status\": \"%s\"\r\n"
-				"}\r\n",
-				get_status_string(msg.status));
-			broadcast(mgr, str);
-		}
+		if (!fus)
+			break;
 
-		if (msg.source != source) {
-			source = msg.source;
+		ipc_send_data(fus->fd, (char *) mp->data.p, mp->data.len);
+		fus->len += mp->data.len;
 
-			snprintf(str, sizeof(str),
-				"{\r\n"
-				"\t\"type\": \"source\",\r\n"
-				"\t\"source\": \"%s\"\r\n"
-				"}\r\n",
-				get_source_string(msg.source));
-			broadcast(mgr, str);
-		}
+		break;
 
-		if (msg.status == SUCCESS && msg.source == SOURCE_WEBSERVER) {
-			ipc_message ipc = {};
+	case MG_EV_HTTP_PART_END:
+		mp = (struct mg_http_multipart_part *) p;
+		fus = (struct file_upload_state *) mp->user_data;
 
-			ipc_postupdate(&ipc);
-		}
+		if (!fus)
+			break;
 
-		if (msg.infolen) {
-			snprintf(str, sizeof(str),
-				"{\r\n"
-				"\t\"type\": \"info\",\r\n"
-				"\t\"source\": \"%s\"\r\n"
-				"}\r\n",
-				msg.info);
-			broadcast(mgr, str);
-		}
+		ipc_end(fus->fd);
 
-		if ((msg.cur_step != step || msg.cur_percent != percent) &&
-				msg.cur_step) {
-			step = msg.cur_step;
-			percent = msg.cur_percent;
+		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;
 
-			snprintf(str, sizeof(str),
-				"{\r\n"
-				"\t\"type\": \"step\",\r\n"
-				"\t\"number\": \"%d\",\r\n"
-				"\t\"step\": \"%d\",\r\n"
-				"\t\"name\": \"%s\",\r\n"
-				"\t\"percent\": \"%d\"\r\n"
-				"}\r\n",
-				msg.nsteps,
-				msg.cur_step,
-				msg.cur_step ? msg.cur_image: "",
-				msg.cur_percent);
-			broadcast(mgr, str);
-		}
+		mp->user_data = NULL;
+		free(fus);
+		break;
 	}
-
-	return NULL;
 }
-#endif
 
 static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
 {
@@ -522,6 +533,7 @@  static struct option long_options[] = {
 	{"ssl-key", required_argument, NULL, 'K'},
 #endif
 	{"document-root", required_argument, NULL, 'r'},
+	{"api", required_argument, NULL, 'a'},
 	{NULL, 0, NULL, 0}
 };
 
@@ -555,12 +567,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':
@@ -588,6 +602,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, "v1")) ?
+						MONGOOSE_API_V1 :
+						MONGOOSE_API_V2;
+			break;
 		case '?':
 		default:
 			return -EINVAL;
@@ -619,17 +638,19 @@  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));
+		mg_register_http_endpoint(nc, "/rebootTarget", MG_CB(reboot_target, NULL));
+		mg_register_http_endpoint(nc, "/postUpdateCommand", MG_CB(post_update_cmd, NULL));
+		break;
+	case MONGOOSE_API_V2:
+		mg_register_http_endpoint(nc, "/restart", restart_handler);
+		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",
 		MG_VERSION, getpid(), s_http_port,