This is an example web-server implementation. It can be used for files
uploading to u-boot using a web-browser. It acts much like tftpget, but no
special servers needs to be installed by the user.

This code can be used as a base for other implementations like firmware
upgrade web-server used by some vendors.

Usage:
  u-boot: start the we-server using the "httpd_upload" command
  PC:     open the "http://your_uboot_ip"; link in the browser

Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
Reviewed-by: Simon Glass <s...@chromium.org>
---
 cmd/Kconfig                |  25 ++++++
 cmd/net.c                  |  20 +++++
 include/net/httpd-upload.h |  12 +++
 net/Makefile               |   1 +
 net/httpd-upload.c         | 170 +++++++++++++++++++++++++++++++++++++
 5 files changed, 228 insertions(+)
 create mode 100644 include/net/httpd-upload.h
 create mode 100644 net/httpd-upload.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index d142a376641..aec66244375 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2041,6 +2041,31 @@ config CMD_NETCAT
          will be lost or reordered. Any netcat implementation should work,
          but openbsd one was tested only.
 
+config CMD_HTTPD_UPLOAD
+       bool "an example HTTP server for file uploading"
+       depends on HTTPD_COMMON
+       help
+         An example HTTP/1.1 compatible web-server implementation for file
+         uploading. It acts much like tftpboot command: put user data to a
+         specified memory location, but no special tools needs to be installed
+         on the user side. The only required tool is browser.
+
+         Start 'httpd_upload' command, open a browser, connect to the board IP,
+         select file to upload and press 'Upload' button. This is enougth.
+
+         There is no big profit from this code, but it can be used as a
+         reference for other web-server implementations (ex: web-based
+         firmware upgrade/recovery used by some router vendors)
+
+config CMD_HTTPD_UPLOAD_MAX_SIZE
+       int "Maximum uploading size"
+       depends on CMD_HTTPD_UPLOAD
+       default 209715200
+       help
+         This option sets the resriction on the size of any uploaded file.
+         Please reserve 2--4 Kb more space due to additional space required
+         for storing of multipart/form-data header and footer. 
+
 config CMD_MII
        bool "mii"
        imply CMD_MDIO
diff --git a/cmd/net.c b/cmd/net.c
index 79bb126dbd4..f2df1eed2ef 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -21,6 +21,9 @@
 #include <net/udp.h>
 #include <net/sntp.h>
 #include <net/ncsi.h>
+#if defined(CONFIG_CMD_HTTPD_UPLOAD)
+#include <net/httpd-upload.h>
+#endif
 
 static int netboot_common(enum proto_t, struct cmd_tbl *, int, char * const 
[]);
 
@@ -229,6 +232,23 @@ U_BOOT_CMD_WITH_SUBCMDS(netcat,
        U_BOOT_SUBCMD_MKENT(save, 3, 0, do_netcat_save));
 #endif
 
