/** ****************************************************************************** * @file httpd.c * @author QQ DING * @version V1.0.0 * @date 1-September-2015 * @brief The main HTTPD server thread and its initialization. ****************************************************************************** * * The MIT License * Copyright (c) 2014 MXCHIP Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************** */ /*! \file httpd.h * \brief HTTP Web Server * * HTTP Web Server can serve static pages from a file system, dynamic content * using a server-side include (SSI) capability, and dynamic content using * custom web service gateway interface (WSGI) handlers. This documentation * describes: * -# How to initialize, start, stop, and shutdown the server. * -# How static files are served. * -# How to implement WSGI handlers. * -# How the SSI directives "file" and "virtual" are processed. * Several limitations that may be unexpected by programmers accustomed to using * more sophisticated web servers and frameworks are also discussed. * * \section httpd_usage Usage * * httpd must be initialized by calling \ref httpd_init. After \ref httpd_init * returns WM_SUCESS, \ref httpd_start and \ref httpd_stop can be called to * start and stop the server respectively. Finally, to perform a final cleanup, * \ref httpd_shutdown should be called. After a successful call to * httpd_start, the server is listening for connections on \ref HTTP_PORT or * \ref HTTPS_PORT. * * The WSGI and SSI handlers can be registered by calling the functions * httpd_register_wsgi_handler() and httpd_register_ssi(). * * When a client request is made, httpd first checks if the requested URI * matches any registered WSGI handlers (see below). If a matching handler is * found it gets executed and the processing is terminated. If a file matching * the requested URI is found, it is served. If that file is a .shtml file, it * is processed for directives as described in the file handling section. * * \section wsgi Web Services Gateway Interface (WSGI) * * The Web Services Gateway Interface (WSGI) allows developers to take complete * control over how HTTP requests are processed. When a request arrives, its * request method (or type) and URI are parsed. A table of all registered WSGI * handlers is queried for any matches. If an exact match is obtained, the * control is transferred to the corresponding WSGI handler. If the * WSGI handler returns WM_SUCCESS, no further processing is performed. * In case * A 500 Internal Server Error is returned to the client: * -# When any other value than WM_SUCCESS is returned by the WSGI handler. * * A 404 Not Found Error is returned to the client: * -# If no handler (including the file system) matches the incoming URI * -# If incoming request type is not supported by that WSGI * * A WSGI handler typically is executed in response to a HTTP GET or * HTTP POST operation. In case of HTTP GET, the handler fetches some * data from the device, formats it as desired and sends it to the * requester. In case of HTTP POST, the handler reads the data that is * sent along with the POST request and takes some action on the * device. The WSGI handlers can make use of some of the common httpd * helper functions to perform tasks pertaining to the * sending/receiving of the data. * * A simple WSGI HTTP GET handler is shown below: * Example: JSON Time Get * * \code * int sys_get_time(httpd_request_t *req) * { * char content[30]; * time_t time_val; * * some_api_to_fetch_time(&time_val) * snprintf(content, sizeof(content), "{\"epoch\":%d}", time_val); * * return httpd_send_response(req, HTTP_RES_200 * , content, strlen(content), HTTP_CONTENT_JSON_STR); * } * \endcode * * A simple WSGI HTTP POST handler is shown below: * Example: JSON Time Set * * \code * int sys_set_time(httpd_request_t *req) * { * char sys_time_req[30]; * int ret; * time_t time_val; * * ret = httpd_get_data_json(req, sys_time_req, * sizeof(sys_time_req), &obj); * if (ret < 0) { * wmprintf("Failed to get post request data"); * return ret; * } * json_get_val_int(&obj, "epoch", (int *)&time_val); * * ret = Set the time using the value time_val func() * if (ret == WM_SUCCESS) * return httpd_send_response(req, HTTP_RES_200 * , HTTPD_JSON_SUCCESS, strlen(HTTPD_JSON_SUCCESS), * HTTP_CONTENT_JSON_STR); * else * return httpd_send_response(req, HTTP_RES_200 * , HTTPD_JSON_ERROR, strlen(HTTPD_JSON_ERROR), * HTTP_CONTENT_JSON_STR); * } * \endcode * * The above examples being simplest examples use the most convenient * functions, namely httpd_send_response() and httpd_get_data_json() * for most of their tasks. An advanced user may prefer to have more * flexibility by using functions that provide a greater control. To * enable this, the exact set of steps that are performed by both * these functions is documented below. * * - httpd_send_response() * -# Purge HTTP headers (valid only for GET request) : httpd_purge_headers() * -# Send response status (200 OK/404 Not Found etc.): httpd_send() * -# Send the default headers from \ref httpd_hdr_field_sel_t : * httpd_send_default_headers() * -# Send the Content-Type of the response : httpd_send_header() * -# Send the header terminator : httpd_send_crlf() * -# Send the chunked data : httpd_send_chunk() * -# Send the last chunk : httpd_send_chunk() with length = 0 * . * . * * - httpd_get_data_json() * -# httpd_get_data() * -# Parse the received headers : httpd_parse_hdr_tags() * -# Receive the POST data : httpd_recv() * . * -# Initialize the JSON object : json_object_init() * . * . * * \section files Static File Handling * * Static files can be served from a filesystem. A WSGI handler can be * registered at '/'. This handler can call httpd_handle_file() for dealing with * file serving requests. If the fs contains a file called /404.html, this file * will be returned when httpd receives a request for a URL that does not have a * registered handler or page. The following table summarizes the Content-Types * with which files of various file extensions are served: * | Extension | Content-Type | | ----:---- | --------- | | .html | text/html (with charset=iso8859-1) | | .shtml | same as .html but with caching disabled | | .css | text/css | | .png | image/png | | .gif | image/gif | | .jpg | image/jpeg | | [none] | application/octet-stream | | [other] | text/plain | * * \subsection gzip_handling Compressed Files * * If the request for a file abc.html is received, the web server first * tries to check if a compressed version of this file abc.html.gz * exists. If found, this compressed version is served. The * Content-Encoding field is set to \e gzip for these requests, so that * the HTTP clients can handle it properly. * * \section ssi Server Side Include (SSI) * * Server Side Include (SSI) directives are mark-up snippets in .shtml files * that are detected by HTTPD and replaced with some other content. Two SSI * directives are supported: file and virtual. * * When the file directive is encountered, it is replaced by the contents of a * file. For example, suppose you have the following .shtml files in your * filesystem: * * index.shtml: * \code * Hello World * * \endcode * * footer.html: * \code * Copyright 2008-2013 your name here<\b> * \endcode * * When index.shtml is requested, the SSI file directive will be replaced by * the contents of footer.html, and the following will be served: * index.shtml: * \code * Hello World * Copyright 2008-2013 your name here<\b> * \endcode * * Much like a file SSI directive, a virtual SSI directive can also be used in * .shtml files. They look something like this: * * \code * * \endcode * * When a virtual SSI directive is encountered, control is passed to the SSI * handler that is registered under the specified name ("foo" in this example). * The foo handler would be passed the string "1 2 3" as its argument, along * with the open socket to which the response is being sent, and an \ref * httpd_request_t that can be queried for the request type (POST or GET) and * any url-encoded form data (POST only). The handler is expected to send * complete http chunks to the socket (see \ref httpd_send_chunk). * * When using .shtml files and SSI directives, keep the following tips and * caveats in mind: * * - See \ref HTTPD_SSI_CALL, \ref httpd_register_ssi, struct \ref * httpd_ssifunction for additional documentation and pointers. * * - Placing SSI directives in a file that does not have the .shtml extension * has no effect; the file will be served as-is. * * - Each SSI directive should be on its own line with no other mark up. If * there is any other markup, it will be ignored and will not be served to * the client. * * - The SSI directives must start with either " WSGI_Handler_Function * WSGI_Handler_Function --> httpd_send_response : GET/HEAD/POST Handler * WSGI_Handler_Function --> httpd_get_data_json : POST Handler * httpd_get_data_json --> httpd_get_data : Fetch the POST data * httpd_get_data_json --> json_object_init : JSON object init * httpd_get_data --> httpd_parse_hdr_tags : Parse the HTTP headers * httpd_get_data --> httpd_recv : Receive the data * httpd_parse_hdr_tags --> httpd_recv * httpd_send_response --> httpd_purge_headers : Purge HTTP headers * httpd_purge_headers --> httpd_recv : Receive the headers and purge them * httpd_send_response --> httpd_send : Send response status * httpd_send_response --> httpd_send_default_headers : Send default headers * httpd_send_response --> httpd_send_header : Send the Content-Type header * httpd_send_response --> httpd_send_crlf : Send header end indicator * httpd_send_response --> httpd_send_chunk : Send chunked data * httpd_send_response --> httpd_send_chunk : Send last chunk (len = 0) * httpd_send_default_headers --> httpd_send * httpd_send_header --> httpd_send * httpd_send_crlf --> httpd_send * httpd_send_chunk --> httpd_send_chunk_begin * httpd_send_chunk --> httpd_send * httpd_send_chunk --> httpd_send_crlf * httpd_send_chunk_begin --> httpd_send * * @enduml * @endcond * ******************************************************************************/ /** @defgroup MICO_Middleware_Interface MiCO Middleware APIs * @brief Provide MiCO Middleware Programming APIs. * @{ */ /** @addtogroup MICO_Middleware_Interface * @{ */ /** @defgroup MICO_HTTP_Web_Server MiCO HTTP Web Server * @brief Provide MiCO HTTP Web Server APIs. * @{ */ #ifndef _HTTPD_H_ #define _HTTPD_H_ #include "common.h" //#include /** Port on which the httpd listens for non secure connections. */ #define HTTP_PORT 80 /** Port on which the httpd listens for secure connections. */ #define HTTPS_PORT 443 /** Do not perform an exact match of the URI, but ensure that only the prefix is * correct. This is required when the registered URI is /nodes/ while the * handler wishes to match with /nodes/0/, /nodes/1/ etc. */ #define APP_HTTP_FLAGS_NO_EXACT_MATCH 1 /** This REQ_TYPE should not be used externally. */ #define HTTPD_REQ_TYPE_UNKNOWN 0 /** This request type indicates that incoming http request is of type POST. */ #define HTTPD_REQ_TYPE_POST 1 /** This request type indicates that incoming http request is of type GET. * * \note GET does not support URL-encoded tag/val pairs. */ #define HTTPD_REQ_TYPE_GET 2 /** This request type indicates that incoming http request is of type PUT. */ #define HTTPD_REQ_TYPE_PUT 4 /** This request type indicates that incoming http request is of type DELETE. */ #define HTTPD_REQ_TYPE_DELETE 8 /** This request type indicates that incoming http request is of type HEAD. */ #define HTTPD_REQ_TYPE_HEAD 16 /** HTTPD Error Codes */ enum wm_httpd_errno { WM_E_HTTPD_ERRNO_BASE = 0, /** Error parsing filename in HTTP Header */ WM_E_HTTPD_HDR_FNAME, /** HTTPD doesn't support the http version */ WM_E_HTTPD_NOTSUPP, /** Post data too long */ WM_E_HTTPD_TOOLONG, /** Data read failed */ WM_E_HTTPD_DATA_RD, /** SSI Table full */ WM_E_HTTPD_SSI_MAX, /** Handler generated a HTTP 404 File Not Found error */ WM_E_HTTPD_HANDLER_404, /** Handler generated a HTTP 400 Bad Request error */ WM_E_HTTPD_HANDLER_400, /** Handler generated a HTTP 500 Internal Server error */ WM_E_HTTPD_HANDLER_500, /** No handler was found */ WM_E_HTTPD_NO_HANDLER, }; /** This value is returned when a WSGI handler is successful to indicate that * all processing has been completed. */ #define HTTPD_DONE 1 /** Maximum received message length * * This value is essentially the longest single line that the HTTPD can * handle. This limits programmers and users in some interesting ways. For * example: * -# This is the maximum length of any url-encoded data sent as the body of a * POST request. * If this limit is exceeded, the server will generate a 500 Internal * Server Error response. * Applications that require a higher capacity for form data must implement * their own custom handler to process form data or tune this value. * -# This is the maximum length of a header line that can be processed by * \ref httpd_parse_hdr_tags. Header lines that exceed this * length will be truncated before being analyzed. Applications that * require more capacity must implement their own custom handler to process * HTTP header data. * -# This is the maximum length of a single line in a .shtml script. * * Developers must ensure that these limits are addressed to avoid unexpected * errors in the field. * * \note This value does not include any NULL termination required internally * by the httpd. */ #define HTTPD_MAX_MESSAGE 512 /** Maximum URI length * * This is the maximum supported URI length. For example, if a client sends a * GET /foobar.html, the length of "/foobar.html" must be less than this value. * Programmers are obliged to ensure that none of the URIs or HTTPD file names * exceed this limit. If URIs do end up exceeding this limit, a 500 Internal * Server Error response will be generated. * * \note This value does not include any NULL termination required internally * by the HTTPD. However it does include the leading "/". */ #define HTTPD_MAX_URI_LENGTH 64 /** Maximum length of the value portion of a tag/value pair * * If this length is exceeded, the value will be truncated to this length and * no error will be generated. If debugging is enabled, however, a warning * will be written to the console. * * \note Truncating the user input passed by client may result in unexpected * behavior depending on how a custom handler uses that data. Programmers * should take steps to validate and/or limit user input using javascript, * input tag properties, or other client-side means. * * \note This value does not include the NULL termination of the value. * */ #define HTTPD_MAX_VAL_LENGTH 64 /** Maximum length of Content-Type * * httpd_parse_hdr_tags() will parse the headers and store the value of * the Content-Type. The maximum value of this field has been arrived * keeping in mind the need to support the following type of Content-Type * header for working with multipart/form-data for file uploads: * Content-Type: multipart/form-data; boundary=\ * The length of this line is 104 bytes */ #define HTTPD_MAX_CONTENT_TYPE_LENGTH 128 #define ISO_nl 0x0a #define ISO_cr 0x0d #define ISO_tab 0x09 #define ISO_space 0x20 #define ISO_bang 0x21 #define ISO_quot 0x22 #define ISO_percent 0x25 #define ISO_period 0x2e #define ISO_slash 0x2f #define ISO_colon 0x3a #define HTTPD_JSON_ERR_MSG "{\"error_msg\":\"%s\"}" #define HTTPD_JSON_RETURN_MSG "{\"return_msg\":\"%s\"}" #define HTTPD_JSON_ERROR "{\"error\": -1}" #define HTTPD_JSON_SUCCESS "{\"success\": 0}" #define HTTPD_TEXT_ERROR "error -1\r\n" #define HTTPD_TEXT_SUCCESS "success\r\n" /** Content-Type: application/json */ #define HTTP_CONTENT_JSON_STR "application/json" /** Content-Type: text/xml */ #define HTTP_CONTENT_XML_STR "text/xml" /** Content-Type: text/hxml */ #define HTTP_CONTENT_HTML_STR "text/html" /** Content-Type: text/css */ #define HTTP_CONTENT_CSS_STR "text/css" /** Content-Type: application/x-javascript */ #define HTTP_CONTENT_JS_STR "application/javascript" /** Content-Type: image/png */ #define HTTP_CONTENT_PNG_STR "image/png" /** Content-Type: text/plain */ #define HTTP_CONTENT_PLAIN_TEXT_STR "text/plain" /** HTTP Response: 200 OK */ #define HTTP_RES_200 "HTTP/1.1 200 OK\r\n" /** HTTP Response: 301 Moved Permanently */ #define HTTP_RES_301 "HTTP/1.1 301 Moved Permanently\r\n" /** HTTP Response: 304 Not Modified */ #define HTTP_RES_304 "HTTP/1.1 304 Not Modified\r\n" /** HTTP Response: 400 Bad Request */ #define HTTP_RES_400 "HTTP/1.1 400 Bad Request\r\n" /** HTTP Response: 404 Not Found */ #define HTTP_RES_404 "HTTP/1.1 404 Not Found\r\n" /** HTTP Response: 505 HTTP Version Not Supported */ #define HTTP_RES_505 "HTTP/1.1 505 HTTP Version Not Supported\r\n" /* Are these \r\n required in here? The send chunk will append its own set of * \r\n, thus making it two \r\n. */ #define HTTPD_XML_ERROR \ "%d\r\n" #define HTTPD_XML_SUCCESS \ "0\r\n" /** Send carriage return and new line characters on the socket \_sock\_ */ #define httpd_send_crlf(_sock_) httpd_send(_sock_, "\r\n", 2); /** httpd_useragent_t reports the product and version of the client accessing * the httpd. */ typedef struct { /** Product of the client */ char product[HTTPD_MAX_VAL_LENGTH + 1]; /* +1 for null termination */ /** Version of the client */ char version[HTTPD_MAX_VAL_LENGTH + 1]; /* +1 for null termination */ } httpd_useragent_t; struct httpd_wsgi_call; /** Request structure representing various properties of an HTTP request */ typedef struct { /** HTTP Request type: GET, HEAD, POST, PUT, DELETE */ int type; /** The incoming URI */ /* +1 is for null termination */ char filename[HTTPD_MAX_URI_LENGTH + 1]; /** The socket of the incoming HTTP Request */ int sock; /** Indicator notifying whether the HTTP headers are parsed or not */ unsigned char hdr_parsed; /** The number of data bytes not yet parsed */ int remaining_bytes; /** The size of data in the incoming HTTP Request */ int body_nbytes; /** Pointer to the corresponding wsgi structure */ const struct httpd_wsgi_call *wsgi; /** Set to 1 if chunked data is used */ unsigned char chunked; /** User-agent: Product and Version */ httpd_useragent_t agent; /** The content type of the incoming HTTP Request */ char content_type[HTTPD_MAX_CONTENT_TYPE_LENGTH]; /** True if "If-None-Match" header is present in the incoming * HTTP Request */ bool if_none_match; /** Used for storing the etag of an URI */ unsigned etag_val; } httpd_request_t; /** @brief Initialize the httpd * * @note This function must be called before any of the other API functions. If any * of the initialization steps fail, the function will fail. * * @return WM_SUCCESS if successful * @return -WM_FAIL otherwise */ int httpd_init(void); /** @brief Shutdown the httpd * * @note This function is the opposite of httpd_init(). It cleans up any * resources used by the httpd. If the httpd thread is running (i.e., * httpd_stop has not been called), it will be halted. * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise * */ int httpd_shutdown(void); /** @brief Start the httpd * * @note Upon WM_SUCCESS, the user can expect that the server is running and * ready to receive connections. httpd_stop() should be called to stop the * server. A return value of -WM_FAIL generally indicates that the thread * has already been started. * * @return WM_SUCCESS :if successful * @return WM_FAIL :otherwise */ int httpd_start(void); /** @brief Stop the httpd * * @note This function is the opposite of httpd_start(). It stops the httpd. If * WM_SUCCESS is returned, the server has been stopped and will no longer * accept connections. -WM_FAIL is returned if the httpd is uninitialized, or * if an internal error occurs (which is not expected). Note that this * function forces the httpd thread to stop and closes any open sockets. If * a handler is being executed during this time, it will be terminated. * * @return WM_SUCCESS : if successful * @return WM_FAIL : otherwise */ int httpd_stop(void); /** @brief Check if httpd is running * * @return 0 : if httpd is not running * @return non-zero : if it is running */ int httpd_is_running(void); /** @brief Helper function to send a buffer over a connection. * * @note WSGI handlers will often want to send data over a connection. This can be * achieved with the regular send() system call, however that call may not send * all of the data. This helper function calls send in a loop repeatedly until * all data has been sent or until an error occurs. This function internally * calls tls_send() if TLS is enabled, else it calls send() * * @param[in] sock The socket on which the data is to be sent * @param[in] buf Pointer to the buffer * @param[in] len Length of the buffer * * @return WM_SUCCESS : if successful * @return -WM_FAIL : otherwise * * \see httpd_send_chunk */ int httpd_send(int sock, const char *buf, int len); /** @brief Helper function to send an HTTP chunk of data over a connection * * @note This function sends HTTP chunked data. It includes sending the chunked * header (which consists of the chunk size in ascii followed by \\r\\n * characters), then the actual http data and at the end chunk end indicator * (\\r\\n characters) * If the len is passed as 0 to this function, it sends the last chunk i.e. * 0\\r\\n * * Note that while using chunked data, HTTP Header "Transfer Encoding: Chunked" * should be sent in the response * * Send either an HTTP chunk of data or the terminating HTTP chunk. * * @param[in] sock The socket on which the data is to be sent * @param[in] buf Pointer to the buffer or NULL for last chunk * @param[in] len Length of the buffer or 0 for last chunk * * @return WM_SUCCESS : if successful * @return -WM_FAIL : otherwise * */ int httpd_send_chunk(int sock, const char *buf, int len); /** @brief Helper function to send a HTTP chunk header * * @note This function is not required when \ref httpd_send_chunk is used. * \ref httpd_send_chunk internally calls this function * * This function can be followed by a call to \ref httpd_send to send the * actual content followed by sending the chunk end indicator (\\r\\n) * * @param[in] sock The socket over which chunk header is to be sent * @param[in] size Length of the chunk to be sent * * @return WM_SUCCESS :if successful * @return - WM_FAIL :otherwise * */ int httpd_send_chunk_begin(int sock, int size); /** Structure representing a WSGI handler * */ struct httpd_wsgi_call { /** URI of the WSGI */ const char *uri; /** Indicator for HTTP headers to be sent in the response*/ int hdr_fields; /** Flag indicating if exact match of the URI is required or not */ int http_flags; /** HTTP GET or HEAD Handler */ int (*get_handler) (httpd_request_t *req); /** HTTP POST Handler */ int (*set_handler) (httpd_request_t *req); /** HTTP PUT Handler */ int (*put_handler) (httpd_request_t *req); /** HTTP DELETE Handler */ int (*delete_handler) (httpd_request_t *req); }; /** @brief Register a WSGI handler * * @note WSGI handlers declared must be registered with the * httpd by calling this function. * * @param[in] wsgi_call pointer to a struct httpd_wsgi_call. The memory * location pointed to by this pointer should be available until * the httpd handler is unregistered. * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_register_wsgi_handler(struct httpd_wsgi_call *wsgi_call); /** @brief Register a list of WSGI handlers * * @note WSGI handler list declared must be registered with the * httpd by calling this function. * * @param[in] wsgi_call_list pointer to a list of struct httpd_wsgi_call. The * memory location pointed to by this pointer should be available until * the httpd handler list is unregistered. * @param[in] handler_cnt number of WSGI handlers in wsgi_call_list * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_register_wsgi_handlers(struct httpd_wsgi_call *wsgi_call_list, int handler_cnt); /** @brief Unregister a WSGI handler * * @note This call unregisters a WSGI handler. * * @param[in] wsgi_call pointer to a struct httpd_wsgi_call * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_unregister_wsgi_handler(struct httpd_wsgi_call *wsgi_call); /** @brief Unregister a list of WSGI handlers * * @note WSGI handler list declared can be unregistered with the * httpd by calling this function. * * @param[in] wsgi_call_list list of struct httpd_wsgi_call * @param[in] handler_cnt number of WSGI handlers in wsgi_call_list * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_unregister_wsgi_handlers(struct httpd_wsgi_call *wsgi_call_list, int handler_cnt); /** @brief Maximum length of virtual SSI arguments * * @note The supported format for virtual SSI directives is: * * @code * * @endcode * * or * * @code * * @endcode * * HTTPD_MAX_SSI_ARGS is the maximum length for "arg1 arg2 arg3" (not * including the quotes). If this length is exceeded, the arguments will be * truncated. * * @note This does not include any null-termination required internally by the * httpd or the virtual SSI handlers. * * @note whitespace between the function name (e.g., foo) and the first * argument (e.g. arg1) is ignored and not passed to handlers. * * @note Double quotes are reserved and should only be used as described in * the above examples. */ #define HTTPD_MAX_SSI_ARGS 32 /** @brief SSI virtual include handler function prototype * * @param[in] req The incoming HTTP request \ref httpd_request_t. Minimally, * the type and filename members will be initialized. Handlers are not * expected to write to this struct. * * @param[in] args Null-terminated string that is the arguments passed to the * handler in the include directive. * * @param[in] sock Socket to which the handler output should be sent. * * @param[in] scratch A scratch buffer that can be used by the handler. It is * @ref HTTPD_MAX_MESSAGE bytes long. * * @note SSI virtual include handlers should only send complete chunks i.e. * they should send any data using \ref httpd_send_chunk, not \ref httpd_send * or any lower-level system calls. These handlers should also not send the * final chunk i.e. they should not invoke httpd_send_chunk with a length of 0. * */ typedef int (*httpd_ssifunction) (const httpd_request_t *req, const char *args, int sock, char *scratch); /** @brief Opaque structure representing a virtual SSI handler * * @note Users should use the macro \ref HTTPD_SSI_CALL to initialize this struct * statically. */ struct httpd_ssi_call { /** The name of the function, used in the script file */ const char *name; /** The pointer to the function that implements the handler */ const httpd_ssifunction function; }; /** @brief httpd virtual SSI handler declaration * * @note This macro is used for declaring a virtual SSI handler struct \ref * httpd_ssi_call. The function is then added to the list of handlers with \ref * httpd_register_ssi, and removed with \ref httpd_unregister_ssi function. * * @param name Name of the \ref httpd_ssi_call structure object * * @param str The string name of the function, used in the script file * * @param function A pointer to the function that implements the handler * * @hideinitializer */ #define HTTPD_SSI_CALL(name, str, function) \ struct httpd_ssi_call name = {str, function} /** Maximum number of virtual SSI handlers that are supported. */ #define MAX_REGISTERED_SSIS 32 /** @brief Register a virtual SSI handler * * @note virtual SSI handlers declared with \ref HTTPD_SSI_CALL must be * registered with the httpd by calling this function. * * @param[in] ssi_call pointer to the httpd_ssi_call to register * * @return WM_SUCCESS :if successful * @return -WM_E_HTTPD_SSI_MAX :if the maximum number of supported handlers has * been reache * @return -WM_FAIL :if the handler is already registered * * @note All SSI handlers are cleared when httpd_init is called. */ int httpd_register_ssi(struct httpd_ssi_call *ssi_call); /** @brief Unregister a virtual SSI handler * * @param[in] ssi_call pointer to the httpd_ssi_call to unregister * * @note All SSI handlers are cleared when httpd_init is called. */ void httpd_unregister_ssi(struct httpd_ssi_call *ssi_call); /** @brief Register and Enable httpd cli-commands * * @return WM_SUCCESS if successful * @return -WM_FAIL otherwise * * @note This function can be called by the application * after the httpd is initialized. */ /* no usage */ int httpd_cli_init(void); /** HTTP Content types */ enum http_content_type { /** Content-Type: text/plain */ HTTP_CONTENT_PLAIN_TEXT, /** Content-Type: application/json */ HTTP_CONTENT_JSON, /** Content-Type: text/xml */ HTTP_CONTENT_XML, /** Content-Type: text/html */ HTTP_CONTENT_HTML, /** Content-Type: image/jpeg */ HTTP_CONTENT_JPEG, }; /** HTTP Headers to be sent in the HTTP response */ typedef enum { /** "Server: Marvell-WM\r\n" */ HTTPD_HDR_ADD_SERVER = 0x0001, /** "Connection: keep-alive\r\n" */ HTTPD_HDR_ADD_CONN_KEEP_ALIVE = 0x0002, /** "Connection: close\r\n" */ HTTPD_HDR_ADD_CONN_CLOSE = 0x0004, /** "Transfer-Encoding: chunked\r\n" */ HTTPD_HDR_ADD_TYPE_CHUNKED = 0x0008, /** "Cache-Control: no-store, no-cache, must-revalidate\r\n" */ HTTPD_HDR_ADD_CACHE_CTRL = 0x0010, /** "Cache-Control: post-check=0, pre-check=0\r\n" */ HTTPD_HDR_ADD_CACHE_CTRL_NO_CHK = 0x0020, /** "Pragma: no-cache\r\n" */ HTTPD_HDR_ADD_PRAGMA_NO_CACHE = 0x0040, } httpd_hdr_field_sel_t; /** Default HTTP headers from \ref httpd_hdr_field_sel_t */ #define HTTPD_DEFAULT_HDR_FLAGS (HTTPD_HDR_ADD_SERVER | \ HTTPD_HDR_ADD_CONN_CLOSE | HTTPD_HDR_ADD_TYPE_CHUNKED) /** @brief Send HTTP Status Headers * * @note This function sends the corresponding HTTP headers on the socket. * * @param[in] sock the socket to send headers on * @param[in] stat_code the status code WM_SUCCESS, -WM_E_HTTPD_HANDLER_404 etc. * @param[in] content_type the content type \ref http_content_type * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_hdr_from_code(int sock, int stat_code, enum http_content_type content_type); /** @brief Validate an incoming request with a registered handler * * @note This function is used to match the incoming request URI against the one that * is registered with HTTPD * * @param[in] req_uri URI of the incoming HTTP request * @param[in] uri URI of an handler registered with HTTPD * @param[in] flags Flag indicating if exact match is required or not \ref * APP_HTTP_FLAGS_NO_EXACT_MATCH * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_validate_uri(char *req_uri, const char *uri, int flags); /** @brief Helper function to receive data over a socket * * @note This function abstracts the TCP/IP recv call. We need the abstraction * because recv could be through TLS connection in which case we do not * call the TCP/IP stack 'recv' function directly. * * This API is only for the httpd handlers who have been passed the socket * descriptors by httpd. * * @param[in] sock The socket on which the data is to be received * @param[out] buf The buffer in which data will be received * @param[in] n Number of bytes to be received * @param[in] flags Same values as that of flag parameter in man page of recv * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_recv(int sock, void *buf, size_t n, int flags); /** @brief Send the entire HTTP response * * @note This is a helper function which can be used by the WSGI handlers * to send the entire HTTP response. * This function first purges the incoming headers (this step is valid only for * HTTP GET or HEAD request.) Then it sends the first line of the response i.e. * 200 OK, 404 Not Found etc. as specified by the user. Further, it sends the * default HTTP headers as per the value of hdr_fields parameter of that WSGI. * After that, it sends the Content-Type HTTP header using the content_type * sent by the user. If the request type is HTTP HEAD, only the headers are * sent out and this function terminates, else data provided by the user * (chunked/non chunked data) follows. * * Note that this API is optional and WSGI handlers may prefer to * send the response as per their wish using other HTTPD APIs. * * @param[in] req The incoming HTTP request \ref httpd_request_t * @param[in] first_line First line of the response. for e.g.: To send 200 OK * it will be: "HTTP/1.1 200 OK\r\n" * @param[in] content The data to be sent * @param[in] length The length of data to be sent * @param[in] content_type The content type of the response. for e.g.: * To send JSON response it will be: "application/json" * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_response(httpd_request_t *req, const char *first_line, char *content, int length, const char *content_type); /** @brief Send HTTP response 301: Moved Permanently * * @note This is a helper function which can be used by the WSGI handlers to send * HTTP response HTTP 301 Moved Permanently. This function first purges the * incoming headers (this step is valid only for HTTP GET or HEAD request.) * Then it sends the first line of the response i.e. 301 Moved Permanently. * Further, it sends the default HTTP headers as per the value of hdr_fields * parameter of that WSGI. After that, it sends the HTTP header Location which * is the new link for redirection. Then, it sends the Content-Type HTTP header * using the content_type sent by the user. If the request type is HTTP HEAD, * only the headers are sent out and this function terminates, else data * provided by the user (chunked/non chunked data) follows. * * Note that this API is optional and WSGI handlers may prefer to * send the response as per their wish using other HTTPD APIs. * * @param[in] req The incoming HTTP request \ref httpd_request_t * @param[in] location The new link for redirection * @param[in] content_type The content type of the response. for e.g.: * To send JSON response it will be: "application/json" * @param[in] content The data to be sent (optional) * @param[in] content_len The length of data to be sent * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_response_301(httpd_request_t *req, char *location, const char *content_type, char *content, int content_len); /** @brief Send an HTTP header * * @note This function can be to send out an HTTP header. * * It accepts a name value pair as input and sends it out as: "name: value\r\n" * for e.g.: * To send header "Content-Type: application/json\r\n" call * httpd_send_header(sock, "Content-Type", "application/json"); * * @param[in] sock the socket to send header on * @param[in] name name of the HTTP header * @param[in] value value of the HTTP header 'name' * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_header(int sock, const char *name, const char *value); /** @brief Send all http header * * @note This function can be to send out all http header. * Only for array types * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_all_header(httpd_request_t *req, const char *first_line, int body_lenth, const char *content_type); #define HTTPD_SEND_BODY_DATA_MAX_LEN 1024 /** Send an HTTP body * * This function can be to send out an HTTP body. * Only for array types * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_body(int sock, const unsigned char *body_image, uint32_t body_size); /** @brief Send the default HTTP Headers * * @note This function can be used by the WSGI handlers to send out some or all * headers present in \ref httpd_hdr_field_sel_t. The WSGI handlers when * declared can decide which headers they want to send out in the response and * accordingly populate hdr_fields parameter in the httpd_wsgi_call structure * * @param[in] sock The socket to send headers on * @param[in] hdr_fields The hdr_fields parameter of the matching WSGI for the * HTTP request, selected from \ref httpd_hdr_field_sel_t * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_send_default_headers(int sock, int hdr_fields); /** @brief Purge the headers of the incoming HTTP request * * @note This function is used to purge the headers of the incoming HTTPD request * * Note that it is mandatory to consume all the HTTP headers for the proper * functioning of web server * * @param[in] sock the socket of the incoming HTTP request * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_purge_headers(int sock); /** @brief Parse the http headers into the req httpd_request_t structure. * * @note This function will one by one read lines terminated by CR-LF (\\r\\n) from * the socket and parse the headers contained in them. Some of the headers (at * this time only the Content-Length) will be then copied to the req * httpd_request_t structure. * * Note that it is mandatory to consume all the HTTP headers * * After executing this function, the socket will point to the request entity * body, if any. * * @param[in] req The incoming HTTP request \ref httpd_request_t * @param[in] sock The socket of the incoming HTTP request * @param[in] scratch Buffer used to read the HTTP headers * @param[in] len Length of the scratch buffer * * @return WM_SUCCESS :if successful * @return -WM_FAIL :otherwise */ int httpd_parse_hdr_tags(httpd_request_t *req, int sock, char *scratch, int len); /** @brief Get the incoming data in case of HTTP POST request * * @note This function first parses the HTTP header tags and then receives the data * in the buffer provided by the user. * * It can be called multiple times if the size of user buffer is less than the * actual data that is received. Care has been taken that, in such situations, * the HTTP headers are parsed only once. When it returns zero it indicates * that all the data has been received. * * Note that this API is optional and WSGI handlers may prefer to parse the * headers and get the data as per their wish using other HTTPD APIs. * * @param[in] req The incoming HTTP request \ref httpd_request_t * @param[out] content The buffer in which the data is to be received * @param[in] length The length of the content buffer * @return The number of bytes still remaining to be read if successful * @return -WM_FAIL :otherwise */ int httpd_get_data(httpd_request_t *req, char *content, int length); /** @brief Get the incoming JSON data in case of HTTP POST request * * @note This function is an extension to \ref httpd_get_data. Additionally this * function takes in a pointer to the JSON object and initializes the same. In * this case, content is actually used as a scratch buffer. JSON object is what * you actually need to use after it returns. * * Note that this function can be called only once i.e. the size of content * buffer passed to this function should be large enough to store all the * incoming data. * * @param[in] req The incoming HTTP request \ref httpd_request_t * @param[out] content The buffer in which the data is to be received * @param[in] length The length of the content buffer * @param[out] obj A pointer to JSON object structure * @return The number of bytes still remaining to be read (should be 0 if used * appropriately) if successful * @return -WM_FAIL : otherwise */ typedef struct { /* Mandatory. Will be sent to the client */ const unsigned char *server_cert; /* Size of server_cert */ int server_cert_size; /* * Server private key. Mandatory. * For the perusal of the server */ const unsigned char *server_key; /* Size of server_key */ int server_key_size; /* * Needed if the server wants to verify client * certificate. Otherwise set to NULL. */ const unsigned char *client_cert; /* Size of client_cert */ int client_cert_size; } httpd_tls_certs_t; int httpd_auth_init(char *name, char *passwd); char *get_httpd_auth( void ); /** * @} */ /** * @} */ /* * Call this function to set httpd tls certificates. */ //int httpd_use_tls_certificates(const httpd_tls_certs_t *httpd_tls_certs); #endif