/* Simple HTTP server for ESP32.
* Copyright Ivan Grokhotkov, 2017.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file http_server.h
* @brief Simple HTTP server
*/
#include "sdkconfig.h"
/* Pull in the definitions of HTTP methods */
#include "http_parser.h"
/**
* Bit masks for events to be passed to a handler
*/
#define HTTP_HANDLE_URI BIT(0) /*!< Called when URI is received */
#define HTTP_HANDLE_HEADERS BIT(1) /*!< Called when all headers are received */
#define HTTP_HANDLE_DATA BIT(2) /*!< Called each time a fragment of request body is received */
#define HTTP_HANDLE_RESPONSE BIT(3) /*!< Called at the end of the request to produce the response */
/** Error buffer length */
#define ERROR_BUF_LENGTH 100
#define HTTPS_SERVER CONFIG_HTTPS_SERVER
#define HTTPD_EXAMPLE CONFIG_HTTPD_EXAMPLE
/** Opaque type representing single HTTP connection */
typedef struct http_context_* http_context_t;
/** Opaque type representing HTTP server */
typedef struct http_server_context_* http_server_t;
/** Callback type of HTTP request handler */
typedef void (* http_handler_fn_t)(http_context_t http_ctx, void* ctx);
/**
* @brief Configuration of the HTTP server
*/
typedef struct {
int port; /*!< TCP Port to listen on */
int task_affinity; /*!< Server task affinity (CPU number of tskNO_AFFINITY */
int task_stack_size; /*!< Server task stack size, in bytes */
int task_priority; /*!< Server task priority */
} http_server_options_t;
/** Default initializer for http_server_options_t */
#define HTTP_SERVER_OPTIONS_DEFAULT() {\
.port = 80, \
.task_affinity = tskNO_AFFINITY, \
.task_stack_size = 4096, \
.task_priority = 1, \
}
/** Default initializer for http_server_options_t */
#define HTTPS_SERVER_OPTIONS_DEFAULT() {\
.port = 443, \
.task_affinity = tskNO_AFFINITY, \
.task_stack_size = 10240, \
.task_priority = 8, \
}
const static char index_html[] = ""
"\n"
"
\n"
" \n"
" \n"
"HELLO ESP32\n"
"\n"
"\n"
"Hello World, from ESP32!
\n"
"\n"
"\n";
const static char response_OK[] =
"OK!\n";
/**
* @brief initialize HTTP server, start listening
* @param options pointer to http server options, can point to a temporary
* @param[out] output, handle of the server; pass it to http_server_stop do
* delete the server.
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if out of RAM
* - ESP_FAIL if some other error
*/
esp_err_t http_server_start(const http_server_options_t* options, http_server_t* out_server);
/**
* @brief Stop the previously started server
* @param server handle obtained from http_server_start
* @return
* - ESP_OK on success
*/
esp_err_t http_server_stop(http_server_t server);
/**
* @brief Register a handler for certain URI
*
* The handler will be called when a client makes a request with matching URI
* and HTTP method.
*
* @note Currently only matches full URIs, doesn't support regex
*
* @param server Server handle to register the handler for
* @param uri_pattern URI pattern to match
* @param method one of HTTP_GET, HTTP_POST, HTTP_PUT, etc
* @param events a bit mask of HTTP_HANDLE_X events for which the handler
* should be called
* @param callback function to call
* @param callback_arg application context to pass to the callback
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if out of memory
*/
esp_err_t http_register_handler(http_server_t server, const char* uri_pattern, int method,
int events, http_handler_fn_t callback, void* callback_arg);
/**
* @brief Register a handler for application/x-www-form-urlencoded requests
*
* Unlike http_register_handler, handlers registered using this function will
* not receive HTTP_HANDLE_DATA events. Instead, request body will be parsed
* into key-value pairs, which can be retrieved while handling
* HTTP_HANDLE_RESPONSE event using http_request_get_form_value.
*
* @param server Server handle to register the handler for
* @param uri_pattern URI pattern to match
* @param method one of HTTP_GET, HTTP_POST, HTTP_PUT, etc
* @param events a bit mask of HTTP_HANDLE_X events for which the handler
* should be called. HTTP_HANDLE_DATA will not be passed to
* the callback.
* @param callback function to call
* @param callback_arg application context to pass to the callback
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if out of memory
*/
esp_err_t http_register_form_handler(http_server_t server, const char* uri_pattern, int method,
int events, http_handler_fn_t callback, void* callback_arg);
/**
* @brief Get value for given URL argument of form argument
* @param http_ctx context passed to the handler
* @param name name of URL or form argument
* @return pointer to the value, valid until the end of request
*/
const char* http_request_get_arg_value(http_context_t http_ctx, const char* name);
/**
* @brief Get request method
* @param http_ctx context passed to the handler
* @return one of HTTP_GET, HTTP_POST, etc
*/
int http_request_get_method(http_context_t http_ctx);
/**
* @brief Get URI present in the request
* @param http_ctx context passed to the handler
* @return pointer to the URI, valid until the end of request
*/
const char* http_request_get_uri(http_context_t http_ctx);
/**
* @brief Get request header
* @param http_ctx context passed to the handler
* @param name header name
* @return
* - If the header with given name is present in the request, returns
* pointer to the value; valid until request callback returns.
* - Otherwise, returns NULL
*/
const char* http_request_get_header(http_context_t ctx, const char* name);
/**
* @brief Get the event which caused a call to the handler
* @param ctx context passed to the handler
* @return one of HTTP_HANDLE_X values
*/
int http_request_get_event(http_context_t ctx);
/**
* @brief Get the request body fragment
* To be used when handling HTTP_HANDLE_DATA event.
*
* @param ctx context passed to the handler
* @param[out] out_data_ptr output, receives pointer to the body fragment
* @param[out] out_size output, receives the size of the body fragment
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if called for events other than HTTP_HANDLE_DATA
*/
esp_err_t http_request_get_data(http_context_t ctx, const char** out_data_ptr, size_t* out_size);
/**
* @brief structure describing a part of the response to be sent
*/
typedef struct {
const void* data; /*!< pointer to data to be sent */
size_t size; /*!< size of data to send; if data is a 0-terminated string, size can be left 0 */
bool data_is_persistent; /*!< set to true if data is in constant RAM */
} http_buffer_t;
#define HTTP_RESPONSE_SIZE_UNKNOWN SIZE_MAX
/**
* @brief Begin writing HTTP response
* @param http_ctx context passed to the handler
* @param code HTTP response code
* @param content_type string to send as a value in content-type header
* @param response_size either the size of the response body, or HTTP_RESPONSE_SIZE_UNKNOWN
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if can't allocate temporary buffer
* - other errors from LwIP
*/
esp_err_t http_response_begin(http_context_t http_ctx, int code,
const char* content_type, size_t response_size);
/**
* @brief Add HTTP header to the response
*
* This function may be called between http_response_begin and http_response_write.
*
* For multipart responses, calling it between http_response_begin and
* http_response_begin_multipart adds header to the list of headers in the original
* response (which is sent first). Calling it after http_response_begin_multipart
* adds the header to the list of headers sent for the current response part.
*
* 'name' and 'val' can point to temporary values. This function copies the
* both strings into internal storage.
*
* @param http_ctx context passed to the handler
* @param name Header name
* @param val Header value
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if can't allocate memory for the header
*/
esp_err_t http_response_set_header(http_context_t http_ctx,
const char* name, const char* val);
/**
* @brief Start one part of a multipart response
*
* For multipart responses, the sequence of calls is as follows:
*
* 1. http_response_begin (with size == HTTP_RESPONSE_SIZE_UNKNOWN)
* 2. (optional) http_response_set_header — for the overall response
* 3. http_response_begin_multipart (if part size is known, pass it in response_size argument)
* 4. (optional) http_repponse_set_header — for the current part
* 5. (optional) http_response_write — write response data
* 6. http_response_end_multipart — when done with the part
* 7. repeat from 3 as needed
*
* @param http_ctx context passed to the handler
* @param content_type value to be set in part's content-type header
* @param response_size either the size of part body, or HTTP_RESPONSE_SIZE_UNKNOWN
* @return
* - ESP_OK
* - ESP_ERR_NO_MEM if can't allocate memory
* - other errors from LwIP
*/
esp_err_t http_response_begin_multipart(http_context_t http_ctx,
const char* content_type, size_t response_size);
/**
* @brief Indicate that one part of the multipart response is finished
* @param http_ctx context passed to the handler
* @param boundary part boundary. Has to be the same as given in the content-type of the first response.
* @return
* - ESP_OK
* - other errors from LwIP
*/
esp_err_t http_response_end_multipart(http_context_t http_ctx, const char* boundary);
/**
* @brief Send a piece of HTTP response to the client
* @param http_ctx context passed to the handler
* @param buffer data to send, see \ref http_buffer_t
* @return
* - ESP_OK on success
* - other errors from LwIP
*/
esp_err_t http_response_write(http_context_t http_ctx, const http_buffer_t* buffer);
/**
* @brief Indicate that response is complete
* @param http_ctx context passed to the handler
* @return
* - ESP_OK on success
* - other errors in the future?
*/
esp_err_t http_response_end(http_context_t http_ctx);
/**
* @brief Example of GET method. Responding a simple "Hello World" html. All initializations included.
* @param none
* @return
* - ESP_OK on success
* - other errors in the future?
*/
esp_err_t simple_GET_method_example(void);
/**
* @brief Example of POST method. Send a application/x-www-form-urlencoded pair key-value where the key is 'key' and some value for it. The value is printed and the server responds a 201 code and a OK message.
* @param none
* @return
* - ESP_OK on success
* - other errors in the future?
*/
esp_err_t simple_POST_method_example(void);
#ifdef __cplusplus
}
#endif