This patch adds HTTP/1.1 compatible web-server that can be used
by other. Server supports GET, POST, and HEAD requests. On client
request it will call user specified GET/POST callback. Then results
will be transmitted to client.

The following restrictions exist on the POST request
at the moment:
  * only multipart/form-data with a single file object
  * object will be stored to a memory area specified in
    image_load_addr variable

Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
---
 include/net.h       |   2 +-
 include/net/httpd.h |  64 ++++
 net/Kconfig         |  14 +
 net/Makefile        |   1 +
 net/httpd.c         | 692 ++++++++++++++++++++++++++++++++++++++++++++
 net/net.c           |   6 +
 6 files changed, 778 insertions(+), 1 deletion(-)
 create mode 100644 include/net/httpd.h
 create mode 100644 net/httpd.c

diff --git a/include/net.h b/include/net.h
index 0af6493788a..154885a2b7e 100644
--- a/include/net.h
+++ b/include/net.h
@@ -515,7 +515,7 @@ extern int          net_restart_wrap;       /* Tried all 
network devices */
 enum proto_t {
        BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP,
        NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT_UDP, FASTBOOT_TCP,
-       WOL, UDP, NCSI, WGET, NETCAT_LOAD, NETCAT_SAVE, RS
+       WOL, UDP, NCSI, WGET, NETCAT_LOAD, NETCAT_SAVE, HTTPD, RS
 };
 
 extern char    net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net/httpd.h b/include/net/httpd.h
new file mode 100644
index 00000000000..ff0dc93ecf5
--- /dev/null
+++ b/include/net/httpd.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * httpd support header file
+ * Copyright (C) 2024 IOPSYS Software Solutions AB
+ * Author: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
+ *
+ */
+#ifndef __NET_HTTPD_COMMON_H__
+#define __NET_HTTPD_COMMON_H__
+
+struct http_reply {
+       int             code;
+       const char      *code_msg;
+       const char      *data_type;
+       void            *data;
+       u32             len;
+};
+
+struct httpd_post_data {
+       const char      *name;
+       const char      *filename;
+       void            *addr;
+       u32             size;
+};
+
+enum httpd_req_check {
+       HTTPD_REQ_OK,
+       HTTPD_BAD_URL,
+       HTTPD_BAD_REQ,
+       HTTPD_CLNT_RST
+};
+
+struct httpd_config {
+       enum net_loop_state     (*on_stop)(void);
+       void                    (*on_req_end)(void *req_id);
+
+       enum httpd_req_check    (*pre_get)(void *req_id, const char *url);
+       enum httpd_req_check    (*pre_post)(void *req_id, const char *url,
+                                           struct httpd_post_data *post);
+
+       struct http_reply *     (*get)(void *req_id, const char *url);
+       struct http_reply *     (*post)(void *req_id, const char *url,
+                                       struct httpd_post_data *post);
+
+       struct http_reply       *error_400;
+       struct http_reply       *error_404;
+};
+
+/**
+ * httpd_setup() - configure the webserver
+ */
+void httpd_setup(struct httpd_config *config);
+
+/**
+ * httpd_stop() - start stopping of the webserver
+ */
+void httpd_stop(void);
+
+/**
+ * httpd_start() - start the webserver
+ */
+void httpd_start(void);
+
+#endif /* __NET_HTTPD_COMMON_H__ */
diff --git a/net/Kconfig b/net/Kconfig
index 7cb80b880a9..55739b9bc98 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -243,6 +243,20 @@ config PROT_TCP_SACK
          This option should be turn on if you want to achieve the fastest
          file transfer possible.
 
+config HTTPD_COMMON
+       bool "HTTP server common code"
+       depends on PROT_TCP
+       help
+         HTTP/1.1 compatible web-server common code. It supports standard
+         GET/POST requests. User MUST provide a configuration to the
+         web-server. On client request web-server will call user specified
+         GET/POST callback. Then results will be transmitted to the client.
+         The following restricions on the POST request are present at the
+         moment:
+           * only mulipart/form-data with a single binary object
+           * object will be stored to a memory area specified in
+             image_load_addr variable
+
 config IPV6
        bool "IPv6 support"
        help
