mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
645 lines
20 KiB
C
645 lines
20 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
#include <errno.h>
|
|
#include <esp_log.h>
|
|
#include <esp_err.h>
|
|
|
|
#include <esp_http_server.h>
|
|
#include "esp_httpd_priv.h"
|
|
#include <netinet/tcp.h>
|
|
|
|
static const char *TAG = "httpd_txrx";
|
|
|
|
esp_err_t httpd_sess_set_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func)
|
|
{
|
|
struct sock_db *sess = httpd_sess_get(hd, sockfd);
|
|
if (!sess) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
sess->send_fn = send_func;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t httpd_sess_set_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func)
|
|
{
|
|
struct sock_db *sess = httpd_sess_get(hd, sockfd);
|
|
if (!sess) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
sess->recv_fn = recv_func;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func)
|
|
{
|
|
struct sock_db *sess = httpd_sess_get(hd, sockfd);
|
|
if (!sess) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
sess->pending_fn = pending_func;
|
|
return ESP_OK;
|
|
}
|
|
|
|
int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len)
|
|
{
|
|
if (r == NULL || buf == NULL) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
int ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0);
|
|
if (ret < 0) {
|
|
ESP_LOGD(TAG, LOG_FMT("error in send_fn"));
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static esp_err_t httpd_send_all(httpd_req_t *r, const char *buf, size_t buf_len)
|
|
{
|
|
struct httpd_req_aux *ra = r->aux;
|
|
int ret;
|
|
|
|
while (buf_len > 0) {
|
|
ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0);
|
|
if (ret < 0) {
|
|
ESP_LOGD(TAG, LOG_FMT("error in send_fn"));
|
|
return ESP_FAIL;
|
|
}
|
|
ESP_LOGD(TAG, LOG_FMT("sent = %d"), ret);
|
|
buf += ret;
|
|
buf_len -= ret;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
static size_t httpd_recv_pending(httpd_req_t *r, char *buf, size_t buf_len)
|
|
{
|
|
struct httpd_req_aux *ra = r->aux;
|
|
size_t offset = sizeof(ra->sd->pending_data) - ra->sd->pending_len;
|
|
|
|
/* buf_len must not be greater than remaining_len */
|
|
buf_len = MIN(ra->sd->pending_len, buf_len);
|
|
memcpy(buf, ra->sd->pending_data + offset, buf_len);
|
|
|
|
ra->sd->pending_len -= buf_len;
|
|
return 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 = %"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 = %"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;
|
|
|
|
/* If buffer filled then no need to recv.
|
|
* If asked to halt after receiving pending data then
|
|
* return with received length */
|
|
if (!buf_len || halt_after_pending) {
|
|
return pending_len;
|
|
}
|
|
}
|
|
|
|
/* Receive data of remaining length */
|
|
int ret = ra->sd->recv_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0);
|
|
if (ret < 0) {
|
|
ESP_LOGD(TAG, LOG_FMT("error in recv_fn"));
|
|
if ((ret == HTTPD_SOCK_ERR_TIMEOUT) && (pending_len != 0)) {
|
|
/* If recv() timeout occurred, but pending data is
|
|
* present, return length of pending data.
|
|
* This behavior is similar to that of socket recv()
|
|
* function, which, in case has only partially read the
|
|
* requested length, due to timeout, returns with read
|
|
* length, rather than error */
|
|
return pending_len;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ESP_LOGD(TAG, LOG_FMT("received length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST((ret + pending_len)));
|
|
return ret + pending_len;
|
|
}
|
|
|
|
int httpd_recv(httpd_req_t *r, char *buf, size_t buf_len)
|
|
{
|
|
return httpd_recv_with_opt(r, buf, buf_len, false);
|
|
}
|
|
|
|
size_t httpd_unrecv(struct httpd_req *r, const char *buf, size_t buf_len)
|
|
{
|
|
struct httpd_req_aux *ra = r->aux;
|
|
/* Truncate if external buf_len is greater than pending_data buffer size */
|
|
ra->sd->pending_len = MIN(sizeof(ra->sd->pending_data), buf_len);
|
|
|
|
/* Copy data into internal pending_data buffer with the exact offset
|
|
* 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 = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST(ra->sd->pending_len));
|
|
return ra->sd->pending_len;
|
|
}
|
|
|
|
/**
|
|
* This API appends an additional header field-value pair in the HTTP response.
|
|
* But the header isn't sent out until any of the send APIs is executed.
|
|
*/
|
|
esp_err_t httpd_resp_set_hdr(httpd_req_t *r, const char *field, const char *value)
|
|
{
|
|
if (r == NULL || field == NULL || value == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
return ESP_ERR_HTTPD_INVALID_REQ;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
struct httpd_data *hd = (struct httpd_data *) r->handle;
|
|
|
|
/* Number of additional headers is limited */
|
|
if (ra->resp_hdrs_count >= hd->config.max_resp_headers) {
|
|
return ESP_ERR_HTTPD_RESP_HDR;
|
|
}
|
|
|
|
/* Assign header field-value pair */
|
|
ra->resp_hdrs[ra->resp_hdrs_count].field = field;
|
|
ra->resp_hdrs[ra->resp_hdrs_count].value = value;
|
|
ra->resp_hdrs_count++;
|
|
|
|
ESP_LOGD(TAG, LOG_FMT("new header = %s: %s"), field, value);
|
|
return ESP_OK;
|
|
}
|
|
|
|
/**
|
|
* This API sets the status of the HTTP response to the value specified.
|
|
* But the status isn't sent out until any of the send APIs is executed.
|
|
*/
|
|
esp_err_t httpd_resp_set_status(httpd_req_t *r, const char *status)
|
|
{
|
|
if (r == NULL || status == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
return ESP_ERR_HTTPD_INVALID_REQ;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
ra->status = (char *)status;
|
|
return ESP_OK;
|
|
}
|
|
|
|
/**
|
|
* This API sets the method/type of the HTTP response to the value specified.
|
|
* But the method isn't sent out until any of the send APIs is executed.
|
|
*/
|
|
esp_err_t httpd_resp_set_type(httpd_req_t *r, const char *type)
|
|
{
|
|
if (r == NULL || type == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
return ESP_ERR_HTTPD_INVALID_REQ;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
ra->content_type = (char *)type;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len)
|
|
{
|
|
if (r == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
return ESP_ERR_HTTPD_INVALID_REQ;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
const char *httpd_hdr_str = "HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n";
|
|
const char *colon_separator = ": ";
|
|
const char *cr_lf_seperator = "\r\n";
|
|
|
|
if (buf_len == HTTPD_RESP_USE_STRLEN) {
|
|
buf_len = strlen(buf);
|
|
}
|
|
|
|
/* Request headers are no longer available */
|
|
ra->req_hdrs_count = 0;
|
|
|
|
/* Size of essential headers is limited by scratch buffer size */
|
|
if (snprintf(ra->scratch, sizeof(ra->scratch), httpd_hdr_str,
|
|
ra->status, ra->content_type, buf_len) >= sizeof(ra->scratch)) {
|
|
return ESP_ERR_HTTPD_RESP_HDR;
|
|
}
|
|
|
|
/* Sending essential headers */
|
|
if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
|
|
/* Sending additional headers based on set_header */
|
|
for (unsigned i = 0; i < ra->resp_hdrs_count; i++) {
|
|
/* Send header field */
|
|
if (httpd_send_all(r, ra->resp_hdrs[i].field, strlen(ra->resp_hdrs[i].field)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
/* Send ': ' */
|
|
if (httpd_send_all(r, colon_separator, strlen(colon_separator)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
/* Send header value */
|
|
if (httpd_send_all(r, ra->resp_hdrs[i].value, strlen(ra->resp_hdrs[i].value)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
/* Send CR + LF */
|
|
if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
}
|
|
|
|
/* End header section */
|
|
if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
esp_http_server_dispatch_event(HTTP_SERVER_EVENT_HEADERS_SENT, &(ra->sd->fd), sizeof(int));
|
|
|
|
/* Sending content */
|
|
if (buf && buf_len) {
|
|
if (httpd_send_all(r, buf, buf_len) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
}
|
|
esp_http_server_event_data evt_data = {
|
|
.fd = ra->sd->fd,
|
|
.data_len = buf_len,
|
|
};
|
|
esp_http_server_dispatch_event(HTTP_SERVER_EVENT_SENT_DATA, &evt_data, sizeof(esp_http_server_event_data));
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len)
|
|
{
|
|
if (r == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
return ESP_ERR_HTTPD_INVALID_REQ;
|
|
}
|
|
|
|
if (buf_len == HTTPD_RESP_USE_STRLEN) {
|
|
buf_len = strlen(buf);
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
const char *httpd_chunked_hdr_str = "HTTP/1.1 %s\r\nContent-Type: %s\r\nTransfer-Encoding: chunked\r\n";
|
|
const char *colon_separator = ": ";
|
|
const char *cr_lf_seperator = "\r\n";
|
|
|
|
/* Request headers are no longer available */
|
|
ra->req_hdrs_count = 0;
|
|
|
|
if (!ra->first_chunk_sent) {
|
|
/* Size of essential headers is limited by scratch buffer size */
|
|
if (snprintf(ra->scratch, sizeof(ra->scratch), httpd_chunked_hdr_str,
|
|
ra->status, ra->content_type) >= sizeof(ra->scratch)) {
|
|
return ESP_ERR_HTTPD_RESP_HDR;
|
|
}
|
|
|
|
/* Sending essential headers */
|
|
if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
|
|
/* Sending additional headers based on set_header */
|
|
for (unsigned i = 0; i < ra->resp_hdrs_count; i++) {
|
|
/* Send header field */
|
|
if (httpd_send_all(r, ra->resp_hdrs[i].field, strlen(ra->resp_hdrs[i].field)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
/* Send ': ' */
|
|
if (httpd_send_all(r, colon_separator, strlen(colon_separator)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
/* Send header value */
|
|
if (httpd_send_all(r, ra->resp_hdrs[i].value, strlen(ra->resp_hdrs[i].value)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
/* Send CR + LF */
|
|
if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
}
|
|
|
|
/* End header section */
|
|
if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
ra->first_chunk_sent = true;
|
|
}
|
|
|
|
/* Sending chunked content */
|
|
char len_str[10];
|
|
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;
|
|
}
|
|
|
|
if (buf) {
|
|
if (httpd_send_all(r, buf, (size_t) buf_len) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
}
|
|
|
|
/* Indicate end of chunk */
|
|
if (httpd_send_all(r, "\r\n", strlen("\r\n")) != ESP_OK) {
|
|
return ESP_ERR_HTTPD_RESP_SEND;
|
|
}
|
|
esp_http_server_event_data evt_data = {
|
|
.fd = ra->sd->fd,
|
|
.data_len = buf_len,
|
|
};
|
|
esp_http_server_dispatch_event(HTTP_SERVER_EVENT_SENT_DATA, &evt_data, sizeof(esp_http_server_event_data));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_code_t error, const char *usr_msg)
|
|
{
|
|
esp_err_t ret;
|
|
const char *msg;
|
|
const char *status;
|
|
|
|
switch (error) {
|
|
case HTTPD_501_METHOD_NOT_IMPLEMENTED:
|
|
status = "501 Method Not Implemented";
|
|
msg = "Server does not support this method";
|
|
break;
|
|
case HTTPD_505_VERSION_NOT_SUPPORTED:
|
|
status = "505 Version Not Supported";
|
|
msg = "HTTP version not supported by server";
|
|
break;
|
|
case HTTPD_400_BAD_REQUEST:
|
|
status = "400 Bad Request";
|
|
msg = "Bad request syntax";
|
|
break;
|
|
case HTTPD_401_UNAUTHORIZED:
|
|
status = "401 Unauthorized";
|
|
msg = "No permission -- see authorization schemes";
|
|
break;
|
|
case HTTPD_403_FORBIDDEN:
|
|
status = "403 Forbidden";
|
|
msg = "Request forbidden -- authorization will not help";
|
|
break;
|
|
case HTTPD_404_NOT_FOUND:
|
|
status = "404 Not Found";
|
|
msg = "Nothing matches the given URI";
|
|
break;
|
|
case HTTPD_405_METHOD_NOT_ALLOWED:
|
|
status = "405 Method Not Allowed";
|
|
msg = "Specified method is invalid for this resource";
|
|
break;
|
|
case HTTPD_408_REQ_TIMEOUT:
|
|
status = "408 Request Timeout";
|
|
msg = "Server closed this connection";
|
|
break;
|
|
case HTTPD_414_URI_TOO_LONG:
|
|
status = "414 URI Too Long";
|
|
msg = "URI is too long";
|
|
break;
|
|
case HTTPD_411_LENGTH_REQUIRED:
|
|
status = "411 Length Required";
|
|
msg = "Client must specify Content-Length";
|
|
break;
|
|
case HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE:
|
|
status = "431 Request Header Fields Too Large";
|
|
msg = "Header fields are too long";
|
|
break;
|
|
case HTTPD_500_INTERNAL_SERVER_ERROR:
|
|
default:
|
|
status = "500 Internal Server Error";
|
|
msg = "Server has encountered an unexpected error";
|
|
}
|
|
|
|
/* If user has provided custom message, override default message */
|
|
msg = usr_msg ? usr_msg : msg;
|
|
ESP_LOGW(TAG, LOG_FMT("%s - %s"), status, msg);
|
|
|
|
/* Set error code in HTTP response */
|
|
httpd_resp_set_status(req, status);
|
|
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
|
|
|
|
#ifdef CONFIG_HTTPD_ERR_RESP_NO_DELAY
|
|
/* Use TCP_NODELAY option to force socket to send data in buffer
|
|
* This ensures that the error message is sent before the socket
|
|
* is closed */
|
|
struct httpd_req_aux *ra = req->aux;
|
|
int nodelay = 1;
|
|
if (setsockopt(ra->sd->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) < 0) {
|
|
/* If failed to turn on TCP_NODELAY, throw warning and continue */
|
|
ESP_LOGW(TAG, LOG_FMT("error calling setsockopt : %d"), errno);
|
|
nodelay = 0;
|
|
}
|
|
#endif
|
|
|
|
/* Send HTTP error message */
|
|
ret = httpd_resp_send(req, msg, HTTPD_RESP_USE_STRLEN);
|
|
|
|
#ifdef CONFIG_HTTPD_ERR_RESP_NO_DELAY
|
|
/* If TCP_NODELAY was set successfully above, time to disable it */
|
|
if (nodelay == 1) {
|
|
nodelay = 0;
|
|
if (setsockopt(ra->sd->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) < 0) {
|
|
/* If failed to turn off TCP_NODELAY, throw error and
|
|
* return failure to signal for socket closure */
|
|
ESP_LOGE(TAG, LOG_FMT("error calling setsockopt : %d"), errno);
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
}
|
|
#endif
|
|
esp_http_server_dispatch_event(HTTP_SERVER_EVENT_ERROR, &error, sizeof(httpd_err_code_t));
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t httpd_register_err_handler(httpd_handle_t handle,
|
|
httpd_err_code_t error,
|
|
httpd_err_handler_func_t err_handler_fn)
|
|
{
|
|
if (handle == NULL || error >= HTTPD_ERR_CODE_MAX) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
struct httpd_data *hd = (struct httpd_data *) handle;
|
|
hd->err_handler_fns[error] = err_handler_fn;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t httpd_req_handle_err(httpd_req_t *req, httpd_err_code_t error)
|
|
{
|
|
struct httpd_data *hd = (struct httpd_data *) req->handle;
|
|
esp_err_t ret;
|
|
|
|
/* Invoke custom error handler if configured */
|
|
if (hd->err_handler_fns[error]) {
|
|
ret = hd->err_handler_fns[error](req, error);
|
|
|
|
/* If error code is 500, force return failure
|
|
* irrespective of the handler's return value */
|
|
ret = (error == HTTPD_500_INTERNAL_SERVER_ERROR ? ESP_FAIL : ret);
|
|
} else {
|
|
/* If no handler is registered for this error default
|
|
* behavior is to send the HTTP error response and
|
|
* return failure for closure of underlying socket */
|
|
httpd_resp_send_err(req, error, NULL);
|
|
ret = ESP_FAIL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len)
|
|
{
|
|
if (r == NULL || buf == NULL) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
ESP_LOGW(TAG, LOG_FMT("invalid request"));
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
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;
|
|
}
|
|
if (buf_len == 0) {
|
|
return buf_len;
|
|
}
|
|
|
|
int ret = httpd_recv(r, buf, buf_len);
|
|
if (ret < 0) {
|
|
ESP_LOGD(TAG, LOG_FMT("error in httpd_recv"));
|
|
return ret;
|
|
}
|
|
ra->remaining_len -= ret;
|
|
ESP_LOGD(TAG, LOG_FMT("received length = %d"), ret);
|
|
esp_http_server_event_data evt_data = {
|
|
.fd = ra->sd->fd,
|
|
.data_len = ret,
|
|
};
|
|
esp_http_server_dispatch_event(HTTP_SERVER_EVENT_ON_DATA, &evt_data, sizeof(esp_http_server_event_data));
|
|
return ret;
|
|
}
|
|
|
|
int httpd_req_to_sockfd(httpd_req_t *r)
|
|
{
|
|
if (r == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (!httpd_valid_req(r)) {
|
|
ESP_LOGW(TAG, LOG_FMT("invalid request"));
|
|
return -1;
|
|
}
|
|
|
|
struct httpd_req_aux *ra = r->aux;
|
|
return ra->sd->fd;
|
|
}
|
|
|
|
static int httpd_sock_err(const char *ctx, int sockfd)
|
|
{
|
|
int errval;
|
|
ESP_LOGW(TAG, LOG_FMT("error in %s : %d"), ctx, errno);
|
|
|
|
switch(errno) {
|
|
case EAGAIN:
|
|
case EINTR:
|
|
errval = HTTPD_SOCK_ERR_TIMEOUT;
|
|
break;
|
|
case EINVAL:
|
|
case EBADF:
|
|
case EFAULT:
|
|
case ENOTSOCK:
|
|
errval = HTTPD_SOCK_ERR_INVALID;
|
|
break;
|
|
default:
|
|
errval = HTTPD_SOCK_ERR_FAIL;
|
|
}
|
|
return errval;
|
|
}
|
|
|
|
int httpd_default_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags)
|
|
{
|
|
(void)hd;
|
|
if (buf == NULL) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
|
|
int ret = send(sockfd, buf, buf_len, flags);
|
|
if (ret < 0) {
|
|
return httpd_sock_err("send", sockfd);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags)
|
|
{
|
|
(void)hd;
|
|
if (buf == NULL) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
|
|
int ret = recv(sockfd, buf, buf_len, flags);
|
|
if (ret < 0) {
|
|
return httpd_sock_err("recv", sockfd);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int httpd_socket_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags)
|
|
{
|
|
struct sock_db *sess = httpd_sess_get(hd, sockfd);
|
|
if (!sess) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
if (!sess->send_fn) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
return sess->send_fn(hd, sockfd, buf, buf_len, flags);
|
|
}
|
|
|
|
int httpd_socket_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags)
|
|
{
|
|
struct sock_db *sess = httpd_sess_get(hd, sockfd);
|
|
if (!sess) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
if (!sess->recv_fn) {
|
|
return HTTPD_SOCK_ERR_INVALID;
|
|
}
|
|
return sess->recv_fn(hd, sockfd, buf, buf_len, flags);
|
|
}
|