/** @file * @brief HTTP client API * * An API for applications do HTTP requests */ /* * Copyright (c) 2019 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_INCLUDE_NET_HTTP_CLIENT_H_ #define ZEPHYR_INCLUDE_NET_HTTP_CLIENT_H_ /** * @brief HTTP client API * @defgroup http_client HTTP client API * @ingroup networking * @{ */ #include #include #include #ifdef __cplusplus extern "C" { #endif #if !defined(HTTP_CRLF) #define HTTP_CRLF "\r\n" #endif #if !defined(HTTP_STATUS_STR_SIZE) #define HTTP_STATUS_STR_SIZE 32 #endif /* Is there more data to come */ enum http_final_call { HTTP_DATA_MORE = 0, HTTP_DATA_FINAL = 1, }; struct http_request; struct http_response; /** * @typedef http_payload_cb_t * @brief Callback used when data needs to be sent to the server. * * @param sock Socket id of the connection * @param req HTTP request information * @param user_data User specified data specified in http_client_req() * * @return >=0 amount of data sent, in this case http_client_req() should * continue sending data, * <0 if http_client_req() should return the error code to the * caller. */ typedef int (*http_payload_cb_t)(int sock, struct http_request *req, void *user_data); /** * @typedef http_header_cb_t * @brief Callback can be used if application wants to construct additional * HTTP headers when the HTTP request is sent. Usage of this is optional. * * @param sock Socket id of the connection * @param req HTTP request information * @param user_data User specified data specified in http_client_req() * * @return >=0 amount of data sent, in this case http_client_req() should * continue sending data, * <0 if http_client_req() should return the error code to the * caller. */ typedef int (*http_header_cb_t)(int sock, struct http_request *req, void *user_data); /** * @typedef http_response_cb_t * @brief Callback used when data is received from the server. * * @param rsp HTTP response information * @param final_data Does this data buffer contain all the data or * is there still more data to come. * @param user_data User specified data specified in http_client_req() */ typedef void (*http_response_cb_t)(struct http_response *rsp, enum http_final_call final_data, void *user_data); /** * HTTP response from the server. */ struct http_response { /** HTTP parser settings for the application usage */ const struct http_parser_settings *http_cb; /** User provided HTTP response callback which is * called when a response is received to a sent HTTP * request. */ http_response_cb_t cb; /** Where the body starts */ uint8_t *body_start; /** Where the response is stored, this is to be * provided by the user. */ uint8_t *recv_buf; /** Response buffer maximum length */ size_t recv_buf_len; /** Length of the data in the result buf. If the value * is larger than recv_buf_len, then it means that * the data is truncated and could not be fully copied * into recv_buf. This can only happen if the user * did not set the response callback. If the callback * is set, then the HTTP client API will call response * callback many times so that all the data is * delivered to the user. */ size_t data_len; /** HTTP Content-Length field value */ size_t content_length; /** Content length parsed. This should be the same as * the content_length field if parsing was ok. */ size_t processed; /* https://tools.ietf.org/html/rfc7230#section-3.1.2 * The status-code element is a 3-digit integer code * * The reason-phrase element exists for the sole * purpose of providing a textual description * associated with the numeric status code. A client * SHOULD ignore the reason-phrase content. */ char http_status[HTTP_STATUS_STR_SIZE]; /** Numeric HTTP status code which corresponds to the * textual description. */ uint16_t http_status_code; uint8_t cl_present : 1; uint8_t body_found : 1; uint8_t message_complete : 1; }; /** HTTP client internal data that the application should not touch */ struct http_client_internal_data { /** Work for handling timeout */ struct k_work_delayable work; /** HTTP parser context */ struct http_parser parser; /** HTTP parser settings */ struct http_parser_settings parser_settings; /** HTTP response specific data (filled by http_client_req() when * data is received) */ struct http_response response; /** User data */ void *user_data; /** HTTP socket */ int sock; /** Request timeout */ k_timeout_t timeout; }; /** * HTTP client request. This contains all the data that is needed when doing * a HTTP request. */ struct http_request { /** HTTP client request internal data */ struct http_client_internal_data internal; /* User should fill in following parameters */ /** The HTTP method: GET, HEAD, OPTIONS, POST, ... */ enum http_method method; /** User supplied callback function to call when response is * received. */ http_response_cb_t response; /** User supplied list of HTTP callback functions if the * calling application wants to know the parsing status or the HTTP * fields. This is optional and normally not needed. */ const struct http_parser_settings *http_cb; /** User supplied buffer where received data is stored */ uint8_t *recv_buf; /** Length of the user supplied receive buffer */ size_t recv_buf_len; /** The URL for this request, for example: /index.html */ const char *url; /** The HTTP protocol, for example "HTTP/1.1" */ const char *protocol; /** The HTTP header fields (application specific) * The Content-Type may be specified here or in the next field. * Depending on your application, the Content-Type may vary, however * some header fields may remain constant through the application's * life cycle. This is a NULL terminated list of header fields. */ const char **header_fields; /** The value of the Content-Type header field, may be NULL */ const char *content_type_value; /** Hostname to be used in the request */ const char *host; /** Port number to be used in the request */ const char *port; /** User supplied callback function to call when payload * needs to be sent. This can be NULL in which case the payload field * in http_request is used. The idea of this payload callback is to * allow user to send more data that is practical to store in allocated * memory. */ http_payload_cb_t payload_cb; /** Payload, may be NULL */ const char *payload; /** Payload length is used to calculate Content-Length. Set to 0 * for chunked transfers. */ size_t payload_len; /** User supplied callback function to call when optional headers need * to be sent. This can be NULL, in which case the optional_headers * field in http_request is used. The idea of this optional_headers * callback is to allow user to send more HTTP header data that is * practical to store in allocated memory. */ http_header_cb_t optional_headers_cb; /** A NULL terminated list of any optional headers that * should be added to the HTTP request. May be NULL. * If the optional_headers_cb is specified, then this field is ignored. * Note that there are two similar fields that contain headers, * the header_fields above and this optional_headers. This is done * like this to support Websocket use case where Websocket will use * header_fields variable and any optional application specific * headers will be placed into this field. */ const char **optional_headers; }; /** * @brief Do a HTTP request. The callback is called when data is received * from the HTTP server. The caller must have created a connection to the * server before calling this function so connect() call must have be done * successfully for the socket. * * @param sock Socket id of the connection. * @param req HTTP request information * @param timeout Max timeout to wait for the data. The timeout value cannot be * 0 as there would be no time to receive the data. * The timeout value is in milliseconds. * @param user_data User specified data that is passed to the callback. * * @return <0 if error, >=0 amount of data sent to the server */ int http_client_req(int sock, struct http_request *req, int32_t timeout, void *user_data); #ifdef __cplusplus } #endif /** * @} */ #endif /* ZEPHYR_INCLUDE_NET_HTTP_CLIENT_H_ */