extern "C" {
#endif
+/*
+note: esp_https_server.h includes a customized copy of this
+initializer that should be kept in sync
+*/
#define HTTPD_DEFAULT_CONFIG() { \
.task_priority = tskIDLE_PRIORITY+5, \
.stack_size = 4096, \
.lru_purge_enable = false, \
.recv_wait_timeout = 5, \
.send_wait_timeout = 5, \
-};
+ .global_user_ctx = NULL, \
+ .global_user_ctx_free_fn = NULL, \
+ .global_transport_ctx = NULL, \
+ .global_transport_ctx_free_fn = NULL, \
+ .open_fn = NULL, \
+ .close_fn = NULL, \
+}
#define ESP_ERR_HTTPD_BASE (0x8000) /*!< Starting number of HTTPD error codes */
#define ESP_ERR_HTTPD_HANDLERS_FULL (ESP_ERR_HTTPD_BASE + 1) /*!< All slots for registering URI handlers have been consumed */
*/
typedef enum http_method httpd_method_t;
+/**
+ * @brief Prototype for freeing context data (if any)
+ * @param[in] ctx : object to free
+ */
+typedef void (*httpd_free_ctx_fn_t)(void *ctx);
+
+/**
+ * @brief Function prototype for opening a session.
+ *
+ * Called immediately after the socket was opened to set up the send/recv functions and
+ * other parameters of the socket.
+ *
+ * @param[in] hd : server instance
+ * @param[in] sockfd : session socket file descriptor
+ * @return status
+ */
+typedef esp_err_t (*httpd_open_func_t)(httpd_handle_t hd, int sockfd);
+
+/**
+ * @brief Function prototype for closing a session.
+ *
+ * @note It's possible that the socket descriptor is invalid at this point, the function
+ * is called for all terminated sessions. Ensure proper handling of return codes.
+ *
+ * @param[in] hd : server instance
+ * @param[in] sockfd : session socket file descriptor
+ */
+typedef void (*httpd_close_func_t)(httpd_handle_t hd, int sockfd);
+
/**
* @brief HTTP Server Configuration Structure
*
bool lru_purge_enable; /*!< Purge "Least Recently Used" connection */
uint16_t recv_wait_timeout; /*!< Timeout for recv function (in seconds)*/
uint16_t send_wait_timeout; /*!< Timeout for send function (in seconds)*/
+
+ /**
+ * Global user context.
+ *
+ * This field can be used to store arbitrary user data within the server context.
+ * The value can be retrieved using the server handle, available e.g. in the httpd_req_t struct.
+ *
+ * When shutting down, the server frees up the user context by
+ * calling free() on the global_user_ctx field. If you wish to use a custom
+ * function for freeing the global user context, please specify that here.
+ */
+ void * global_user_ctx;
+ httpd_free_ctx_fn_t global_user_ctx_free_fn;
+
+ /**
+ * Global transport context.
+ *
+ * Similar to global_user_ctx, but used for session encoding or encryption (e.g. to hold the SSL context).
+ * It will be freed using free(), unless global_transport_ctx_free_fn is specified.
+ */
+ void * global_transport_ctx;
+ httpd_free_ctx_fn_t global_transport_ctx_free_fn;
+
+ /**
+ * Custom session opening callback.
+ *
+ * Called on a new session socket just after accept(), but before reading any data.
+ *
+ * This is an opportunity to set up e.g. SSL encryption using global_transport_ctx
+ * and the send/recv/pending session overrides.
+ *
+ * If a context needs to be maintained between these functions, store it in the session using
+ * httpd_sess_set_transport_ctx() and retrieve it later with httpd_sess_get_transport_ctx()
+ */
+ httpd_open_func_t open_fn;
+
+ /**
+ * Custom session closing callback.
+ *
+ * Called when a session is deleted, before freeing user and transport contexts and before
+ * closing the socket. This is a place for custom de-init code common to all sockets.
+ *
+ * Set the user or transport context to NULL if it was freed here, so the server does not
+ * try to free it again.
+ *
+ * This function is run for all terminated sessions, including sessions where the socket
+ * was closed by the network stack - that is, the file descriptor may not be valid anymore.
+ */
+ httpd_close_func_t close_fn;
} httpd_config_t;
/**
* @{
*/
-/**
- * @brief Function type for freeing context data (if any)
- */
-typedef void (*httpd_free_sess_ctx_fn_t)(void *sess_ctx);
-
/* Max supported HTTP request header length */
#define HTTPD_MAX_REQ_HDR_LEN CONFIG_HTTPD_MAX_REQ_HDR_LEN
* calling free() on the sess_ctx member. If you wish to use a custom
* function for freeing the session context, please specify that here.
*/
- httpd_free_sess_ctx_fn_t free_ctx;
+ httpd_free_ctx_fn_t free_ctx;
} httpd_req_t;
/**
* HTTPD_SOCK_ERR_ codes, which will eventually be conveyed as
* return value of httpd_send() function
*
+ * @param[in] hd : server instance
+ * @param[in] sockfd : session socket file descriptor
+ * @param[in] buf : buffer with bytes to send
+ * @param[in] buf_len : data size
+ * @param[in] flags : flags for the send() function
* @return
* - Bytes : The number of bytes sent successfully
* - HTTPD_SOCK_ERR_INVALID : Invalid arguments
* - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket send()
* - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket send()
*/
-typedef int (*httpd_send_func_t)(int sockfd, const char *buf, size_t buf_len, int flags);
+typedef int (*httpd_send_func_t)(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags);
/**
* @brief Prototype for HTTPDs low-level recv function
* HTTPD_SOCK_ERR_ codes, which will eventually be conveyed as
* return value of httpd_req_recv() function
*
+ * @param[in] hd : server instance
+ * @param[in] sockfd : session socket file descriptor
+ * @param[in] buf : buffer with bytes to send
+ * @param[in] buf_len : data size
+ * @param[in] flags : flags for the send() function
* @return
* - Bytes : The number of bytes received successfully
* - 0 : Buffer length parameter is zero / connection closed by peer
* - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket recv()
* - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket recv()
*/
-typedef int (*httpd_recv_func_t)(int sockfd, char *buf, size_t buf_len, int flags);
+typedef int (*httpd_recv_func_t)(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags);
+
+/**
+ * @brief Prototype for HTTPDs low-level "get pending bytes" function
+ *
+ * @note User specified pending function must handle errors internally,
+ * depending upon the set value of errno, and return specific
+ * HTTPD_SOCK_ERR_ codes, which will be handled accordingly in
+ * the server task.
+ *
+ * @param[in] hd : server instance
+ * @param[in] sockfd : session socket file descriptor
+ * @return
+ * - Bytes : The number of bytes waiting to be received
+ * - HTTPD_SOCK_ERR_INVALID : Invalid arguments
+ * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket pending()
+ * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket pending()
+ */
+typedef int (*httpd_pending_func_t)(httpd_handle_t hd, int sockfd);
/** End of TX / RX
* @}
*/
esp_err_t httpd_set_send_override(httpd_req_t *r, httpd_send_func_t send_func);
+/**
+ * @brief Override web server's pending function
+ *
+ * This function overrides the web server's pending function. This function is
+ * used to test for pending bytes in a socket.
+ *
+ * @note This API is supposed to be called only from the context of
+ * a URI handler where httpd_req_t* request pointer is valid.
+ *
+ * @param[in] r The request being responded to
+ * @param[in] pending_func The pending function to be set for this request
+ *
+ * @return
+ * - ESP_OK : On successfully registering override
+ * - ESP_ERR_INVALID_ARG : Null arguments
+ * - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
+ */
+esp_err_t httpd_set_pending_override(httpd_req_t *r, httpd_pending_func_t pending_func);
+
+/**
+ * @brief Override web server's send function (by session FD)
+ *
+ * @see httpd_set_send_override()
+ *
+ * @param[in] hd HTTPD instance handle
+ * @param[in] fd session socket FD
+ * @param[in] send_func The send function to be set for this session
+ *
+ * @return status code
+ */
+esp_err_t httpd_set_sess_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func);
+
+/**
+ * @brief Override web server's receive function (by session FD)
+ *
+ * @see httpd_set_recv_override()
+ *
+ * @param[in] hd HTTPD instance handle
+ * @param[in] fd session socket FD
+ * @param[in] recv_func The receive function to be set for this session
+ *
+ * @return status code
+ */
+esp_err_t httpd_set_sess_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func);
+
+/**
+ * @brief Override web server's pending function (by session FD)
+ *
+ * @see httpd_set_pending_override()
+ *
+ * @param[in] hd HTTPD instance handle
+ * @param[in] fd session socket FD
+ * @param[in] pending_func The receive function to be set for this session
+ *
+ * @return status code
+ */
+esp_err_t httpd_set_sess_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func);
+
/**
* @brief Get the Socket Descriptor from the HTTP request
*
*
* @param[in] r The request being responded to
* @param[in] buf Buffer from where the content is to be fetched
- * @param[in] buf_len Length of the buffer
+ * @param[in] buf_len Length of the buffer, -1 to use strlen()
*
* @return
* - ESP_OK : On successfully sending the response packet
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request
*/
-esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, size_t buf_len);
+esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len);
/**
* @brief API to send one HTTP chunk
*
* @param[in] r The request being responded to
* @param[in] buf Pointer to a buffer that stores the data
- * @param[in] buf_len Length of the data from the buffer that should be sent out
+ * @param[in] buf_len Length of the data from the buffer that should be sent out, -1 to use strlen()
*
* @return
* - ESP_OK : On successfully sending the response packet chunk
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
*/
-esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, size_t buf_len);
+esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len);
/* Some commonly used status codes */
#define HTTPD_200 "200 OK" /*!< HTTP Response 200 */
*/
void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd);
+/**
+ * @brief Set session context by socket descriptor
+ *
+ * @param[in] handle Handle to server returned by httpd_start
+ * @param[in] sockfd The socket descriptor for which the context should be extracted.
+ * @param[in] ctx Context object to assign to the session
+ * @param[in] free_fn Function that should be called to free the context
+ */
+void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn);
+
+/**
+ * @brief Get session 'transport' context by socket descriptor
+ * @see httpd_sess_get_ctx()
+ *
+ * This context is used by the send/receive functions, for example to manage SSL context.
+ *
+ * @param[in] handle Handle to server returned by httpd_start
+ * @param[in] sockfd The socket descriptor for which the context should be extracted.
+ * @return
+ * - void* : Pointer to the transport context associated with this session
+ * - NULL : Empty context / Invalid handle / Invalid socket fd
+ */
+void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd);
+
+/**
+ * @brief Set session 'transport' context by socket descriptor
+ * @see httpd_sess_set_ctx()
+ *
+ * @param[in] handle Handle to server returned by httpd_start
+ * @param[in] sockfd The socket descriptor for which the context should be extracted.
+ * @param[in] ctx Transport context object to assign to the session
+ * @param[in] free_fn Function that should be called to free the transport context
+ */
+void httpd_sess_set_transport_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn);
+
+/**
+ * @brief Get HTTPD global user context (it was set in the server config struct)
+ *
+ * @param[in] handle Handle to server returned by httpd_start
+ * @return global user context
+ */
+void *httpd_get_global_user_ctx(httpd_handle_t handle);
+
+/**
+ * @brief Get HTTPD global transport context (it was set in the server config struct)
+ *
+ * @param[in] handle Handle to server returned by httpd_start
+ * @return global transport context
+ */
+void *httpd_get_global_transport_ctx(httpd_handle_t handle);
+
/**
* @brief Trigger an httpd session close externally
*
struct sock_db {
int fd; /*!< The file descriptor for this socket */
void *ctx; /*!< A custom context for this socket */
+ void *transport_ctx; /*!< A custom 'transport' context for this socket, to be used by send/recv/pending */
httpd_handle_t handle; /*!< Server handle */
- httpd_free_sess_ctx_fn_t free_ctx; /*!< Function for freeing the context */
+ httpd_free_ctx_fn_t free_ctx; /*!< Function for freeing the context */
+ httpd_free_ctx_fn_t free_transport_ctx; /*!< Function for freeing the 'transport' context */
httpd_send_func_t send_fn; /*!< Send function for this socket */
- httpd_recv_func_t recv_fn; /*!< Send function for this socket */
+ httpd_recv_func_t recv_fn; /*!< Receive function for this socket */
+ httpd_pending_func_t pending_fn; /*!< Pending function for this socket */
int64_t timestamp; /*!< Timestamp indicating when the socket was last used */
char pending_data[PARSER_BLOCK_SIZE]; /*!< Buffer for pending data to be received */
size_t pending_len; /*!< Length of pending data to be received */
* @{
*/
+/**
+ * @brief Retrieve a session by its descriptor
+ *
+ * @param[in] hd Server instance data
+ * @param[in] sockfd Socket FD
+ * @return pointer into the socket DB, or NULL if not found
+ */
+struct sock_db *httpd_sess_get(struct httpd_data *hd, int sockfd);
+
+/**
+ * @brief Delete sessions whose FDs have became invalid.
+ * This is a recovery strategy e.g. after select() fails.
+ *
+ * @param[in] hd Server instance data
+ */
+void httpd_sess_delete_invalid(struct httpd_data *hd);
+
/**
* @brief Initializes an http session by resetting the sockets database.
*
* NEVER be called directly. The semantics of this is exactly similar to
* send() of the BSD socket API.
*
+ * @param[in] hd Server instance data
* @param[in] sockfd Socket descriptor for sending data
* @param[in] buf Pointer to the buffer from where the body of the response is taken
* @param[in] buf_len Length of the buffer
* - Length of data : if successful
* - -1 : if failed (appropriate errno is set)
*/
-int httpd_default_send(int sockfd, const char *buf, size_t buf_len, int flags);
+int httpd_default_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags);
/**
* @brief This is the low level default recv function of the HTTPD. This should
* NEVER be called directly. The semantics of this is exactly similar to
* recv() of the BSD socket API.
*
+ * @param[in] hd Server instance data
* @param[in] sockfd Socket descriptor for sending data
* @param[out] buf Pointer to the buffer which will be filled with the received data
* @param[in] buf_len Length of the buffer
* - Length of data : if successful
* - -1 : if failed (appropriate errno is set)
*/
-int httpd_default_recv(int sockfd, char *buf, size_t buf_len, int flags);
+int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags);
/** End of Group : Send and Receive
* @}
tv.tv_usec = 0;
setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
- if (httpd_sess_new(hd, new_fd)) {
- ESP_LOGW(TAG, LOG_FMT("no slots left for launching new session"));
+ if (ESP_OK != httpd_sess_new(hd, new_fd)) {
+ ESP_LOGW(TAG, LOG_FMT("session creation failed"));
close(new_fd);
return ESP_FAIL;
}
return ESP_OK;
}
+void *httpd_get_global_user_ctx(httpd_handle_t handle)
+{
+ return ((struct httpd_data *)handle)->config.global_user_ctx;
+}
+
+void *httpd_get_global_transport_ctx(httpd_handle_t handle)
+{
+ return ((struct httpd_data *)handle)->config.global_transport_ctx;
+}
+
static void httpd_close_all_sessions(struct httpd_data *hd)
{
int fd = -1;
int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL);
if (active_cnt < 0) {
ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno);
- /* Assert, as it's not possible to recover from this point onwards,
- * and there is no way to notify the main thread that server handle
- * has become invalid */
- assert(false);
- return ESP_FAIL;
+ httpd_sess_delete_invalid(hd);
+ return ESP_OK;
}
/* Case0: Do we have a control message? */
ESP_LOGD(TAG, LOG_FMT("sent control msg to stop server"));
while (hd->hd_td.status != THREAD_STOPPED) {
- httpd_os_thread_sleep(1000);
+ httpd_os_thread_sleep(100);
+ }
+
+ /* Release global user context, if not NULL */
+ if (hd->config.global_user_ctx) {
+ if (hd->config.global_user_ctx_free_fn) {
+ hd->config.global_user_ctx_free_fn(hd->config.global_user_ctx);
+ } else {
+ free(hd->config.global_user_ctx);
+ }
+ hd->config.global_user_ctx = NULL;
+ }
+
+ /* Release global transport context, if not NULL */
+ if (hd->config.global_transport_ctx) {
+ if (hd->config.global_transport_ctx_free_fn) {
+ hd->config.global_transport_ctx_free_fn(hd->config.global_transport_ctx);
+ } else {
+ free(hd->config.global_transport_ctx);
+ }
+ hd->config.global_transport_ctx = NULL;
}
ESP_LOGD(TAG, LOG_FMT("server stopped"));
return false;
}
-static struct sock_db *httpd_sess_get(struct httpd_data *hd, int newfd)
+struct sock_db *httpd_sess_get(struct httpd_data *hd, int newfd)
{
int i;
for (i = 0; i < hd->config.max_open_sockets; i++) {
hd->hd_sd[i].handle = (httpd_handle_t) hd;
hd->hd_sd[i].send_fn = httpd_default_send;
hd->hd_sd[i].recv_fn = httpd_default_recv;
+
+ /* Call user-defined session opening function */
+ if (hd->config.open_fn) {
+ esp_err_t ret = hd->config.open_fn(hd, hd->hd_sd[i].fd);
+ if (ret != ESP_OK) return ret;
+ }
return ESP_OK;
}
}
return NULL;
}
- struct httpd_data *hd = (struct httpd_data *) handle;
- struct sock_db *sd = httpd_sess_get(hd, sockfd);
+ struct sock_db *sd = httpd_sess_get(handle, sockfd);
if (sd == NULL) {
return NULL;
}
return sd->ctx;
}
+void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn)
+{
+ if (handle == NULL) {
+ return;
+ }
+
+ struct sock_db *sd = httpd_sess_get(handle, sockfd);
+ if (sd == NULL) {
+ return;
+ }
+
+ sd->ctx = ctx;
+ sd->free_ctx = free_fn;
+}
+
+void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd)
+{
+ if (handle == NULL) {
+ return NULL;
+ }
+
+ struct sock_db *sd = httpd_sess_get(handle, sockfd);
+ if (sd == NULL) {
+ return NULL;
+ }
+
+ return sd->transport_ctx;
+}
+
+void httpd_sess_set_transport_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn)
+{
+ if (handle == NULL) {
+ return;
+ }
+
+ struct sock_db *sd = httpd_sess_get(handle, sockfd);
+ if (sd == NULL) {
+ return;
+ }
+
+ sd->transport_ctx = ctx;
+ sd->free_transport_ctx = free_fn;
+}
+
void httpd_sess_set_descriptors(struct httpd_data *hd,
fd_set *fdset, int *maxfd)
{
}
}
+/** Check if a FD is valid */
+static int fd_is_valid(int fd)
+{
+ return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
+}
+
+void httpd_sess_delete_invalid(struct httpd_data *hd)
+{
+ for (int i = 0; i < hd->config.max_open_sockets; i++) {
+ if (hd->hd_sd[i].fd != -1 && !fd_is_valid(hd->hd_sd[i].fd)) {
+ ESP_LOGW(TAG, LOG_FMT("Closing invalid socket %d"), hd->hd_sd[i].fd);
+ httpd_sess_delete(hd, hd->hd_sd[i].fd);
+ }
+ }
+}
+
int httpd_sess_delete(struct httpd_data *hd, int fd)
{
ESP_LOGD(TAG, LOG_FMT("fd = %d"), fd);
int pre_sess_fd = -1;
for (i = 0; i < hd->config.max_open_sockets; i++) {
if (hd->hd_sd[i].fd == fd) {
- hd->hd_sd[i].fd = -1;
+ /* global close handler */
+ if (hd->config.close_fn) {
+ hd->config.close_fn(hd, fd);
+ }
+
+ /* release 'user' context */
if (hd->hd_sd[i].ctx) {
if (hd->hd_sd[i].free_ctx) {
hd->hd_sd[i].free_ctx(hd->hd_sd[i].ctx);
hd->hd_sd[i].ctx = NULL;
hd->hd_sd[i].free_ctx = NULL;
}
+
+ /* release 'transport' context */
+ if (hd->hd_sd[i].transport_ctx) {
+ if (hd->hd_sd[i].free_transport_ctx) {
+ hd->hd_sd[i].free_transport_ctx(hd->hd_sd[i].transport_ctx);
+ } else {
+ free(hd->hd_sd[i].transport_ctx);
+ }
+ hd->hd_sd[i].transport_ctx = NULL;
+ hd->hd_sd[i].free_transport_ctx = NULL;
+ }
+
+ /* mark session slot as available */
+ hd->hd_sd[i].fd = -1;
break;
} else if (hd->hd_sd[i].fd != -1) {
/* Return the fd just preceding the one being
return ESP_FAIL;
}
+ if (sd->pending_fn) {
+ // test if there's any data to be read (besides read() function, which is handled by select() in the main httpd loop)
+ // this should check e.g. for the SSL data buffer
+ if (sd->pending_fn(hd, fd) > 0) return true;
+ }
+
return (sd->pending_len != 0);
}
static const char *TAG = "httpd_txrx";
+esp_err_t httpd_set_sess_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_set_sess_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_set_sess_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;
+}
+
esp_err_t httpd_set_send_override(httpd_req_t *r, httpd_send_func_t send_func)
{
if (r == NULL || send_func == NULL) {
return ESP_OK;
}
+esp_err_t httpd_set_pending_override(httpd_req_t *r, httpd_pending_func_t pending_func)
+{
+ if (r == NULL || pending_func == 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->sd->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) {
}
struct httpd_req_aux *ra = r->aux;
- int ret = ra->sd->send_fn(ra->sd->fd, buf, buf_len, 0);
+ 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;
int ret;
while (buf_len > 0) {
- ret = ra->sd->send_fn(ra->sd->fd, buf, 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;
}
/* Receive data of remaining length */
- int ret = ra->sd->recv_fn(ra->sd->fd, buf, buf_len, 0);
+ 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)) {
return ESP_OK;
}
-esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, size_t buf_len)
+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;
const char *colon_separator = ": ";
const char *cr_lf_seperator = "\r\n";
+ if (buf_len == -1) buf_len = strlen(buf);
+
/* Request headers are no longer available */
ra->req_hdrs_count = 0;
return ESP_OK;
}
-esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, size_t buf_len)
+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;
return ESP_ERR_HTTPD_INVALID_REQ;
}
+ if (buf_len == -1) 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 = ": ";
}
if (buf) {
- if (httpd_send_all(r, buf, buf_len) != ESP_OK) {
+ if (httpd_send_all(r, buf, (size_t) buf_len) != ESP_OK) {
return ESP_ERR_HTTPD_RESP_SEND;
}
}
return errval;
}
-int httpd_default_send(int sockfd, const char *buf, size_t buf_len, int flags)
+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;
}
return ret;
}
-int httpd_default_recv(int sockfd, char *buf, size_t buf_len, int flags)
+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;
}