diff --git a/net/Makefile b/net/Makefile
index dac7b4859fb..c1f491fad02 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_PROT_UDP) += udp.o
 obj-$(CONFIG_PROT_TCP) += tcp.o
 obj-$(CONFIG_CMD_WGET) += wget.o
 obj-$(CONFIG_CMD_NETCAT) += netcat.o
+obj-$(CONFIG_HTTPD_COMMON) += httpd.o
 
 # Disable this warning as it is triggered by:
 # sprintf(buf, index ? "foo%d" : "foo", index)
diff --git a/net/httpd.c b/net/httpd.c
new file mode 100644
index 00000000000..052fae9ced0
--- /dev/null
+++ b/net/httpd.c
@@ -0,0 +1,692 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * httpd support driver
+ * Copyright (C) 2024 IOPSYS Software Solutions AB
+ * Author: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
+ */
+
+#include <command.h>
+#include <version.h>
+#include <display_options.h>
+#include <env.h>
+#include <image.h>
+#include <mapmem.h>
+#include <malloc.h>
+#include <net.h>
+#include <net/tcp.h>
+#include <net/httpd.h>
+
+#define HTTP_PORT              80
+
+#define MAX_URL_LEN            128
+#define MAX_BOUNDARY_LEN       80
+#define MAX_MPART_NAME_LEN     80
+#define MAX_FILENAME_LEN       256
+#define BUFFER_LEN             2048
+
+enum http_req_state {
+       ST_REQ_LINE = 0,
+       ST_REQ_HDR,
+       ST_REQ_MPBOUNDARY,
+       ST_REQ_MPART,
+       ST_REQ_MPFILE,
+       ST_REQ_MPEND,
+       ST_REQ_DONE,
+};
+
+enum http_reply_state {
+       ST_REPLY_ERR = 0,
+       ST_REPLY_HDR,
+       ST_REPLY_BODY
+};
+
+enum http_method {
+       HTTP_UNKNOWN = -1,
+       HTTP_GET,
+       HTTP_POST,
+       HTTP_HEAD,
+       HTTP_OPTIONS,
+};
+
+struct httpd_priv {
+       enum http_req_state     req_state;
+       enum http_reply_state   reply_state;
+
+       struct tcp_stream       *tcp;
+
+       enum http_method        method;
+       char                    url[MAX_URL_LEN];
+       u32                     version_major;
+       u32                     version_minor;
+       u32                     hdr_len;
+
+       u32                     post_fstart;
+       u32                     post_flen;
+       char                    post_fname[MAX_FILENAME_LEN];
+       char                    post_name[MAX_MPART_NAME_LEN];
+       char                    post_boundary[MAX_BOUNDARY_LEN];
+
+       int                     reply_code;
+       u32                     reply_fstart;
+       u32                     reply_flen;
+       void                    *reply_fdata;
+
+       u32                     rx_processed;
+       char                    buf[BUFFER_LEN];
+};
+
+static struct http_reply options_reply = {
+       .code      = 200,
+       .code_msg  = "OK",
+       .data_type = "text/plain",
+       .data      = NULL,
+       .len       = 0
+};
+
+static int stop_server;
+static int tsize_num_hash;
+
+static struct httpd_config *cfg;
+
+static void show_block_marker(u32 offs, u32 size)
+{
+       int cnt;
+
+       if (offs > size)
+               offs = size;
+
+       cnt = offs * 50 / size;
+       while (tsize_num_hash < cnt) {
+               putc('#');
+               tsize_num_hash++;
+       }
+
+       if (cnt == 50)
+               putc('\n');
+}
+
+static void tcp_stream_on_closed(struct tcp_stream *tcp)
+{
+       struct httpd_priv *priv = tcp->priv;
+
+       if ((priv->req_state != ST_REQ_DONE) &&
+           (priv->req_state >= ST_REQ_MPFILE)) {
+               printf("\nHTTPD: transfer was terminated\n");
+       }
+
+       if (cfg->on_req_end)
+               cfg->on_req_end(tcp->priv);
+
+       free(tcp->priv);
+
+       if (stop_server)
+               net_set_state(cfg->on_stop ? cfg->on_stop() : NETLOOP_SUCCESS);
+}
+
+static void http_make_reply(struct httpd_priv *priv, struct http_reply *reply,
+                           int head_only)
+{
+       int offs = 0;
+
+       if (priv->version_major >= 1) {
+               offs = snprintf(priv->buf, sizeof(priv->buf),
+                               "HTTP/%d.%d %d %s\r\n"
+                               "Server: %s\r\n"
+                               "Connection: close\r\n"
+                               "Cache-Control: no-store\r\n"
+                               "Content-Type: %s\r\n"
+                               "Content-Length: %d\r\n",
+                               priv->version_major, priv->version_minor,
+                               reply->code, reply->code_msg, U_BOOT_VERSION,
+                               reply->data_type,
+                               head_only ? 0 : reply->len);
+               if (priv->method == HTTP_OPTIONS)
+                       offs += snprintf(priv->buf + offs, sizeof(priv->buf) - 
offs,
+                                       "Access-Control-Allow-Methods: GET, 
HEAD, OPTIONS, POST\r\n");
+               offs += snprintf(priv->buf + offs, sizeof(priv->buf) - offs,
+                               "\r\n");
+       }
+
+       priv->reply_code = reply->code;
+       priv->reply_fstart = offs;
+       if (!head_only) {
+               priv->reply_flen = reply->len;
+               priv->reply_fdata = reply->data;
+       }
+}
+
+static enum httpd_req_check http_parse_line(struct httpd_priv *priv, char 
*line)
+{
+       char                    *url, *version, *data, *end;
+       u32                     len, tmp;
+       enum httpd_req_check    ret;
+       struct httpd_post_data  post;
+
+       switch (priv->req_state) {
+       case ST_REQ_LINE:
+               if (!strncasecmp(line, "GET ", 4)) {
+                       priv->method = HTTP_GET;
+                       url = line + 4;
+               } else if (!strncasecmp(line, "POST ", 5)) {
+                       priv->method = HTTP_POST;
+                       url = line + 5;
+               } else if (!strncasecmp(line, "HEAD ", 5)) {
+                       priv->method = HTTP_HEAD;
+                       url = line + 5;
+               } else if (!strncasecmp(line, "OPTIONS ", 8)) {
+                       priv->method = HTTP_OPTIONS;
+                       url = line + 8;
+               } else {
+                       /* unknown request */
+                       return HTTPD_CLNT_RST;
+               }
+
+               version = strstr(url, " ");
+               if (!version) {
+                       /* check for HTTP 0.9 */
+                       if (*url != '/' || priv->method != HTTP_GET)
+                               return HTTPD_CLNT_RST;
+
+                       if (strlen(url) >= MAX_URL_LEN)
+                               return HTTPD_CLNT_RST;
+
+                       if (cfg->pre_get) {
+                               ret = cfg->pre_get(priv, url);
+                               if (ret != HTTPD_REQ_OK)
+                                       return ret;
+                       }
+
+                       priv->req_state = ST_REQ_DONE;
+                       priv->hdr_len = strlen(line) + 2;
+                       priv->version_major = 0;
+                       priv->version_minor = 9;
+                       strcpy(priv->url, url);
+                       return HTTPD_REQ_OK;
+               }
+
+               if (strncasecmp(version + 1, "HTTP/", 5)) {
+                       /* version is required for HTTP >= 1.0 */
+                       return HTTPD_CLNT_RST;
+               }
+
+               *version++ = '\0';
+               version += strlen("HTTP/");
+
+               priv->version_major = dectoul(version, &end);
+               switch (*end) {
+               case '\0':
+                       priv->version_minor = 0;
+                       break;
+               case '.':
+                       priv->version_minor = dectoul(end + 1, &end);
+                       if (*end == '\0')
+                               break;
+                       fallthrough;
+               default:
+                       /* bad version format */
+                       return HTTPD_CLNT_RST;
+               }
+
+               if (priv->version_major < 1) {
+                       /* bad version */
+                       return HTTPD_CLNT_RST;
+               }
+
+               if (priv->version_major > 1 || priv->version_minor > 1) {
+                       /* We support HTTP/1.1 or early standards only */
+                       priv->version_major = 1;
+                       priv->version_minor = 1;
+               }
+
+               if (*url != '/')
+                       return HTTPD_CLNT_RST;
+
+               if (strlen(url) >= MAX_URL_LEN)
+                       return HTTPD_CLNT_RST;
+
+               priv->req_state = ST_REQ_HDR;
+               strcpy(priv->url, url);
+               return HTTPD_REQ_OK;
+
+       case ST_REQ_HDR:
+               if (*line == '\0') {
+                       priv->hdr_len = priv->rx_processed + 2;
+                       switch (priv->method) {
+                       case HTTP_GET:
+                       case HTTP_HEAD:
+                               if (cfg->pre_get) {
+                                       ret = cfg->pre_get(priv, priv->url);
+                                       if (ret != HTTPD_REQ_OK)
+                                               return ret;
+                               }
+                               fallthrough;
+
+                       case HTTP_OPTIONS:
+                               priv->req_state = ST_REQ_DONE;
+                               return HTTPD_REQ_OK;
+
+                       default:
+                               break;
+                       }
+
+                       if (*priv->post_boundary != '\0') {
+                               priv->req_state = ST_REQ_MPBOUNDARY;
+                               return HTTPD_REQ_OK;
+                       }
+                       /* NOT multipart/form-data POST request */
+                       return HTTPD_BAD_REQ;
+               }
+
+               if (priv->method != HTTP_POST)
+                       return HTTPD_REQ_OK;
+
+               len = strlen("Content-Length: ");
+               if (!strncasecmp(line, "Content-Length: ", len)) {
+                       data = line + len;
+                       priv->post_flen = simple_strtol(data, &end, 10);
+                       if (*end != '\0') {
+                               /* bad Content-Length string */
+                               return HTTPD_BAD_REQ;
+                       }
+                       return HTTPD_REQ_OK;
+               }
+
+               len = strlen("Content-Type: ");
+               if (!strncasecmp(line, "Content-Type: ", len)) {
+                       data = strstr(line + len, " boundary=");
+                       if (!data) {
+                               /* expect multipart/form-data format */
+                               return HTTPD_BAD_REQ;
+                       }
+
+                       data += strlen(" boundary=");
+                       if (strlen(data) >= sizeof(priv->post_boundary)) {
+                               /* no space to keep boundary */
+                               return HTTPD_BAD_REQ;
+                       }
+
+                       strcpy(priv->post_boundary, data);
+                       return HTTPD_REQ_OK;
+               }
+
+               return HTTPD_REQ_OK;
+
+       case ST_REQ_MPBOUNDARY:
+               if (*line == '\0')
+                       return HTTPD_REQ_OK;
+               if (line[0] != '-' || line[1] != '-' ||
+                   strcmp(line + 2, priv->post_boundary)) {
+                       /* expect boundary line */
+                       return HTTPD_BAD_REQ;
+               }
+               priv->req_state = ST_REQ_MPART;
+               return HTTPD_REQ_OK;
+
+       case ST_REQ_MPART:
+               if (*line == '\0') {
+                       if (*priv->post_name == '\0')
+                               return HTTPD_BAD_REQ;
+
+                       priv->post_fstart = priv->rx_processed + 2;
+                       priv->post_flen -= priv->post_fstart - priv->hdr_len;
+                       /* expect: "\r\n--${boundary}--\r\n", so strlen() + 8 */
+                       priv->post_flen -= strlen(priv->post_boundary) + 8;
+
+                       if (cfg->pre_post) {
+                               post.addr     = NULL;
+                               post.name     = priv->post_name;
+                               post.filename = priv->post_fname;
+                               post.size     = priv->post_flen;
+
+                               ret = cfg->pre_post(priv, priv->url, &post);
+                               if (ret != HTTPD_REQ_OK)
+                                       return ret;
+                       }
+
+                       tsize_num_hash = 0;
+                       printf("File: %s, %u bytes\n", priv->post_fname, 
priv->post_flen);
+                       printf("Loading: ");
+
+                       priv->req_state = ST_REQ_MPFILE;
+                       return HTTPD_REQ_OK;
+               }
+
+               len = strlen("Content-Disposition: ");
+               if (!strncasecmp(line, "Content-Disposition: ", len)) {
+                       data = strstr(line + len, " name=\"");
+                       if (!data) {
+                               /* name attribute not found */
+                               return HTTPD_BAD_REQ;
+                       }
+
+                       data += strlen(" name=\"");
+                       end = strstr(data, "\"");
+                       if (!end) {
+                               /* bad name attribute format */
+                               return HTTPD_BAD_REQ;
+                       }
+
+                       tmp = end - data;
+                       if (tmp >= sizeof(priv->post_name)) {
+                               /* multipart name is too long */
+                               return HTTPD_BAD_REQ;
+                       }
+                       strncpy(priv->post_name, data, tmp);
+                       priv->post_name[tmp] = '\0';
+
+                       data = strstr(line + len, " filename=\"");
+                       if (!data) {
+                               /* filename attribute not found */
+                               return HTTPD_BAD_REQ;
+                       }
+
+                       data += strlen(" filename=\"");
+                       end = strstr(data, "\"");
+                       if (!end) {
+                               /* bad filename attribute format */
+                               return HTTPD_BAD_REQ;
+                       }
+
+                       tmp = end - data;
+                       if (tmp >= sizeof(priv->post_fname))
+                               tmp = sizeof(priv->post_fname) - 1;
+                       strncpy(priv->post_fname, data, tmp);
+                       priv->post_fname[tmp] = '\0';
+                       return HTTPD_REQ_OK;
+               }
+
+               return HTTPD_REQ_OK;
+
+       case ST_REQ_MPEND:
+               if (*line == '\0')
+                       return HTTPD_REQ_OK;
+
+               len = strlen(priv->post_boundary);
+               if (line[0] != '-' || line[1] != '-' ||
+                   strncmp(line + 2, priv->post_boundary, len) ||
+                   line[len + 2] != '-' || line[len + 3] != '-' ||
+                   line[len + 4] != '\0') {
+                       /* expect final boundary line */
+                       return HTTPD_BAD_REQ;
+               }
+               priv->req_state = ST_REQ_DONE;
+               return HTTPD_REQ_OK;
+
+       default:
+               return HTTPD_BAD_REQ;
+       }
+}
+
+static enum httpd_req_check http_parse_buf(struct httpd_priv *priv,
+                                          char *buf, u32 size)
+{
+       char                    *eol_pos;
+       u32                     len;
+       enum httpd_req_check    ret;
+
+       buf[size] = '\0';
+       while (size > 0) {
+               eol_pos = strstr(buf, "\r\n");
+               if (!eol_pos)
+                       break;
+
+               *eol_pos = '\0';
+               len = eol_pos + 2 - buf;
+
+               ret = http_parse_line(priv, buf);
+               if (ret != HTTPD_REQ_OK) {
+                       /* request processing error */
+                       return ret;
+               }
+
+               priv->rx_processed += len;
+               buf += len;
+               size -= len;
+
+               if ((priv->req_state == ST_REQ_MPFILE) ||
+                   (priv->req_state == ST_REQ_DONE))
+                       return HTTPD_REQ_OK;
+       }
+       /* continue when more data becomes available */
+       return HTTPD_REQ_OK;
+}
+
+static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
+{
+       struct httpd_priv       *priv;
+       void                    *ptr;
+       u32                     shift, size;
+       enum httpd_req_check    ret;
+       struct http_reply       *reply;
+       struct httpd_post_data  post;
+
+       priv = tcp->priv;
+
+       switch (priv->req_state) {
+       case ST_REQ_DONE:
+               return;
+
+       case ST_REQ_MPFILE:
+               show_block_marker(rx_bytes - priv->post_fstart,
+                                 priv->post_flen);
+               if (rx_bytes < priv->post_fstart + priv->post_flen) {
+                       priv->rx_processed = rx_bytes;
+                       return;
+               }
+               priv->req_state = ST_REQ_MPEND;
+               priv->rx_processed = priv->post_fstart + priv->post_flen;
+               fallthrough;
+
+       case ST_REQ_MPEND:
+               shift = priv->rx_processed - priv->post_fstart;
+               ptr = map_sysmem(image_load_addr + shift,
+                                rx_bytes - priv->rx_processed);
+               ret = http_parse_buf(priv, ptr,
+                                    rx_bytes - priv->rx_processed);
+               unmap_sysmem(ptr);
+
+               if (ret != HTTPD_REQ_OK)
+                       goto error;
+               if (priv->req_state != ST_REQ_DONE)
+                       return;
+               break;
+
+       default:
+               ret = http_parse_buf(priv, priv->buf + priv->rx_processed,
+                                    rx_bytes - priv->rx_processed);
+               if (ret != HTTPD_REQ_OK)
+                       goto error;
+
+               if (priv->req_state == ST_REQ_MPFILE) {
+                       /*
+                        * We just switched from parsing of HTTP request
+                        * headers to binary data reading. Our tcp->rx
+                        * handler may put some binary data to priv->buf.
+                        * It's time to copy these data to a proper place.
+                        * It's required to copy whole buffer data starting
+                        * from priv->rx_processed position. Otherwise we
+                        * may miss data placed after the first hole.
+                        */
+                       size = sizeof(priv->buf) - priv->rx_processed;
+                       if (size > 0) {
+                               ptr = map_sysmem(image_load_addr, size);
+                               memcpy(ptr, priv->buf + priv->rx_processed, 
size);
+                               unmap_sysmem(ptr);
+                       }
+
+                       show_block_marker(rx_bytes - priv->post_fstart,
+                                         priv->post_flen);
+               }
+
+               if (priv->req_state != ST_REQ_DONE)
+                       return;
+               break;
+       }
+
+       switch (priv->method) {
+       case HTTP_OPTIONS:
+               reply = &options_reply;
+               break;
+
+       case HTTP_GET:
+       case HTTP_HEAD:
+               if (!cfg->get) {
+                       ret = HTTPD_BAD_REQ;
+                       goto error;
+               }
+               reply = cfg->get(priv, priv->url);
+               break;
+
+       case HTTP_POST:
+               if (!cfg->post) {
+                       ret = HTTPD_BAD_REQ;
+                       goto error;
+               }
+               post.name     = priv->post_name;
+               post.filename = priv->post_fname;
+               post.size     = priv->post_flen;
+               post.addr     = map_sysmem(image_load_addr, post.size);
+               reply = cfg->post(priv, priv->url, &post);
+               unmap_sysmem(post.addr);
+               break;
+
+       default:
+               ret = HTTPD_BAD_REQ;
+               goto error;
+       }
+
+       http_make_reply(priv, reply, priv->method == HTTP_HEAD);
+       return;
+
+error:
+       priv->req_state = ST_REQ_DONE;
+       switch (ret) {
+       case HTTPD_BAD_URL:
+               http_make_reply(priv, cfg->error_404, 0);
+               break;
+       case HTTPD_BAD_REQ:
+               http_make_reply(priv, cfg->error_400, 0);
+               break;
+       default:
+               tcp_stream_reset(tcp);
+               break;
+       }
+}
+
+static u32 tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, u32 
len)
+{
+       void                    *ptr;
+       struct httpd_priv       *priv;
+       u32                     shift;
+
+       priv = tcp->priv;
+       switch (priv->req_state) {
+       case ST_REQ_DONE:
+               return len;
+       case ST_REQ_MPFILE:
+       case ST_REQ_MPEND:
+               shift = rx_offs - priv->post_fstart;
+               ptr = map_sysmem(image_load_addr + shift, len);
+               memcpy(ptr, buf, len);
+               unmap_sysmem(ptr);
+               return len;
+       default:
+               /*
+                * accept data that fits to buffer,
+                * reserve space for end of line symbol
+                */
+               if (rx_offs + len > sizeof(priv->buf) - 1)
+                       len = sizeof(priv->buf) - rx_offs - 1;
+               memcpy(priv->buf + rx_offs, buf, len);
+               return len;
+       }
+}
+
+static void tcp_stream_on_snd_una_update(struct tcp_stream *tcp, u32 tx_bytes)
+{
+       struct httpd_priv *priv;
+
+       priv = tcp->priv;
+       if ((priv->req_state == ST_REQ_DONE) &&
+           (tx_bytes == priv->reply_fstart + priv->reply_flen))
+               tcp_stream_close(tcp);
+}
+
+static u32 tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, u32 
maxlen)
+{
+       struct httpd_priv       *priv;
+       u32                     len, bytes = 0;
+       char                    *ptr;
+
+       priv = tcp->priv;
+       if (priv->req_state != ST_REQ_DONE)
+               return 0;
+
+       if (tx_offs < priv->reply_fstart) {
+               len = maxlen;
+               if (len > priv->reply_fstart - tx_offs)
+                       len = priv->reply_fstart - tx_offs;
+               memcpy(buf, priv->buf + tx_offs, len);
+               buf += len;
+               tx_offs += len;
+               bytes += len;
+               maxlen -= len;
+       }
+
+       if (tx_offs >= priv->reply_fstart) {
+               if (tx_offs + maxlen > priv->reply_fstart + priv->reply_flen)
+                       maxlen = priv->reply_fstart + priv->reply_flen - 
tx_offs;
+               if (maxlen > 0) {
+                       ptr = priv->reply_fdata + tx_offs - priv->reply_fstart;
+                       memcpy(buf, ptr, maxlen);
+                       bytes += maxlen;
+               }
+       }
+
+       return bytes;
+}
+
+static int tcp_stream_on_create(struct tcp_stream *tcp)
+{
+       struct httpd_priv       *priv;
+
+       if (!cfg || stop_server || tcp->lport != HTTP_PORT)
+               return 0;
+
+       priv = malloc(sizeof(struct httpd_priv));
+       if (!priv)
+               return 0;
+
+       memset(priv, 0, sizeof(struct httpd_priv));
+       priv->tcp = tcp;
+
+       tcp->priv = priv;
+       tcp->on_closed = tcp_stream_on_closed;
+       tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update;
+       tcp->rx = tcp_stream_rx;
+       tcp->on_snd_una_update = tcp_stream_on_snd_una_update;
+       tcp->tx = tcp_stream_tx;
+       return 1;
+}
+
+void httpd_setup(struct httpd_config *config)
+{
+       cfg = config;
+}
+
+void httpd_stop(void)
+{
+       stop_server = 1;
+}
+
+void httpd_start(void)
+{
+       if (!cfg) {
+               net_set_state(NETLOOP_FAIL);
+               return;
+       }
+       stop_server = 0;
+       memset(net_server_ethaddr, 0, 6);
+       tcp_stream_set_on_create_handler(tcp_stream_on_create);
+       printf("HTTPD listening on port %d...\n", HTTP_PORT);
+}
diff --git a/net/net.c b/net/net.c
index d81604a57ce..0b6cf4ef45c 100644
--- a/net/net.c
+++ b/net/net.c
@@ -109,6 +109,7 @@
 #include <net/tcp.h>
 #include <net/wget.h>
 #include <net/netcat.h>
+#include <net/httpd.h>
 #include "arp.h"
 #include "bootp.h"
 #include "cdp.h"
@@ -573,6 +574,11 @@ restart:
                        netcat_save_start();
                        break;
 #endif
+#if defined(CONFIG_HTTPD_COMMON)
+               case HTTPD:
+                       httpd_start();
+                       break;
+#endif
 #if defined(CONFIG_CMD_CDP)
                case CDP:
                        cdp_start();
-- 
2.43.0

Reply via email to