esp_http_server: Update to support build for Linux

This commit is contained in:
Harshit Malpani 2023-01-09 15:13:26 +05:30
parent 1e7f9c8f72
commit 7c68b67295
No known key found for this signature in database
GPG Key ID: FF1193D150EF75C3
9 changed files with 142 additions and 33 deletions

View File

@ -1,3 +1,16 @@
set(priv_req mbedtls)
set(priv_inc_dir "src/util")
set(requires http_parser)
if(NOT ${IDF_TARGET} STREQUAL "linux")
list(APPEND priv_req lwip esp_timer)
list(APPEND priv_inc_dir "src/port/esp32")
list(APPEND requires esp_event)
else()
list(APPEND priv_inc_dir "src/port/linux")
list(APPEND priv_req pthread)
list(APPEND requires linux)
endif()
idf_component_register(SRCS "src/httpd_main.c"
"src/httpd_parse.c"
"src/httpd_sess.c"
@ -6,6 +19,15 @@ idf_component_register(SRCS "src/httpd_main.c"
"src/httpd_ws.c"
"src/util/ctrl_sock.c"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src/port/esp32" "src/util"
REQUIRES esp_event http_parser # for http_parser.h
PRIV_REQUIRES lwip mbedtls esp_timer)
PRIV_INCLUDE_DIRS ${priv_inc_dir}
REQUIRES ${requires}
PRIV_REQUIRES ${priv_req})
if(${IDF_TARGET} STREQUAL "linux")
find_library(LIB_BSD bsd)
if(LIB_BSD)
target_link_libraries(${COMPONENT_LIB} PRIVATE ${LIB_BSD})
elseif(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
message(WARNING "Missing LIBBSD library. Install libbsd-dev package and/or check linker directories.")
endif()
endif()

View File

@ -15,6 +15,7 @@
#include <sdkconfig.h>
#include <esp_err.h>
#include <esp_event.h>
#include <esp_event_base.h>
#ifdef __cplusplus
extern "C" {

View File

@ -22,6 +22,14 @@
extern "C" {
#endif
#if CONFIG_NEWLIB_NANO_FORMAT
#define NEWLIB_NANO_COMPAT_FORMAT PRIu32
#define NEWLIB_NANO_COMPAT_CAST(size_t_var) (uint32_t)size_t_var
#else
#define NEWLIB_NANO_COMPAT_FORMAT "zu"
#define NEWLIB_NANO_COMPAT_CAST(size_t_var) size_t_var
#endif
/* Size of request data block/chunk (not to be confused with chunked encoded data)
* that is received and parsed in one turn of the parsing process. This should not
* exceed the scratch buffer size and should at least be 8 bytes */

View File

@ -11,6 +11,7 @@
#include <esp_log.h>
#include <esp_err.h>
#include <assert.h>
#include <netinet/tcp.h>
#include <esp_http_server.h>
#include "esp_httpd_priv.h"
@ -19,6 +20,13 @@
#include "freertos/semphr.h"
#endif
#if defined(CONFIG_LWIP_MAX_SOCKETS)
#define HTTPD_MAX_SOCKETS CONFIG_LWIP_MAX_SOCKETS
#else
/* LwIP component is not included into the build, use a default value */
#define HTTPD_MAX_SOCKETS 15
#endif
static const int DEFAULT_KEEP_ALIVE_IDLE = 5;
static const int DEFAULT_KEEP_ALIVE_INTERVAL= 5;
static const int DEFAULT_KEEP_ALIVE_COUNT= 3;
@ -476,10 +484,10 @@ esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config)
* 3) for receiving control messages over UDP
* So the total number of required sockets is max_open_sockets + 3
*/
if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) {
if (HTTPD_MAX_SOCKETS < config->max_open_sockets + 3) {
ESP_LOGE(TAG, "Config option max_open_sockets is too large (max allowed %d, 3 sockets used by HTTP server internally)\n\t"
"Either decrease this or configure LWIP_MAX_SOCKETS to a larger value",
CONFIG_LWIP_MAX_SOCKETS - 3);
HTTPD_MAX_SOCKETS - 3);
return ESP_ERR_INVALID_ARG;
}

View File

