| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include <errno.h>
- #include <esp_log.h>
- #include <esp_err.h>
- #include <esp_http_server.h>
- #include "esp_httpd_priv.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 = %d"), 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);
- 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 = %d"), 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 = %d"), 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;
- }
- /* Sending content */
- if (buf && buf_len) {
- if (httpd_send_all(r, buf, buf_len) != ESP_OK) {
- return ESP_ERR_HTTPD_RESP_SEND;
- }
- }
- 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), "%x\r\n", 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;
- }
- 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 = "Request method is not supported by server";
- 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 = "Server unable to understand request due to invalid syntax";
- break;
- case HTTPD_404_NOT_FOUND:
- status = "404 Not Found";
- msg = "This URI does not exist";
- break;
- case HTTPD_405_METHOD_NOT_ALLOWED:
- status = "405 Method Not Allowed";
- msg = "Request method for this URI is not handled by server";
- 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 for server to interpret";
- break;
- case HTTPD_411_LENGTH_REQUIRED:
- status = "411 Length Required";
- msg = "Chunked encoding not supported by server";
- break;
- case HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE:
- status = "431 Request Header Fields Too Large";
- msg = "Header fields are too long for server to interpret";
- 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, strlen(msg));
- #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
- 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 = %d"), 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);
- 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;
- }
|