[PATCH 08/12] net/httpd: add httpd common code
Mikhail Kshevetskiy
mikhail.kshevetskiy at genexis.eu
Wed Jul 3 04:06:52 CEST 2024
On 7/1/24 19:54, Peter Robinson wrote:
> Hi Mikhail,
>
>> 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.
> Why are we adding a HTTP server? I don't see a cover letter explaining
> overall what you're attempting to achieve with this patch set so
> please add that. Also I suggest you look at the LWIP patch set [1] as
> that may make what you wish to achieve more straight forward.
>
> Peter
>
> [1] https://lists.denx.de/pipermail/u-boot/2024-June/556526.html
This patch series consist of
* TCP fixes. Current U-Boot implementation of TCP is bad. It is
especially bad for uploading. This patch series fixes TCP support. I
know about attempts to add LWIP to u-Boot, but it's not in U-Boot yet.
* Rewrite of existing TCP clients (wget, fastboot_tcp) on the base of
new code
* netcat client/server. It was written to test data downloading and
uploading using TCP.
* HTTPD support. It consist of 2 parts: common code and sample
web-server. Sample web-server can be used as a reference httpd
implementation. We use this HTTPD support for our firmware upgrade
web-server. It is similar to the sample web-server.
PS: Will resend patches with a cover letter tomorrow.
>> 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.kshevetskiy at iopsys.eu>
>> ---
>> include/net.h | 2 +-
>> include/net/httpd.h | 64 ++++
>> net/Kconfig | 14 +
>> net/Makefile | 1 +
>> net/httpd.c | 695 ++++++++++++++++++++++++++++++++++++++++++++
>> net/net.c | 6 +
>> 6 files changed, 781 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 235396a171b..6debbf8ed2a 100644
>> --- a/include/net.h
>> +++ b/include/net.h
>> @@ -516,7 +516,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_STORE, RS
>> + WOL, UDP, NCSI, WGET, NETCAT_LOAD, NETCAT_STORE, 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.kshevetskiy at 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 5dff6336293..424c5f0dae8 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..31c10843a44
>> --- /dev/null
>> +++ b/net/httpd.c
>> @@ -0,0 +1,695 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * httpd support driver
>> + * Copyright (C) 2024 IOPSYS Software Solutions AB
>> + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy at 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 != NULL)
>> + cfg->on_req_end(tcp->priv);
>> +
>> + free(tcp->priv);
>> +
>> + if (stop_server)
>> + net_set_state(cfg->on_stop != NULL ?
>> + 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) == 0) {
>> + priv->method = HTTP_GET;
>> + url = line + 4;
>> + } else if (strncasecmp(line, "POST ", 5) == 0) {
>> + priv->method = HTTP_POST;
>> + url = line + 5;
>> + } else if (strncasecmp(line, "HEAD ", 5) == 0) {
>> + priv->method = HTTP_HEAD;
>> + url = line + 5;
>> + } else if (strncasecmp(line, "OPTIONS ", 8) == 0) {
>> + priv->method = HTTP_OPTIONS;
>> + url = line + 8;
>> + } else {
>> + /* unknown request */
>> + return HTTPD_CLNT_RST;
>> + }
>> +
>> + version = strstr(url, " ");
>> + if (version == NULL) {
>> + /* 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 != NULL) {
>> + 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) != 0) {
>> + /* 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 != NULL) {
>> + 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) == 0) {
>> + 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) == 0) {
>> + data = strstr(line + len, " boundary=");
>> + if (data == NULL) {
>> + /* 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) != 0)) {
>> + /* 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 != NULL) {
>> + 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) == 0) {
>> + data = strstr(line + len, " name=\"");
>> + if (data == NULL) {
>> + /* name attribute not found */
>> + return HTTPD_BAD_REQ;
>> + }
>> +
>> + data += strlen(" name=\"");
>> + end = strstr(data, "\"");
>> + if (end == NULL) {
>> + /* 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 == NULL) {
>> + /* filename attribute not found */
>> + return HTTPD_BAD_REQ;
>> + }
>> +
>> + data += strlen(" filename=\"");
>> + end = strstr(data, "\"");
>> + if (end == NULL) {
>> + /* 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) != 0) ||
>> + (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 == NULL)
>> + 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 == NULL) {
>> + ret = HTTPD_BAD_REQ;
>> + goto error;
>> + }
>> + reply = cfg->get(priv, priv->url);
>> + break;
>> +
>> + case HTTP_POST:
>> + if (cfg->post == NULL) {
>> + 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 == NULL) || stop_server ||
>> + (tcp->lport != HTTP_PORT))
>> + return 0;
>> +
>> + priv = malloc(sizeof(struct httpd_priv));
>> + if (priv == NULL)
>> + 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 == NULL) {
>> + 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 809fe5c4792..ca761c57372 100644
>> --- a/net/net.c
>> +++ b/net/net.c
>> @@ -111,6 +111,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"
>> @@ -575,6 +576,11 @@ restart:
>> netcat_store_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
>>
More information about the U-Boot
mailing list