@ -6,6 +6,11 @@
#include <stdlib.h>
#include <string.h>
#if __has_include(<bsd/string.h>)
// for strlcpy
#include <bsd/string.h>
#endif
#include <sys/param.h>
#include <esp_log.h>
#include <esp_err.h>
@ -72,8 +77,8 @@ static esp_err_t verify_url (http_parser *parser)
}
if (sizeof(r->uri) < (length + 1)) {
ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
length, sizeof(r->uri));
ESP_LOGW(TAG, LOG_FMT("URI length (%"NEWLIB_NANO_COMPAT_FORMAT") greater than supported (%"NEWLIB_NANO_COMPAT_FORMAT")"),
NEWLIB_NANO_COMPAT_CAST(length), NEWLIB_NANO_COMPAT_CAST(sizeof(r->uri)));
parser_data->error = HTTPD_414_URI_TOO_LONG;
return ESP_FAIL;
}
@ -126,12 +131,12 @@ static esp_err_t cb_url(http_parser *parser,
return ESP_FAIL;
}
ESP_LOGD(TAG, LOG_FMT("processing url = %.*s"), length, at);
ESP_LOGD(TAG, LOG_FMT("processing url = %.*s"), (int)length, at);
/* Update length of URL string */
if ((parser_data->last.length += length) > HTTPD_MAX_URI_LEN) {
ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
parser_data->last.length, HTTPD_MAX_URI_LEN);
ESP_LOGW(TAG, LOG_FMT("URI length (%"NEWLIB_NANO_COMPAT_FORMAT") greater than supported (%d)"),
NEWLIB_NANO_COMPAT_CAST(parser_data->last.length), HTTPD_MAX_URI_LEN);
parser_data->error = HTTPD_414_URI_TOO_LONG;
parser_data->status = PARSING_FAILED;
return ESP_FAIL;
@ -149,7 +154,7 @@ static esp_err_t pause_parsing(http_parser *parser, const char* at)
* and hence needs to be read again later for parsing */
ssize_t unparsed = parser_data->raw_datalen - (at - ra->scratch);
if (unparsed < 0) {
ESP_LOGE(TAG, LOG_FMT("parsing beyond valid data = %d"), -unparsed);
ESP_LOGE(TAG, LOG_FMT("parsing beyond valid data = %d"), (int)(-unparsed));
return ESP_ERR_INVALID_STATE;
}
@ -157,7 +162,7 @@ static esp_err_t pause_parsing(http_parser *parser, const char* at)
* receiving again with httpd_recv_with_opt() later when
* read_block() executes */
if (unparsed && (unparsed != httpd_unrecv(r, at, unparsed))) {
ESP_LOGE(TAG, LOG_FMT("data too large for un-recv = %d"), unparsed);
ESP_LOGE(TAG, LOG_FMT("data too large for un-recv = %d"), (int)unparsed);
return ESP_FAIL;
}
@ -181,7 +186,7 @@ static size_t continue_parsing(http_parser *parser, size_t length)
* so we must skip that before parsing resumes */
length = MIN(length, data->pre_parsed);
data->pre_parsed -= length;
ESP_LOGD(TAG, LOG_FMT("skip pre-parsed data of size = %d"), length);
ESP_LOGD(TAG, LOG_FMT("skip pre-parsed data of size = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(length));
http_parser_pause(parser, 0);
data->paused = false;
@ -241,7 +246,7 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
return ESP_FAIL;
}
ESP_LOGD(TAG, LOG_FMT("processing field = %.*s"), length, at);
ESP_LOGD(TAG, LOG_FMT("processing field = %.*s"), (int)length, at);
/* Update length of header string */
parser_data->last.length += length;
@ -285,7 +290,7 @@ static esp_err_t cb_header_value(http_parser *parser, const char *at, size_t len
return ESP_FAIL;
}
ESP_LOGD(TAG, LOG_FMT("processing value = %.*s"), length, at);
ESP_LOGD(TAG, LOG_FMT("processing value = %.*s"), (int)length, at);
/* Update length of header string */
parser_data->last.length += length;
@ -366,7 +371,7 @@ static esp_err_t cb_headers_complete(http_parser *parser)
parser->content_length : 0);
ESP_LOGD(TAG, LOG_FMT("bytes read = %" PRId32 ""), parser->nread);
ESP_LOGD(TAG, LOG_FMT("content length = %zu"), r->content_len);
ESP_LOGD(TAG, LOG_FMT("content length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(r->content_len));
/* Handle upgrade requests - only WebSocket is supported for now */
if (parser->upgrade) {
@ -582,14 +587,14 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
/* http_parser error */
data->error = HTTPD_400_BAD_REQUEST;
data->status = PARSING_FAILED;
ESP_LOGW(TAG, LOG_FMT("incomplete (%d/%d) with parser error = %d"),
nparsed, length, parser->http_errno);
ESP_LOGW(TAG, LOG_FMT("incomplete (%"NEWLIB_NANO_COMPAT_FORMAT"/%"NEWLIB_NANO_COMPAT_FORMAT") with parser error = %d"),
NEWLIB_NANO_COMPAT_CAST(nparsed), NEWLIB_NANO_COMPAT_CAST(length), parser->http_errno);
return -1;
}
/* Return with the total length of the request packet
* that has been parsed till now */
ESP_LOGD(TAG, LOG_FMT("parsed block size = %d"), offset + nparsed);
ESP_LOGD(TAG, LOG_FMT("parsed block size = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST((offset + nparsed)));
return offset + nparsed;
}

View File

@ -11,6 +11,7 @@
#include <esp_http_server.h>
#include "esp_httpd_priv.h"
#include <netinet/tcp.h>
static const char *TAG = "httpd_txrx";
@ -96,14 +97,14 @@ static size_t httpd_recv_pending(httpd_req_t *r, char *buf, size_t buf_len)
int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_after_pending)
{
ESP_LOGD(TAG, LOG_FMT("requested length = %d"), buf_len);
ESP_LOGD(TAG, LOG_FMT("requested length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(buf_len));
size_t pending_len = 0;
struct httpd_req_aux *ra = r->aux;
/* First fetch pending data from local buffer */
if (ra->sd->pending_len > 0) {
ESP_LOGD(TAG, LOG_FMT("pending length = %d"), ra->sd->pending_len);
ESP_LOGD(TAG, LOG_FMT("pending length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(ra->sd->pending_len));
pending_len = httpd_recv_pending(r, buf, buf_len);
buf += pending_len;
buf_len -= pending_len;
@ -132,7 +133,7 @@ int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_aft
return ret;
}
ESP_LOGD(TAG, LOG_FMT("received length = %d"), ret + pending_len);
ESP_LOGD(TAG, LOG_FMT("received length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST((ret + pending_len)));
return ret + pending_len;
}
@ -151,7 +152,7 @@ size_t httpd_unrecv(struct httpd_req *r, const char *buf, size_t buf_len)
* such that it is right aligned inside the buffer */
size_t offset = sizeof(ra->sd->pending_data) - ra->sd->pending_len;
memcpy(ra->sd->pending_data + offset, buf, ra->sd->pending_len);
ESP_LOGD(TAG, LOG_FMT("length = %d"), ra->sd->pending_len);
ESP_LOGD(TAG, LOG_FMT("length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(ra->sd->pending_len));
return ra->sd->pending_len;
}
@ -360,7 +361,7 @@ esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len
/* Sending chunked content */
char len_str[10];
snprintf(len_str, sizeof(len_str), "%x\r\n", buf_len);
snprintf(len_str, sizeof(len_str), "%lx\r\n", (long)buf_len);
if (httpd_send_all(r, len_str, strlen(len_str)) != ESP_OK) {
return ESP_ERR_HTTPD_RESP_SEND;
}
@ -529,7 +530,7 @@ int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len)
}
struct httpd_req_aux *ra = r->aux;
ESP_LOGD(TAG, LOG_FMT("remaining length = %d"), ra->remaining_len);
ESP_LOGD(TAG, LOG_FMT("remaining length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(ra->remaining_len));
if (buf_len > ra->remaining_len) {
buf_len = ra->remaining_len;

View File

@ -153,7 +153,7 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *suppor
char subprotocol[50] = { '\0' };
if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Protocol", subprotocol, sizeof(subprotocol) - 1) == ESP_ERR_HTTPD_RESULT_TRUNC) {
ESP_LOGW(TAG, "Sec-WebSocket-Protocol length exceeded buffer size of %d, was trunctated", sizeof(subprotocol));
ESP_LOGW(TAG, "Sec-WebSocket-Protocol length exceeded buffer size of %"NEWLIB_NANO_COMPAT_FORMAT", was trunctated", NEWLIB_NANO_COMPAT_CAST(sizeof(subprotocol)));
}
@ -175,7 +175,7 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *suppor
int r = snprintf(tx_buf + fmt_len, sizeof(tx_buf) - fmt_len, "Sec-WebSocket-Protocol: %s\r\n", supported_subprotocol);
if (r <= 0) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of subprotocol returned %d, buffer size: %d", r, sizeof(tx_buf));
"(snprintf of subprotocol returned %d, buffer size: %"NEWLIB_NANO_COMPAT_FORMAT, r, NEWLIB_NANO_COMPAT_CAST(sizeof(tx_buf)));
return ESP_FAIL;
}
@ -183,7 +183,7 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *suppor
if (fmt_len >= sizeof(tx_buf)) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of subprotocol returned %d, desired response len: %d, buffer size: %d", r, fmt_len, sizeof(tx_buf));
"(snprintf of subprotocol returned %d, desired response len: %d, buffer size: %"NEWLIB_NANO_COMPAT_FORMAT, r, fmt_len, NEWLIB_NANO_COMPAT_CAST(sizeof(tx_buf)));
return ESP_FAIL;
}
}
@ -191,13 +191,13 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *suppor
int r = snprintf(tx_buf + fmt_len, sizeof(tx_buf) - fmt_len, "\r\n");
if (r <= 0) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of subprotocol returned %d, buffer size: %d", r, sizeof(tx_buf));
"(snprintf of subprotocol returned %d, buffer size: %"NEWLIB_NANO_COMPAT_FORMAT, r, NEWLIB_NANO_COMPAT_CAST(sizeof(tx_buf)));
return ESP_FAIL;
}
fmt_len += r;
if (fmt_len >= sizeof(tx_buf)) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of header terminal returned %d, desired response len: %d, buffer size: %d", r, fmt_len, sizeof(tx_buf));
"(snprintf of header terminal returned %d, desired response len: %d, buffer size: %"NEWLIB_NANO_COMPAT_FORMAT, r, fmt_len, NEWLIB_NANO_COMPAT_CAST(sizeof(tx_buf)));
return ESP_FAIL;
}
@ -354,7 +354,7 @@ esp_err_t httpd_ws_recv_frame(httpd_req_t *req, httpd_ws_frame_t *frame, size_t
offset += read_len;
left_len -= read_len;
ESP_LOGD(TAG, "Frame length: %d, Bytes Read: %d", frame->len, offset);
ESP_LOGD(TAG, "Frame length: %"NEWLIB_NANO_COMPAT_FORMAT", Bytes Read: %"NEWLIB_NANO_COMPAT_FORMAT, NEWLIB_NANO_COMPAT_CAST(frame->len), NEWLIB_NANO_COMPAT_CAST(offset));
}
/* Unmask payload */

