mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/http_server_err_handling' into 'master'
http_server : Add feature for invoking user configurable handlers during server errors See merge request idf/esp-idf!4229
This commit is contained in:
commit
f614f5bfa9
@ -13,4 +13,11 @@ menu "HTTP Server"
|
||||
help
|
||||
This sets the maximum supported size of HTTP request URI to be processed by the server
|
||||
|
||||
config HTTPD_ERR_RESP_NO_DELAY
|
||||
bool "Use TCP_NODELAY socket option when sending HTTP error responses"
|
||||
default y
|
||||
help
|
||||
Using TCP_NODEALY socket option ensures that HTTP error response reaches the client before the
|
||||
underlying socket is closed. Please note that turning this off may cause multiple test failures
|
||||
|
||||
endmenu
|
||||
|
@ -471,6 +471,122 @@ esp_err_t httpd_unregister_uri(httpd_handle_t handle, const char* uri);
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* ************** Group: HTTP Error ************** */
|
||||
/** @name HTTP Error
|
||||
* Prototype for HTTP errors and error handling functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Error codes sent as HTTP response in case of errors
|
||||
* encountered during processing of an HTTP request
|
||||
*/
|
||||
typedef enum {
|
||||
/* For any unexpected errors during parsing, like unexpected
|
||||
* state transitions, or unhandled errors.
|
||||
*/
|
||||
HTTPD_500_INTERNAL_SERVER_ERROR = 0,
|
||||
|
||||
/* For methods not supported by http_parser. Presently
|
||||
* http_parser halts parsing when such methods are
|
||||
* encountered and so the server responds with 400 Bad
|
||||
* Request error instead.
|
||||
*/
|
||||
HTTPD_501_METHOD_NOT_IMPLEMENTED,
|
||||
|
||||
/* When HTTP version is not 1.1 */
|
||||
HTTPD_505_VERSION_NOT_SUPPORTED,
|
||||
|
||||
/* Returned when http_parser halts parsing due to incorrect
|
||||
* syntax of request, unsupported method in request URI or
|
||||
* due to chunked encoding / upgrade field present in headers
|
||||
*/
|
||||
HTTPD_400_BAD_REQUEST,
|
||||
|
||||
/* When requested URI is not found */
|
||||
HTTPD_404_NOT_FOUND,
|
||||
|
||||
/* When URI found, but method has no handler registered */
|
||||
HTTPD_405_METHOD_NOT_ALLOWED,
|
||||
|
||||
/* Intended for recv timeout. Presently it's being sent
|
||||
* for other recv errors as well. Client should expect the
|
||||
* server to immediately close the connection after
|
||||
* responding with this.
|
||||
*/
|
||||
HTTPD_408_REQ_TIMEOUT,
|
||||
|
||||
/* Intended for responding to chunked encoding, which is
|
||||
* not supported currently. Though unhandled http_parser
|
||||
* callback for chunked request returns "400 Bad Request"
|
||||
*/
|
||||
HTTPD_411_LENGTH_REQUIRED,
|
||||
|
||||
/* URI length greater than CONFIG_HTTPD_MAX_URI_LEN */
|
||||
HTTPD_414_URI_TOO_LONG,
|
||||
|
||||
/* Headers section larger than CONFIG_HTTPD_MAX_REQ_HDR_LEN */
|
||||
HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE,
|
||||
|
||||
/* Used internally for retrieving the total count of errors */
|
||||
HTTPD_ERR_CODE_MAX
|
||||
} httpd_err_code_t;
|
||||
|
||||
/**
|
||||
* @brief Function prototype for HTTP error handling.
|
||||
*
|
||||
* This function is executed upon HTTP errors generated during
|
||||
* internal processing of an HTTP request. This is used to override
|
||||
* the default behavior on error, which is to send HTTP error response
|
||||
* and close the underlying socket.
|
||||
*
|
||||
* @note
|
||||
* - If implemented, the server will not automatically send out HTTP
|
||||
* error response codes, therefore, httpd_resp_send_err() must be
|
||||
* invoked inside this function if user wishes to generate HTTP
|
||||
* error responses.
|
||||
* - When invoked, the validity of `uri`, `method`, `content_len`
|
||||
* and `user_ctx` fields of the httpd_req_t parameter is not
|
||||
* guaranteed as the HTTP request may be partially received/parsed.
|
||||
* - The function must return ESP_OK if underlying socket needs to
|
||||
* be kept open. Any other value will ensure that the socket is
|
||||
* closed. The return value is ignored when error is of type
|
||||
* `HTTPD_500_INTERNAL_SERVER_ERROR` and the socket closed anyway.
|
||||
*
|
||||
* @param[in] req HTTP request for which the error needs to be handled
|
||||
* @param[in] error Error type
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : error handled successful
|
||||
* - ESP_FAIL : failure indicates that the underlying socket needs to be closed
|
||||
*/
|
||||
typedef esp_err_t (*httpd_err_handler_func_t)(httpd_req_t *req,
|
||||
httpd_err_code_t error);
|
||||
|
||||
/**
|
||||
* @brief Function for registering HTTP error handlers
|
||||
*
|
||||
* This function maps a handler function to any supported error code
|
||||
* given by `httpd_err_code_t`. See prototype `httpd_err_handler_func_t`
|
||||
* above for details.
|
||||
*
|
||||
* @param[in] handle HTTP server handle
|
||||
* @param[in] error Error type
|
||||
* @param[in] handler_fn User implemented handler function
|
||||
* (Pass NULL to unset any previously set handler)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : handler registered successfully
|
||||
* - ESP_ERR_INVALID_ARG : invalid error code or server handle
|
||||
*/
|
||||
esp_err_t httpd_register_err_handler(httpd_handle_t handle,
|
||||
httpd_err_code_t error,
|
||||
httpd_err_handler_func_t handler_fn);
|
||||
|
||||
/** End of HTTP Error
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* ************** Group: TX/RX ************** */
|
||||
/** @name TX / RX
|
||||
* Prototype for HTTPDs low-level send/recv functions
|
||||
@ -901,7 +1017,7 @@ esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len
|
||||
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
|
||||
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request
|
||||
*/
|
||||
inline esp_err_t httpd_resp_sendstr(httpd_req_t *r, const char *str) {
|
||||
static inline esp_err_t httpd_resp_sendstr(httpd_req_t *r, const char *str) {
|
||||
return httpd_resp_send(r, str, (str == NULL) ? 0 : strlen(str));
|
||||
}
|
||||
|
||||
@ -922,7 +1038,7 @@ inline esp_err_t httpd_resp_sendstr(httpd_req_t *r, const char *str) {
|
||||
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
|
||||
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request
|
||||
*/
|
||||
inline esp_err_t httpd_resp_sendstr_chunk(httpd_req_t *r, const char *str) {
|
||||
static inline esp_err_t httpd_resp_sendstr_chunk(httpd_req_t *r, const char *str) {
|
||||
return httpd_resp_send_chunk(r, str, (str == NULL) ? 0 : strlen(str));
|
||||
}
|
||||
|
||||
@ -1014,6 +1130,30 @@ esp_err_t httpd_resp_set_type(httpd_req_t *r, const char *type);
|
||||
*/
|
||||
esp_err_t httpd_resp_set_hdr(httpd_req_t *r, const char *field, const char *value);
|
||||
|
||||
/**
|
||||
* @brief For sending out error code in response to HTTP request.
|
||||
*
|
||||
* @note
|
||||
* - This API is supposed to be called only from the context of
|
||||
* a URI handler where httpd_req_t* request pointer is valid.
|
||||
* - Once this API is called, all request headers are purged, so
|
||||
* request headers need be copied into separate buffers if
|
||||
* they are required later.
|
||||
* - If you wish to send additional data in the body of the
|
||||
* response, please use the lower-level functions directly.
|
||||
*
|
||||
* @param[in] req Pointer to the HTTP request for which the response needs to be sent
|
||||
* @param[in] error Error type to send
|
||||
* @param[in] msg Error message string (pass NULL for default message)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : On successfully sending the response packet
|
||||
* - ESP_ERR_INVALID_ARG : Null arguments
|
||||
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
|
||||
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
|
||||
*/
|
||||
esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_code_t error, const char *msg);
|
||||
|
||||
/**
|
||||
* @brief Helper function for HTTP 404
|
||||
*
|
||||
@ -1035,7 +1175,9 @@ esp_err_t httpd_resp_set_hdr(httpd_req_t *r, const char *field, const char *valu
|
||||
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
|
||||
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
|
||||
*/
|
||||
esp_err_t httpd_resp_send_404(httpd_req_t *r);
|
||||
static inline esp_err_t httpd_resp_send_404(httpd_req_t *r) {
|
||||
return httpd_resp_send_err(r, HTTPD_404_NOT_FOUND, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function for HTTP 408
|
||||
@ -1058,7 +1200,9 @@ esp_err_t httpd_resp_send_404(httpd_req_t *r);
|
||||
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
|
||||
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
|
||||
*/
|
||||
esp_err_t httpd_resp_send_408(httpd_req_t *r);
|
||||
static inline esp_err_t httpd_resp_send_408(httpd_req_t *r) {
|
||||
return httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function for HTTP 500
|
||||
@ -1081,7 +1225,9 @@ esp_err_t httpd_resp_send_408(httpd_req_t *r);
|
||||
* - ESP_ERR_HTTPD_RESP_SEND : Error in raw send
|
||||
* - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
|
||||
*/
|
||||
esp_err_t httpd_resp_send_500(httpd_req_t *r);
|
||||
static inline esp_err_t httpd_resp_send_500(httpd_req_t *r) {
|
||||
return httpd_resp_send_err(r, HTTPD_500_INTERNAL_SERVER_ERROR, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Raw HTTP send
|
||||
|
@ -32,7 +32,7 @@ extern "C" {
|
||||
|
||||
/* 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 atleast be 8 bytes */
|
||||
* exceed the scratch buffer size and should at least be 8 bytes */
|
||||
#define PARSER_BLOCK_SIZE 128
|
||||
|
||||
/* Calculate the maximum size needed for the scratch buffer */
|
||||
@ -54,64 +54,6 @@ struct thread_data {
|
||||
} status; /*!< State of the thread */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Error codes sent by server in case of errors
|
||||
* encountered during processing of an HTTP request
|
||||
*/
|
||||
typedef enum {
|
||||
/* For any unexpected errors during parsing, like unexpected
|
||||
* state transitions, or unhandled errors.
|
||||
*/
|
||||
HTTPD_500_SERVER_ERROR = 0,
|
||||
|
||||
/* For methods not supported by http_parser. Presently
|
||||
* http_parser halts parsing when such methods are
|
||||
* encountered and so the server responds with 400 Bad
|
||||
* Request error instead.
|
||||
*/
|
||||
HTTPD_501_METHOD_NOT_IMPLEMENTED,
|
||||
|
||||
/* When HTTP version is not 1.1 */
|
||||
HTTPD_505_VERSION_NOT_SUPPORTED,
|
||||
|
||||
/* Returned when http_parser halts parsing due to incorrect
|
||||
* syntax of request, unsupported method in request URI or
|
||||
* due to chunked encoding option present in headers
|
||||
*/
|
||||
HTTPD_400_BAD_REQUEST,
|
||||
|
||||
/* When requested URI is not found */
|
||||
HTTPD_404_NOT_FOUND,
|
||||
|
||||
/* When URI found, but method has no handler registered */
|
||||
HTTPD_405_METHOD_NOT_ALLOWED,
|
||||
|
||||
/* Intended for recv timeout. Presently it's being sent
|
||||
* for other recv errors as well. Client should expect the
|
||||
* server to immediatly close the connection after
|
||||
* responding with this.
|
||||
*/
|
||||
HTTPD_408_REQ_TIMEOUT,
|
||||
|
||||
/* Intended for responding to chunked encoding, which is
|
||||
* not supported currently. Though unhandled http_parser
|
||||
* callback for chunked request returns "400 Bad Request"
|
||||
*/
|
||||
HTTPD_411_LENGTH_REQUIRED,
|
||||
|
||||
/* URI length greater than HTTPD_MAX_URI_LEN */
|
||||
HTTPD_414_URI_TOO_LONG,
|
||||
|
||||
/* Headers section larger thn HTTPD_MAX_REQ_HDR_LEN */
|
||||
HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE,
|
||||
|
||||
/* There is no particular HTTP error code for not supporting
|
||||
* upgrade. For this respond with 200 OK. Client expects status
|
||||
* code 101 if upgrade were supported, so 200 should be fine.
|
||||
*/
|
||||
HTTPD_XXX_UPGRADE_NOT_SUPPORTED
|
||||
} httpd_err_resp_t;
|
||||
|
||||
/**
|
||||
* @brief A database of all the open sockets in the system.
|
||||
*/
|
||||
@ -131,7 +73,7 @@ struct sock_db {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Auxilary data structure for use during reception and processing
|
||||
* @brief Auxiliary data structure for use during reception and processing
|
||||
* of requests and temporarily keeping responses
|
||||
*/
|
||||
struct httpd_req_aux {
|
||||
@ -151,7 +93,7 @@ struct httpd_req_aux {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Server data for each instance. This is exposed publicaly as
|
||||
* @brief Server data for each instance. This is exposed publicly as
|
||||
* httpd_handle_t but internal structure/members are kept private.
|
||||
*/
|
||||
struct httpd_data {
|
||||
@ -159,11 +101,14 @@ struct httpd_data {
|
||||
int listen_fd; /*!< Server listener FD */
|
||||
int ctrl_fd; /*!< Ctrl message receiver FD */
|
||||
int msg_fd; /*!< Ctrl message sender FD */
|
||||
struct thread_data hd_td; /*!< Information for the HTTPd thread */
|
||||
struct thread_data hd_td; /*!< Information for the HTTPD thread */
|
||||
struct sock_db *hd_sd; /*!< The socket database */
|
||||
httpd_uri_t **hd_calls; /*!< Registered URI handlers */
|
||||
struct httpd_req hd_req; /*!< The current HTTPD request */
|
||||
struct httpd_req_aux hd_req_aux; /*!< Additional data about the HTTPD request kept unexposed */
|
||||
|
||||
/* Array of registered error handler functions */
|
||||
httpd_err_handler_func_t *err_handler_fns;
|
||||
};
|
||||
|
||||
/******************* Group : Session Management ********************/
|
||||
@ -204,7 +149,7 @@ void httpd_sess_init(struct httpd_data *hd);
|
||||
* @param[in] newfd Descriptor of the new client to be added to the session.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : on successfully queueing the work
|
||||
* - ESP_OK : on successfully queuing the work
|
||||
* - ESP_FAIL : in case of control socket error while sending
|
||||
*/
|
||||
esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd);
|
||||
@ -226,7 +171,7 @@ esp_err_t httpd_sess_process(struct httpd_data *hd, int clifd);
|
||||
* and close the connection for this client.
|
||||
*
|
||||
* @note The returned descriptor should be used by httpd_sess_iterate()
|
||||
* to continue the iteration correctly. This ensurs that the
|
||||
* to continue the iteration correctly. This ensures that the
|
||||
* iteration is not restarted abruptly which may cause reading from
|
||||
* a socket which has been already processed and thus blocking
|
||||
* the server loop until data appears on that socket.
|
||||
@ -249,7 +194,7 @@ int httpd_sess_delete(struct httpd_data *hd, int clifd);
|
||||
void httpd_sess_free_ctx(void *ctx, httpd_free_ctx_fn_t free_fn);
|
||||
|
||||
/**
|
||||
* @brief Add descriptors present in the socket database to an fd_set and
|
||||
* @brief Add descriptors present in the socket database to an fdset and
|
||||
* update the value of maxfd which are needed by the select function
|
||||
* for looking through all available sockets for incoming data.
|
||||
*
|
||||
@ -288,12 +233,12 @@ bool httpd_is_sess_available(struct httpd_data *hd);
|
||||
* @brief Checks if session has any pending data/packets
|
||||
* for processing
|
||||
*
|
||||
* This is needed as httpd_unrecv may unreceive next
|
||||
* This is needed as httpd_unrecv may un-receive next
|
||||
* packet in the stream. If only partial packet was
|
||||
* received then select() would mark the fd for processing
|
||||
* as remaining part of the packet would still be in socket
|
||||
* recv queue. But if a complete packet got unreceived
|
||||
* then it would not be processed until furtur data is
|
||||
* then it would not be processed until further data is
|
||||
* received on the socket. This is when this function
|
||||
* comes in use, as it checks the socket's pending data
|
||||
* buffer.
|
||||
@ -343,7 +288,7 @@ esp_err_t httpd_sess_close_lru(struct httpd_data *hd);
|
||||
esp_err_t httpd_uri(struct httpd_data *hd);
|
||||
|
||||
/**
|
||||
* @brief Deregister all URI handlers
|
||||
* @brief Unregister all URI handlers
|
||||
*
|
||||
* @param[in] hd Server instance data
|
||||
*/
|
||||
@ -353,7 +298,7 @@ void httpd_unregister_all_uri_handlers(struct httpd_data *hd);
|
||||
* @brief Validates the request to prevent users from calling APIs, that are to
|
||||
* be called only inside a URI handler, outside the handler context
|
||||
*
|
||||
* @param[in] req Pointer to HTTP request that neds to be validated
|
||||
* @param[in] req Pointer to HTTP request that needs to be validated
|
||||
*
|
||||
* @return
|
||||
* - true : if valid request
|
||||
@ -363,7 +308,7 @@ bool httpd_validate_req_ptr(httpd_req_t *r);
|
||||
|
||||
/* httpd_validate_req_ptr() adds some overhead to frequently used APIs,
|
||||
* and is useful mostly for debugging, so it's preferable to disable
|
||||
* the check by defaut and enable it only if necessary */
|
||||
* the check by default and enable it only if necessary */
|
||||
#ifdef CONFIG_HTTPD_VALIDATE_REQ
|
||||
#define httpd_valid_req(r) httpd_validate_req_ptr(r)
|
||||
#else
|
||||
@ -409,6 +354,19 @@ esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd);
|
||||
*/
|
||||
esp_err_t httpd_req_delete(struct httpd_data *hd);
|
||||
|
||||
/**
|
||||
* @brief For handling HTTP errors by invoking registered
|
||||
* error handler function
|
||||
*
|
||||
* @param[in] req Pointer to the HTTP request for which error occurred
|
||||
* @param[in] error Error type
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : error handled successful
|
||||
* - ESP_FAIL : failure indicates that the underlying socket needs to be closed
|
||||
*/
|
||||
esp_err_t httpd_req_handle_err(httpd_req_t *req, httpd_err_code_t error);
|
||||
|
||||
/** End of Group : Parsing
|
||||
* @}
|
||||
*/
|
||||
@ -419,22 +377,10 @@ esp_err_t httpd_req_delete(struct httpd_data *hd);
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief For sending out error code in response to HTTP request.
|
||||
*
|
||||
* @param[in] req Pointer to the HTTP request for which the resonse needs to be sent
|
||||
* @param[in] error Error type to send
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : if successful
|
||||
* - ESP_FAIL : if failed
|
||||
*/
|
||||
esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_resp_t error);
|
||||
|
||||
/**
|
||||
* @brief For sending out data in response to an HTTP request.
|
||||
*
|
||||
* @param[in] req Pointer to the HTTP request for which the resonse needs to be sent
|
||||
* @param[in] req Pointer to the HTTP request for which the response needs to be sent
|
||||
* @param[in] buf Pointer to the buffer from where the body of the response is taken
|
||||
* @param[in] buf_len Length of the buffer
|
||||
*
|
||||
@ -457,7 +403,7 @@ int httpd_send(httpd_req_t *req, const char *buf, size_t buf_len);
|
||||
* @param[in] req Pointer to new HTTP request which only has the socket descriptor
|
||||
* @param[out] buf Pointer to the buffer which will be filled with the received data
|
||||
* @param[in] buf_len Length of the buffer
|
||||
* @param[in] halt_after_pending When set true, halts immediatly after receiving from
|
||||
* @param[in] halt_after_pending When set true, halts immediately after receiving from
|
||||
* pending buffer
|
||||
*
|
||||
* @return
|
||||
|
@ -288,31 +288,43 @@ static struct httpd_data *httpd_create(const httpd_config_t *config)
|
||||
{
|
||||
/* Allocate memory for httpd instance data */
|
||||
struct httpd_data *hd = calloc(1, sizeof(struct httpd_data));
|
||||
if (hd != NULL) {
|
||||
hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *));
|
||||
if (hd->hd_calls == NULL) {
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db));
|
||||
if (hd->hd_sd == NULL) {
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
struct httpd_req_aux *ra = &hd->hd_req_aux;
|
||||
ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr));
|
||||
if (ra->resp_hdrs == NULL) {
|
||||
free(hd->hd_sd);
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
/* Save the configuration for this instance */
|
||||
hd->config = *config;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "mem alloc failed");
|
||||
if (!hd) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance"));
|
||||
return NULL;
|
||||
}
|
||||
hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *));
|
||||
if (!hd->hd_calls) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers"));
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db));
|
||||
if (!hd->hd_sd) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data"));
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
struct httpd_req_aux *ra = &hd->hd_req_aux;
|
||||
ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr));
|
||||
if (!ra->resp_hdrs) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers"));
|
||||
free(hd->hd_sd);
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t));
|
||||
if (!hd->err_handler_fns) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers"));
|
||||
free(ra->resp_hdrs);
|
||||
free(hd->hd_sd);
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
/* Save the configuration for this instance */
|
||||
hd->config = *config;
|
||||
return hd;
|
||||
}
|
||||
|
||||
@ -320,6 +332,7 @@ static void httpd_delete(struct httpd_data *hd)
|
||||
{
|
||||
struct httpd_req_aux *ra = &hd->hd_req_aux;
|
||||
/* Free memory of httpd instance data */
|
||||
free(hd->err_handler_fns);
|
||||
free(ra->resp_hdrs);
|
||||
free(hd->hd_sd);
|
||||
|
||||
|
@ -46,7 +46,7 @@ typedef struct {
|
||||
} status;
|
||||
|
||||
/* Response error code in case of PARSING_FAILED */
|
||||
httpd_err_resp_t error;
|
||||
httpd_err_code_t error;
|
||||
|
||||
/* For storing last callback parameters */
|
||||
struct {
|
||||
@ -81,7 +81,6 @@ static esp_err_t verify_url (http_parser *parser)
|
||||
ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
|
||||
length, sizeof(r->uri));
|
||||
parser_data->error = HTTPD_414_URI_TOO_LONG;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -128,6 +127,7 @@ static esp_err_t cb_url(http_parser *parser,
|
||||
parser_data->status = PARSING_URL;
|
||||
} else if (parser_data->status != PARSING_URL) {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -194,6 +194,9 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
|
||||
/* Check previous status */
|
||||
if (parser_data->status == PARSING_URL) {
|
||||
if (verify_url(parser) != ESP_OK) {
|
||||
/* verify_url would already have set the
|
||||
* error field of parser data, so only setting
|
||||
* status to failed */
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -207,6 +210,7 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
|
||||
|
||||
/* Stop parsing for now and give control to process */
|
||||
if (pause_parsing(parser, at) != ESP_OK) {
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -221,6 +225,7 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
|
||||
parser_data->status = PARSING_HDR_FIELD;
|
||||
} else if (parser_data->status != PARSING_HDR_FIELD) {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -251,6 +256,7 @@ static esp_err_t cb_header_value(http_parser *parser, const char *at, size_t len
|
||||
ra->req_hdrs_count++;
|
||||
} else if (parser_data->status != PARSING_HDR_VALUE) {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -275,6 +281,9 @@ static esp_err_t cb_headers_complete(http_parser *parser)
|
||||
if (parser_data->status == PARSING_URL) {
|
||||
ESP_LOGD(TAG, LOG_FMT("no headers"));
|
||||
if (verify_url(parser) != ESP_OK) {
|
||||
/* verify_url would already have set the
|
||||
* error field of parser data, so only setting
|
||||
* status to failed */
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -287,6 +296,7 @@ static esp_err_t cb_headers_complete(http_parser *parser)
|
||||
parser_data->last.at += parser_data->last.length;
|
||||
} else {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -300,7 +310,9 @@ static esp_err_t cb_headers_complete(http_parser *parser)
|
||||
|
||||
if (parser->upgrade) {
|
||||
ESP_LOGW(TAG, LOG_FMT("upgrade from HTTP not supported"));
|
||||
parser_data->error = HTTPD_XXX_UPGRADE_NOT_SUPPORTED;
|
||||
/* There is no specific HTTP error code to notify the client that
|
||||
* upgrade is not supported, thus sending 400 Bad Request */
|
||||
parser_data->error = HTTPD_400_BAD_REQUEST;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -320,6 +332,7 @@ static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
|
||||
/* Check previous status */
|
||||
if (parser_data->status != PARSING_BODY) {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -329,6 +342,7 @@ static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
|
||||
* may reset the parser state and cause current
|
||||
* request packet to be lost */
|
||||
if (pause_parsing(parser, at) != ESP_OK) {
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -352,11 +366,15 @@ static esp_err_t cb_no_body(http_parser *parser)
|
||||
if (parser_data->status == PARSING_URL) {
|
||||
ESP_LOGD(TAG, LOG_FMT("no headers"));
|
||||
if (verify_url(parser) != ESP_OK) {
|
||||
/* verify_url would already have set the
|
||||
* error field of parser data, so only setting
|
||||
* status to failed */
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else if (parser_data->status != PARSING_BODY) {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -369,6 +387,7 @@ static esp_err_t cb_no_body(http_parser *parser)
|
||||
* may reset the parser state and cause current
|
||||
* request packet to be lost */
|
||||
if (pause_parsing(parser, at) != ESP_OK) {
|
||||
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
parser_data->status = PARSING_FAILED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -396,13 +415,25 @@ static int read_block(httpd_req_t *req, size_t offset, size_t length)
|
||||
int nbytes = httpd_recv_with_opt(req, raux->scratch + offset, buf_len, true);
|
||||
if (nbytes < 0) {
|
||||
ESP_LOGD(TAG, LOG_FMT("error in httpd_recv"));
|
||||
/* If timeout occurred allow the
|
||||
* situation to be handled */
|
||||
if (nbytes == HTTPD_SOCK_ERR_TIMEOUT) {
|
||||
httpd_resp_send_err(req, HTTPD_408_REQ_TIMEOUT);
|
||||
/* Invoke error handler which may return ESP_OK
|
||||
* to signal for retrying call to recv(), else it may
|
||||
* return ESP_FAIL to signal for closure of socket */
|
||||
return (httpd_req_handle_err(req, HTTPD_408_REQ_TIMEOUT) == ESP_OK) ?
|
||||
HTTPD_SOCK_ERR_TIMEOUT : HTTPD_SOCK_ERR_FAIL;
|
||||
}
|
||||
return -1;
|
||||
/* Some socket error occurred. Return failure
|
||||
* to force closure of underlying socket.
|
||||
* Error message is not sent as socket may not
|
||||
* be valid anymore */
|
||||
return HTTPD_SOCK_ERR_FAIL;
|
||||
} else if (nbytes == 0) {
|
||||
ESP_LOGD(TAG, LOG_FMT("connection closed"));
|
||||
return -1;
|
||||
/* Connection closed by client so no
|
||||
* need to send error response */
|
||||
return HTTPD_SOCK_ERR_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, LOG_FMT("received HTTP request block size = %d"), nbytes);
|
||||
@ -417,7 +448,11 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
|
||||
size_t nparsed = 0;
|
||||
|
||||
if (!length) {
|
||||
ESP_LOGW(TAG, LOG_FMT("response uri/header too big"));
|
||||
/* Parsing is still happening but nothing to
|
||||
* parse means no more space left on buffer,
|
||||
* therefore it can be inferred that the
|
||||
* request URI/header must be too long */
|
||||
ESP_LOGW(TAG, LOG_FMT("request URI/header too long"));
|
||||
switch (data->status) {
|
||||
case PARSING_URL:
|
||||
data->error = HTTPD_414_URI_TOO_LONG;
|
||||
@ -425,14 +460,17 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
|
||||
case PARSING_HDR_FIELD:
|
||||
case PARSING_HDR_VALUE:
|
||||
data->error = HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state"));
|
||||
data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
|
||||
break;
|
||||
}
|
||||
data->status = PARSING_FAILED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Unpause the parsing if paused */
|
||||
/* Un-pause the parsing if paused */
|
||||
if (data->paused) {
|
||||
nparsed = continue_parsing(parser, length);
|
||||
length -= nparsed;
|
||||
@ -448,6 +486,8 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
|
||||
|
||||
/* Check state */
|
||||
if (data->status == PARSING_FAILED) {
|
||||
/* It is expected that the error field of
|
||||
* parser data should have been set by now */
|
||||
ESP_LOGW(TAG, LOG_FMT("parsing failed"));
|
||||
return -1;
|
||||
} else if (data->paused) {
|
||||
@ -457,8 +497,8 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
|
||||
return 0;
|
||||
} else if (nparsed != length) {
|
||||
/* http_parser error */
|
||||
data->status = PARSING_FAILED;
|
||||
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);
|
||||
return -1;
|
||||
@ -508,7 +548,16 @@ static esp_err_t httpd_parse_req(struct httpd_data *hd)
|
||||
do {
|
||||
/* Read block into scratch buffer */
|
||||
if ((blk_len = read_block(r, offset, PARSER_BLOCK_SIZE)) < 0) {
|
||||
/* Return error to close socket */
|
||||
if (blk_len == HTTPD_SOCK_ERR_TIMEOUT) {
|
||||
/* Retry read in case of non-fatal timeout error.
|
||||
* read_block() ensures that the timeout error is
|
||||
* handled properly so that this doesn't get stuck
|
||||
* in an infinite loop */
|
||||
continue;
|
||||
}
|
||||
/* If not HTTPD_SOCK_ERR_TIMEOUT, returned error must
|
||||
* be HTTPD_SOCK_ERR_FAIL which means we need to return
|
||||
* failure and thereby close the underlying socket */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -518,8 +567,10 @@ static esp_err_t httpd_parse_req(struct httpd_data *hd)
|
||||
|
||||
/* Parse data block from buffer */
|
||||
if ((offset = parse_block(&parser, offset, blk_len)) < 0) {
|
||||
/* Server/Client error. Send error code as response status */
|
||||
return httpd_resp_send_err(r, parser_data.error);
|
||||
/* HTTP error occurred.
|
||||
* Send error code as response status and
|
||||
* invoke error handler */
|
||||
return httpd_req_handle_err(r, parser_data.error);
|
||||
}
|
||||
} while (parser_data.status != PARSING_COMPLETE);
|
||||
|
||||
|
@ -379,78 +379,128 @@ esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t httpd_resp_send_404(httpd_req_t *r)
|
||||
{
|
||||
return httpd_resp_send_err(r, HTTPD_404_NOT_FOUND);
|
||||
}
|
||||
|
||||
esp_err_t httpd_resp_send_408(httpd_req_t *r)
|
||||
{
|
||||
return httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT);
|
||||
}
|
||||
|
||||
esp_err_t httpd_resp_send_500(httpd_req_t *r)
|
||||
{
|
||||
return httpd_resp_send_err(r, HTTPD_500_SERVER_ERROR);
|
||||
}
|
||||
|
||||
esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_resp_t error)
|
||||
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 doesn't 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_XXX_UPGRADE_NOT_SUPPORTED:
|
||||
/* If the server does not support upgrade, or is unable to upgrade
|
||||
* it responds with a standard HTTP/1.1 response */
|
||||
status = "200 OK";
|
||||
msg = "Upgrade not supported by server";
|
||||
break;
|
||||
case HTTPD_500_SERVER_ERROR:
|
||||
default:
|
||||
status = "500 Server Error";
|
||||
msg = "Server has encountered an unexpected 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);
|
||||
|
||||
httpd_resp_set_status (req, status);
|
||||
httpd_resp_set_type (req, HTTPD_TYPE_TEXT);
|
||||
return httpd_resp_send (req, msg, strlen(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)
|
||||
|
@ -93,7 +93,7 @@ bool httpd_uri_match_wildcard(const char *template, const char *uri, size_t len)
|
||||
static httpd_uri_t* httpd_find_uri_handler(struct httpd_data *hd,
|
||||
const char *uri, size_t uri_len,
|
||||
httpd_method_t method,
|
||||
httpd_err_resp_t *err)
|
||||
httpd_err_code_t *err)
|
||||
{
|
||||
if (err) {
|
||||
*err = HTTPD_404_NOT_FOUND;
|
||||
@ -279,7 +279,7 @@ esp_err_t httpd_uri(struct httpd_data *hd)
|
||||
struct http_parser_url *res = &hd->hd_req_aux.url_parse_res;
|
||||
|
||||
/* For conveying URI not found/method not allowed */
|
||||
httpd_err_resp_t err = 0;
|
||||
httpd_err_code_t err = 0;
|
||||
|
||||
ESP_LOGD(TAG, LOG_FMT("request for %s with type %d"), req->uri, req->method);
|
||||
|
||||
@ -294,11 +294,11 @@ esp_err_t httpd_uri(struct httpd_data *hd)
|
||||
switch (err) {
|
||||
case HTTPD_404_NOT_FOUND:
|
||||
ESP_LOGW(TAG, LOG_FMT("URI '%s' not found"), req->uri);
|
||||
return httpd_resp_send_err(req, HTTPD_404_NOT_FOUND);
|
||||
return httpd_req_handle_err(req, HTTPD_404_NOT_FOUND);
|
||||
case HTTPD_405_METHOD_NOT_ALLOWED:
|
||||
ESP_LOGW(TAG, LOG_FMT("Method '%d' not allowed for URI '%s'"),
|
||||
req->method, req->uri);
|
||||
return httpd_resp_send_err(req, HTTPD_405_METHOD_NOT_ALLOWED);
|
||||
return httpd_req_handle_err(req, HTTPD_405_METHOD_NOT_ALLOWED);
|
||||
default:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ typedef struct httpd_ssl_config httpd_ssl_config_t;
|
||||
.global_transport_ctx_free_fn = NULL, \
|
||||
.open_fn = NULL, \
|
||||
.close_fn = NULL, \
|
||||
.uri_match_fn = NULL \
|
||||
}, \
|
||||
.cacert_pem = NULL, \
|
||||
.cacert_len = 0, \
|
||||
|
@ -55,6 +55,7 @@ esp_err_t echo_post_handler(httpd_req_t *req)
|
||||
int ret;
|
||||
|
||||
if (!buf) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", req->content_len + 1);
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -84,12 +85,15 @@ esp_err_t echo_post_handler(httpd_req_t *req)
|
||||
if (hdr_len) {
|
||||
/* Read Custom header value */
|
||||
req_hdr = malloc(hdr_len + 1);
|
||||
if (req_hdr) {
|
||||
httpd_req_get_hdr_value_str(req, "Custom", req_hdr, hdr_len + 1);
|
||||
|
||||
/* Set as additional header for response packet */
|
||||
httpd_resp_set_hdr(req, "Custom", req_hdr);
|
||||
if (!req_hdr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", hdr_len + 1);
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
httpd_req_get_hdr_value_str(req, "Custom", req_hdr, hdr_len + 1);
|
||||
|
||||
/* Set as additional header for response packet */
|
||||
httpd_resp_set_hdr(req, "Custom", req_hdr);
|
||||
}
|
||||
httpd_resp_send(req, buf, req->content_len);
|
||||
free (req_hdr);
|
||||
|
@ -367,7 +367,7 @@ def put_hello(dut, port):
|
||||
|
||||
def post_hello(dut, port):
|
||||
# POST /hello returns 405'
|
||||
Utility.console_log("[test] POST /hello returns 404 =>", end=' ')
|
||||
Utility.console_log("[test] POST /hello returns 405 =>", end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("POST", "/hello", "Hello")
|
||||
resp = conn.getresponse()
|
||||
@ -541,8 +541,10 @@ def leftover_data_test(dut, port):
|
||||
if not test_val("False URI Status", str(404), str(resp.status)):
|
||||
s.close()
|
||||
return False
|
||||
resp.read()
|
||||
# socket would have been closed by server due to error
|
||||
s.close()
|
||||
|
||||
s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
|
||||
s.request("GET", url='/hello')
|
||||
resp = s.getresponse()
|
||||
if not test_val("Hello World Data", "Hello World!", resp.read().decode()):
|
||||
@ -637,7 +639,7 @@ def code_500_server_error_test(dut, port):
|
||||
Utility.console_log("[test] 500 Server Error test =>", end=' ')
|
||||
s = Session(dut, port)
|
||||
# Sending a very large content length will cause malloc to fail
|
||||
content_len = 2**31
|
||||
content_len = 2**30
|
||||
s.client.sendall(("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: " + str(content_len) + "\r\n\r\nABCD").encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
@ -802,7 +804,7 @@ def send_postx_hdr_len(dut, port, length):
|
||||
hdr = s.read_resp_hdrs()
|
||||
resp = s.read_resp_data()
|
||||
s.close()
|
||||
if "Custom" in hdr:
|
||||
if hdr and ("Custom" in hdr):
|
||||
return (hdr["Custom"] == custom_hdr_val), resp
|
||||
return False, s.status
|
||||
|
||||
@ -826,7 +828,7 @@ def test_upgrade_not_supported(dut, port):
|
||||
s.client.sendall(("OPTIONS * HTTP/1.1\r\nHost:" + dut + "\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n").encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Client Error", "200", s.status):
|
||||
if not test_val("Client Error", "400", s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
|
@ -72,9 +72,10 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req)
|
||||
const size_t entrypath_offset = strlen(fullpath);
|
||||
|
||||
if (!dir) {
|
||||
/* If opening directory failed then send 404 server error */
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_OK;
|
||||
ESP_LOGE(TAG, "Failed to stat dir : %s", fullpath);
|
||||
/* Respond with 404 Not Found */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Send HTML file header */
|
||||
@ -172,18 +173,17 @@ static esp_err_t http_resp_file(httpd_req_t *req)
|
||||
strcat(filepath, req->uri);
|
||||
if (stat(filepath, &file_stat) == -1) {
|
||||
ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
|
||||
/* If file doesn't exist respond with 404 Not Found */
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_OK;
|
||||
/* Respond with 404 Not Found */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fd = fopen(filepath, "r");
|
||||
if (!fd) {
|
||||
ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
|
||||
/* If file exists but unable to open respond with 500 Server Error */
|
||||
httpd_resp_set_status(req, "500 Server Error");
|
||||
httpd_resp_sendstr(req, "Failed to read existing file!");
|
||||
return ESP_OK;
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filepath, file_stat.st_size);
|
||||
@ -202,10 +202,9 @@ static esp_err_t http_resp_file(httpd_req_t *req)
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Send error message with status code */
|
||||
httpd_resp_set_status(req, "500 Server Error");
|
||||
httpd_resp_sendstr(req, "Failed to send file!");
|
||||
return ESP_OK;
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Keep looping till the whole file is sent */
|
||||
@ -249,10 +248,8 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
if (strlen(filename) == 0 || filename[strlen(filename) - 1] == '/') {
|
||||
ESP_LOGE(TAG, "Invalid file name : %s", filename);
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
/* Send failure reason */
|
||||
httpd_resp_sendstr(req, "Invalid file name!");
|
||||
return ESP_OK;
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid file name");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Retrieve the base path of file storage to construct the full path */
|
||||
@ -262,18 +259,18 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
strcat(filepath, filename);
|
||||
if (stat(filepath, &file_stat) == 0) {
|
||||
ESP_LOGE(TAG, "File already exists : %s", filepath);
|
||||
/* If file exists respond with 400 Bad Request */
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
httpd_resp_sendstr(req, "File already exists!");
|
||||
return ESP_OK;
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* File cannot be larger than a limit */
|
||||
if (req->content_len > MAX_FILE_SIZE) {
|
||||
ESP_LOGE(TAG, "File too large : %d bytes", req->content_len);
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
httpd_resp_sendstr(req, "File size must be less than "
|
||||
MAX_FILE_SIZE_STR "!");
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
||||
"File size must be less than "
|
||||
MAX_FILE_SIZE_STR "!");
|
||||
/* Return failure to close underlying connection else the
|
||||
* incoming file content will keep the socket busy */
|
||||
return ESP_FAIL;
|
||||
@ -282,10 +279,9 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
fd = fopen(filepath, "w");
|
||||
if (!fd) {
|
||||
ESP_LOGE(TAG, "Failed to create file : %s", filepath);
|
||||
/* If file creation failed, respond with 500 Server Error */
|
||||
httpd_resp_set_status(req, "500 Server Error");
|
||||
httpd_resp_sendstr(req, "Failed to create file!");
|
||||
return ESP_OK;
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Receiving file : %s...", filename);
|
||||
@ -314,10 +310,9 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
unlink(filepath);
|
||||
|
||||
ESP_LOGE(TAG, "File reception failed!");
|
||||
/* Return failure reason with status code */
|
||||
httpd_resp_set_status(req, "500 Server Error");
|
||||
httpd_resp_sendstr(req, "Failed to receive file!");
|
||||
return ESP_OK;
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Write buffer content to file on storage */
|
||||
@ -328,9 +323,9 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
unlink(filepath);
|
||||
|
||||
ESP_LOGE(TAG, "File write failed!");
|
||||
httpd_resp_set_status(req, "500 Server Error");
|
||||
httpd_resp_sendstr(req, "Failed to write file to storage!");
|
||||
return ESP_OK;
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Keep track of remaining size of
|
||||
@ -363,10 +358,8 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||
if (strlen(filename) == 0 || filename[strlen(filename) - 1] == '/') {
|
||||
ESP_LOGE(TAG, "Invalid file name : %s", filename);
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
/* Send failure reason */
|
||||
httpd_resp_sendstr(req, "Invalid file name!");
|
||||
return ESP_OK;
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid file name");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Retrieve the base path of file storage to construct the full path */
|
||||
@ -376,10 +369,9 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||
strcat(filepath, filename);
|
||||
if (stat(filepath, &file_stat) == -1) {
|
||||
ESP_LOGE(TAG, "File does not exist : %s", filename);
|
||||
/* If file does not exist respond with 400 Bad Request */
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
httpd_resp_sendstr(req, "File does not exist!");
|
||||
return ESP_OK;
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Deleting file : %s", filename);
|
||||
|
@ -152,6 +152,33 @@ httpd_uri_t echo = {
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
/* This handler allows the custom error handling functionality to be
|
||||
* tested from client side. For that, when a PUT request 0 is sent to
|
||||
* URI /ctrl, the /hello and /echo URIs are unregistered and following
|
||||
* custom error handler http_404_error_handler() is registered.
|
||||
* Afterwards, when /hello or /echo is requested, this custom error
|
||||
* handler is invoked which, after sending an error message to client,
|
||||
* either closes the underlying socket (when requested URI is /echo)
|
||||
* or keeps it open (when requested URI is /hello). This allows the
|
||||
* client to infer if the custom error handler is functioning as expected
|
||||
* by observing the socket state.
|
||||
*/
|
||||
esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
|
||||
{
|
||||
if (strcmp("/hello", req->uri) == 0) {
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/hello URI is not available");
|
||||
/* Return ESP_OK to keep underlying socket open */
|
||||
return ESP_OK;
|
||||
} else if (strcmp("/echo", req->uri) == 0) {
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/echo URI is not available");
|
||||
/* Return ESP_FAIL to close underlying socket */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* For any other URI send 404 and close socket */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* An HTTP PUT handler. This demonstrates realtime
|
||||
* registration and deregistration of URI handlers
|
||||
*/
|
||||
@ -168,15 +195,19 @@ esp_err_t ctrl_put_handler(httpd_req_t *req)
|
||||
}
|
||||
|
||||
if (buf == '0') {
|
||||
/* Handler can be unregistered using the uri string */
|
||||
/* URI handlers can be unregistered using the uri string */
|
||||
ESP_LOGI(TAG, "Unregistering /hello and /echo URIs");
|
||||
httpd_unregister_uri(req->handle, "/hello");
|
||||
httpd_unregister_uri(req->handle, "/echo");
|
||||
/* Register the custom error handler */
|
||||
httpd_register_err_handler(req->handle, HTTPD_404_NOT_FOUND, http_404_error_handler);
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(TAG, "Registering /hello and /echo URIs");
|
||||
httpd_register_uri_handler(req->handle, &hello);
|
||||
httpd_register_uri_handler(req->handle, &echo);
|
||||
/* Unregister custom error handler */
|
||||
httpd_register_err_handler(req->handle, HTTPD_404_NOT_FOUND, NULL);
|
||||
}
|
||||
|
||||
/* Respond with empty body */
|
||||
|
@ -19,7 +19,20 @@ from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
import http.client
|
||||
import argparse
|
||||
import Utility
|
||||
|
||||
try:
|
||||
import Utility
|
||||
except ImportError:
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This environment variable is expected on the host machine
|
||||
# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
|
||||
test_fw_path = os.getenv("TEST_FW_PATH")
|
||||
if test_fw_path and test_fw_path not in sys.path:
|
||||
sys.path.insert(0, test_fw_path)
|
||||
|
||||
import Utility
|
||||
|
||||
|
||||
def verbose_print(verbosity, *args):
|
||||
@ -27,6 +40,16 @@ def verbose_print(verbosity, *args):
|
||||
Utility.console_log(''.join(str(elems) for elems in args))
|
||||
|
||||
|
||||
def test_val(text, expected, received):
|
||||
if expected != received:
|
||||
Utility.console_log(" Fail!")
|
||||
Utility.console_log(" [reason] " + text + ":")
|
||||
Utility.console_log(" expected: " + str(expected))
|
||||
Utility.console_log(" received: " + str(received))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def test_get_handler(ip, port, verbosity=False):
|
||||
verbose_print(verbosity, "======== GET HANDLER TEST =============")
|
||||
# Establish HTTP connection
|
||||
@ -44,12 +67,15 @@ def test_get_handler(ip, port, verbosity=False):
|
||||
resp = sess.getresponse()
|
||||
resp_hdrs = resp.getheaders()
|
||||
resp_data = resp.read().decode()
|
||||
try:
|
||||
if resp.getheader("Custom-Header-1") != "Custom-Value-1":
|
||||
return False
|
||||
if resp.getheader("Custom-Header-2") != "Custom-Value-2":
|
||||
return False
|
||||
except Exception:
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
if not (
|
||||
test_val("Status code mismatch", 200, resp.status) and
|
||||
test_val("Response mismatch", "Custom-Value-1", resp.getheader("Custom-Header-1")) and
|
||||
test_val("Response mismatch", "Custom-Value-2", resp.getheader("Custom-Header-2")) and
|
||||
test_val("Response mismatch", "Hello World!", resp_data)
|
||||
):
|
||||
return False
|
||||
|
||||
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
|
||||
@ -59,10 +85,7 @@ def test_get_handler(ip, port, verbosity=False):
|
||||
verbose_print(verbosity, "\t", k, ": ", v)
|
||||
verbose_print(verbosity, "Response Data : " + resp_data)
|
||||
verbose_print(verbosity, "========================================\n")
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return (resp_data == "Hello World!")
|
||||
return True
|
||||
|
||||
|
||||
def test_post_handler(ip, port, msg, verbosity=False):
|
||||
@ -82,7 +105,7 @@ def test_post_handler(ip, port, msg, verbosity=False):
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return (resp_data == msg)
|
||||
return test_val("Response mismatch", msg, resp_data)
|
||||
|
||||
|
||||
def test_put_handler(ip, port, verbosity=False):
|
||||
@ -91,31 +114,125 @@ def test_put_handler(ip, port, verbosity=False):
|
||||
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
|
||||
# PUT message to /ctrl to disable /hello URI handler
|
||||
verbose_print(verbosity, "Disabling /hello handler")
|
||||
# PUT message to /ctrl to disable /hello and /echo URI handlers
|
||||
# and set 404 error handler to custom http_404_error_handler()
|
||||
verbose_print(verbosity, "Disabling /hello and /echo handlers")
|
||||
sess.request("PUT", url="/ctrl", body="0")
|
||||
resp = sess.getresponse()
|
||||
resp.read()
|
||||
|
||||
sess.request("GET", url="/hello")
|
||||
resp = sess.getresponse()
|
||||
resp_data1 = resp.read().decode()
|
||||
verbose_print(verbosity, "Response on GET /hello : " + resp_data1)
|
||||
try:
|
||||
# Send HTTP request to /hello URI
|
||||
sess.request("GET", url="/hello")
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
# PUT message to /ctrl to enable /hello URI handler
|
||||
verbose_print(verbosity, "Enabling /hello handler")
|
||||
sess.request("PUT", url="/ctrl", body="1")
|
||||
resp = sess.getresponse()
|
||||
resp.read()
|
||||
# 404 Error must be returned from server as URI /hello is no longer available.
|
||||
# But the custom error handler http_404_error_handler() will not close the
|
||||
# session if the requested URI is /hello
|
||||
if not test_val("Status code mismatch", 404, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
sess.request("GET", url="/hello")
|
||||
resp = sess.getresponse()
|
||||
resp_data2 = resp.read().decode()
|
||||
verbose_print(verbosity, "Response on GET /hello : " + resp_data2)
|
||||
# Compare error response string with expectation
|
||||
verbose_print(verbosity, "Response on GET /hello : " + resp_data)
|
||||
if not test_val("Response mismatch", "/hello URI is not available", resp_data):
|
||||
raise AssertionError
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return ((resp_data2 == "Hello World!") and (resp_data1 == "This URI doesn't exist"))
|
||||
# Using same session for sending an HTTP request to /echo, as it is expected
|
||||
# that the custom error handler http_404_error_handler() would not have closed
|
||||
# the session
|
||||
sess.request("POST", url="/echo", body="Some content")
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
# 404 Error must be returned from server as URI /hello is no longer available.
|
||||
# The custom error handler http_404_error_handler() will close the session
|
||||
# this time as the requested URI is /echo
|
||||
if not test_val("Status code mismatch", 404, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
# Compare error response string with expectation
|
||||
verbose_print(verbosity, "Response on POST /echo : " + resp_data)
|
||||
if not test_val("Response mismatch", "/echo URI is not available", resp_data):
|
||||
raise AssertionError
|
||||
|
||||
try:
|
||||
# Using same session should fail as by now the session would have closed
|
||||
sess.request("POST", url="/hello", body="Some content")
|
||||
resp = sess.getresponse()
|
||||
resp.read().decode()
|
||||
|
||||
# If control reaches this point then the socket was not closed.
|
||||
# This is not expected
|
||||
verbose_print(verbosity, "Socket not closed by server")
|
||||
raise AssertionError
|
||||
|
||||
except http.client.HTTPException:
|
||||
# Catch socket error as we tried to communicate with an already closed socket
|
||||
pass
|
||||
|
||||
except http.client.HTTPException:
|
||||
verbose_print(verbosity, "Socket closed by server")
|
||||
return False
|
||||
|
||||
except AssertionError:
|
||||
return False
|
||||
|
||||
finally:
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
verbose_print(verbosity, "Enabling /hello handler")
|
||||
# Create new connection
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
# PUT message to /ctrl to enable /hello URI handler
|
||||
# and restore 404 error handler to default
|
||||
sess.request("PUT", url="/ctrl", body="1")
|
||||
resp = sess.getresponse()
|
||||
resp.read()
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
# Create new connection
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
|
||||
try:
|
||||
# Sending HTTP request to /hello should work now
|
||||
sess.request("GET", url="/hello")
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
if not test_val("Status code mismatch", 200, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
verbose_print(verbosity, "Response on GET /hello : " + resp_data)
|
||||
if not test_val("Response mismatch", "Hello World!", resp_data):
|
||||
raise AssertionError
|
||||
|
||||
# 404 Error handler should have been restored to default
|
||||
sess.request("GET", url="/invalid")
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
if not test_val("Status code mismatch", 404, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
verbose_print(verbosity, "Response on GET /invalid : " + resp_data)
|
||||
if not test_val("Response mismatch", "This URI does not exist", resp_data):
|
||||
raise AssertionError
|
||||
|
||||
except http.client.HTTPException:
|
||||
verbose_print(verbosity, "Socket closed by server")
|
||||
return False
|
||||
|
||||
except AssertionError:
|
||||
return False
|
||||
|
||||
finally:
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_custom_uri_query(ip, port, query, verbosity=False):
|
||||
@ -138,7 +255,7 @@ def test_custom_uri_query(ip, port, query, verbosity=False):
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return (resp_data == "Hello World!")
|
||||
return "Hello World!" == resp_data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -154,9 +271,9 @@ if __name__ == '__main__':
|
||||
port = args['port']
|
||||
msg = args['msg']
|
||||
|
||||
if not test_get_handler(ip, port, True):
|
||||
Utility.console_log("Failed!")
|
||||
if not test_post_handler(ip, port, msg, True):
|
||||
Utility.console_log("Failed!")
|
||||
if not test_put_handler(ip, port, True):
|
||||
if not (
|
||||
test_get_handler(ip, port, True) and
|
||||
test_put_handler(ip, port, True) and
|
||||
test_post_handler(ip, port, msg, True)
|
||||
):
|
||||
Utility.console_log("Failed!")
|
||||
|
Loading…
Reference in New Issue
Block a user