+#if defined(CONFIG_CMD_HTTPD_UPLOAD)
+static int do_httpd_upload(struct cmd_tbl *cmdtp, int flag, int argc, char 
*const argv[])
+{
+       if (argc < 2)
+               return 1;
+
+       httpd_upload_prepare();
+       return netboot_common(HTTPD, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+       httpd_upload,   2,      1,      do_httpd_upload,
+       "starts httpd server for file uploading",
+       "[loadAddress]\n"
+);
+#endif
+
 static void netboot_update_env(void)
 {
        char tmp[46];
diff --git a/include/net/httpd-upload.h b/include/net/httpd-upload.h
new file mode 100644
index 00000000000..a80df214668
--- /dev/null
+++ b/include/net/httpd-upload.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * httpd-upload include file
+ * Copyright (C) 2024 IOPSYS Software Solutions AB
+ * Author: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
+ */
+#ifndef __NET_HTTPD_UPLOAD_TCP_H__
+#define __NET_HTTPD_UPLOAD_TCP_H__
+
+void httpd_upload_prepare(void);
+
+#endif /* __NET_HTTPD_UPLOAD_TCP_H__ */
diff --git a/net/Makefile b/net/Makefile
index c1f491fad02..c1b41240aab 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_PROT_TCP) += tcp.o
 obj-$(CONFIG_CMD_WGET) += wget.o
 obj-$(CONFIG_CMD_NETCAT) += netcat.o
 obj-$(CONFIG_HTTPD_COMMON) += httpd.o
+obj-$(CONFIG_CMD_HTTPD_UPLOAD) += httpd-upload.o
 
 # Disable this warning as it is triggered by:
 # sprintf(buf, index ? "foo%d" : "foo", index)
diff --git a/net/httpd-upload.c b/net/httpd-upload.c
new file mode 100644
index 00000000000..8e43860fa74
--- /dev/null
+++ b/net/httpd-upload.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * httpd-upload support driver
+ * Copyright (C) 2024 IOPSYS Software Solutions AB
+ * Author: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
+ */
+
+#include <command.h>
+#include <net.h>
+#include <net/httpd.h>
+#include <net/httpd-upload.h>
+
+#define MAX_FILE_SIZE  CONFIG_CMD_HTTPD_UPLOAD_MAX_SIZE
+
+static enum net_loop_state  httpd_on_stop(void);
+
+static enum httpd_req_check httpd_pre_post(void *req_id, const char *url,
+                                          struct httpd_post_data *post);
+static struct http_reply    *httpd_get(void *req_id, const char *url);
+static struct http_reply    *httpd_post(void *req_id, const char *url,
+                                       struct httpd_post_data *post);
+static void                 httpd_on_req_end(void *req_id);
+
+static unsigned char error_400_html[] =
+       "<html>\n"
+       "  <head><title>Bad request</title></head>\n"
+       "  <body>\n"
+       "    <h1>400 - Bad Request</h1>\n"
+       "    <p>The request you are trying to do is wrong!</p>\n"
+       "  </body>\n"
+       "</html>\n";
+
+static unsigned char error_404_html[] =
+       "<html>\n"
+       "  <head><title>Page not found</title></head>\n"
+       "  <body>\n"
+       "    <h1>404 - Page not found</h1>\n"
+       "    <p>The page you were looking for doesn't exist!</p>\n"
+       "  </body>\n"
+       "</html>\n";
+
+static unsigned char index_html[] =
+       "<html>\n"
+       "  <head><title>Upload File</title></head>\n"
+       "  <body>\n"
+       "    <h1>Upload File.</h1>\n"
+       "    <p>\n"
+       "      This will write the uploaded file to the memory area pointed\n"
+       "      by ${loadaddr}.\n"
+       "      <form method=\"post\"\n"
+       "            enctype=\"multipart/form-data\"\n"
+       "            action=\"file_upload\">\n"
+       "        File to upload:\n"
+       "        <input type=\"file\" name=\"fileID\" size=\"500\" />\n"
+       "        <p>\n"
+       "          &nbsp;&nbsp;<input type=\"submit\" value=\"upload\" />\n"
+       "        </p>\n"
+       "        <p>\n"
+       "          It takes no more than a second after the file has been\n"
+       "          uploaded until status OK is shown.\n"
+       "        </p>\n"
+       "      </form>\n"
+       "    </p>\n"
+       "  </body>\n"
+       "</html>\n";
+
+static unsigned char upload_ok_html[] =
+       "<html>\n"
+       "  <head><title>OK</title></head>\n"
+       "  <body>\n"
+       "    <h1>Upload OK</h1>\n"
+       "    <p>The file was uploaded.</p>\n"
+       "  </body>\n"
+       "</html>\n";
+
+static struct http_reply error_400 = {
+       .code      = 400,
+       .code_msg  = "Bad Request",
+       .data_type = "text/html; charset=utf-8",
+       .data      = error_400_html,
+       .len       = sizeof(error_400_html)
+};
+
+static struct http_reply error_404 = {
+       .code      = 404,
+       .code_msg  = "Not Found",
+       .data_type = "text/html; charset=utf-8",
+       .data      = error_404_html,
+       .len       = sizeof(error_404_html)
+};
+
+static struct http_reply index = {
+       .code      = 200,
+       .code_msg  = "OK",
+       .data_type = "text/html; charset=utf-8",
+       .data      = index_html,
+       .len       = sizeof(index_html)
+};
+
+static struct http_reply upload_ok = {
+       .code      = 200,
+       .code_msg  = "OK",
+       .data_type = "text/html; charset=utf-8",
+       .data      = upload_ok_html,
+       .len       = sizeof(upload_ok_html)
+};
+
+static struct httpd_config cfg = {
+       .on_stop    = httpd_on_stop,
+       .on_req_end = httpd_on_req_end,
+       .get        = httpd_get,
+       .post       = httpd_post,
+       .pre_post   = httpd_pre_post,
+       .error_400  = &error_400,
+       .error_404  = &error_404,
+};
+
+static enum net_loop_state     httpd_loop_state;
+static void                    *post_req_id;
+
+void httpd_upload_prepare(void)
+{
+       httpd_setup(&cfg);
+       httpd_loop_state = NETLOOP_FAIL;
+}
+
+static enum httpd_req_check httpd_pre_post(void *req_id, const char *url,
+                                          struct httpd_post_data *post)
+{
+       if (post->size > MAX_FILE_SIZE) {
+               printf("HTTPD: reset connection, upload file is too large\n");
+               return HTTPD_CLNT_RST;
+       }
+
+       post_req_id = req_id;
+       return HTTPD_REQ_OK;
+}
+
+static struct http_reply *httpd_post(void *req_id, const char *url,
+                                    struct httpd_post_data *post)
+{
+       if (strcmp(url, "/file_upload"))
+               return &error_404;
+
+       httpd_loop_state = NETLOOP_SUCCESS;
+       printf("HTTPD: upload OK\n");
+       return &upload_ok;
+}
+
+static struct http_reply *httpd_get(void *req_id, const char *url)
+{
+       if (!strcmp(url, "/"))
+               return &index;
+       if (!strcmp(url, "/index.html"))
+               return &index;
+       return &error_404;
+}
+
+static void httpd_on_req_end(void *req_id)
+{
+       if (req_id == post_req_id) {
+               post_req_id = NULL;
+               httpd_stop();
+       }
+}
+
+static enum net_loop_state httpd_on_stop(void)
+{
+       return httpd_loop_state;
+}
-- 
2.45.2

Reply via email to