View File

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OS_SUCCESS ESP_OK
#define OS_FAIL ESP_FAIL
typedef TaskHandle_t othread_t;
static inline int httpd_os_thread_create(othread_t *thread,
const char *name, uint16_t stacksize, int prio,
void (*thread_routine)(void *arg), void *arg,
BaseType_t core_id)
{
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
pthread_attr_setstacksize(&thread_attr, stacksize);
int ret = pthread_create((pthread_t *)thread, &thread_attr, (void*)thread_routine, arg);
if (ret == 0) {
return OS_SUCCESS;
}
return OS_FAIL;
}
/* Only self delete is supported */
static inline void httpd_os_thread_delete(void)
{
int x;
pthread_exit((void *)&x);
}
static inline void httpd_os_thread_sleep(int msecs)
{
usleep(msecs * 1000);
}
static inline othread_t httpd_os_thread_handle(void)
{
return (othread_t)pthread_self();
}
#ifdef __cplusplus
}
#endif

View File

@ -10,9 +10,18 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "sdkconfig.h"
#include "ctrl_sock.h"
#if CONFIG_IDF_TARGET_LINUX
#define IPV4_ENABLED 1
#define IPV6_ENABLED 1
#else // CONFIG_IDF_TARGET_LINUX
#define IPV4_ENABLED CONFIG_LWIP_IPV4
#define IPV6_ENABLED CONFIG_LWIP_IPV6
#endif // !CONFIG_IDF_TARGET_LINUX
/* Control socket, because in some network stacks select can't be woken up any
* other way
*/
@ -25,7 +34,7 @@ int cs_create_ctrl_sock(int port)
int ret;
struct sockaddr_storage addr = {};
#ifdef CONFIG_LWIP_IPV4
#if IPV4_ENABLED
struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
addr4->sin_family = AF_INET;
addr4->sin_port = htons(port);
@ -53,7 +62,7 @@ int cs_send_to_ctrl_sock(int send_fd, int port, void *data, unsigned int data_le
{
int ret;
struct sockaddr_storage to_addr = {};
#ifdef CONFIG_LWIP_IPV4
#if IPV4_ENABLED
struct sockaddr_in *addr4 = (struct sockaddr_in *)&to_addr;
addr4->sin_family = AF_INET;
addr4->sin_port = htons(port);