From d1675bf3e658c945599dad1d786ce8a7590c327e Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 9 Nov 2017 16:44:55 +0200 Subject: [PATCH] net: http: Remove the old legacy API There are no internal users for old HTTP API so removing it. Signed-off-by: Jukka Rissanen --- include/net/http.h | 1082 ++++++++++- include/net/http_app.h | 1092 ----------- include/net/http_legacy.h | 1244 ------------- subsys/net/ip/net_shell.c | 32 - subsys/net/lib/http/CMakeLists.txt | 11 +- subsys/net/lib/http/Kconfig | 60 - subsys/net/lib/http/{http_app.c => http.c} | 0 subsys/net/lib/http/http_app_client.c | 733 -------- subsys/net/lib/http/http_app_server.c | 922 ---------- subsys/net/lib/http/http_client.c | 1890 +++++--------------- subsys/net/lib/http/http_server.c | 1889 ++++++------------- 11 files changed, 2061 insertions(+), 6894 deletions(-) delete mode 100644 include/net/http_app.h delete mode 100644 include/net/http_legacy.h rename subsys/net/lib/http/{http_app.c => http.c} (100%) delete mode 100644 subsys/net/lib/http/http_app_client.c delete mode 100644 subsys/net/lib/http/http_app_server.c diff --git a/include/net/http.h b/include/net/http.h index eb4daa1a8a1..72b90752f0e 100644 --- a/include/net/http.h +++ b/include/net/http.h @@ -1,16 +1,1090 @@ /* - * Copyright (c) 2017 Intel Corporation + * Copyright (c) 2017 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ +/** + * @file + * + * @brief HTTP server and client implementation for Zephyr. + */ + #ifndef __HTTP_H__ #define __HTTP_H__ -#if defined(CONFIG_HTTP_APP) -#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(ZEPHYR_USER_AGENT) +#define ZEPHYR_USER_AGENT "Zephyr" +#endif + +#if !defined(CONFIG_HTTP_SERVER_NUM_URLS) +#define CONFIG_HTTP_SERVER_NUM_URLS 1 +#endif + +#if !defined(CONFIG_HTTP_HEADERS) +#define CONFIG_HTTP_HEADERS 1 +#endif + +#if !defined(HTTP_PROTOCOL) +#define HTTP_PROTOCOL "HTTP/1.1" +#endif + +/** + * @brief HTTP client and server library + * @defgroup http HTTP Library + * @{ + */ + +#define HTTP_CRLF "\r\n" + +struct http_ctx; + +enum http_state { + HTTP_STATE_CLOSED, + HTTP_STATE_WAITING_HEADER, + HTTP_STATE_RECEIVING_HEADER, + HTTP_STATE_HEADER_RECEIVED, + HTTP_STATE_OPEN, +}; + +enum http_url_flags { + HTTP_URL_STANDARD = 0, + HTTP_URL_WEBSOCKET, +}; + +enum http_connection_type { + HTTP_CONNECTION = 1, + WS_CONNECTION, +}; + +/* HTTP header fields struct */ +struct http_field_value { + /** Field name, this variable will point to the beginning of the string + * containing the HTTP field name + */ + const char *key; + + /** Value, this variable will point to the beginning of the string + * containing the field value + */ + const char *value; + + /** Length of the field name */ + u16_t key_len; + + /** Length of the field value */ + u16_t value_len; +}; + +/* HTTP root URL struct, used for pattern matching */ +struct http_root_url { + /** URL */ + const char *root; + + /** URL specific user data */ + u8_t *user_data; + + /** Length of the URL */ + u16_t root_len; + + /** Flags for this URL (values are from enum http_url_flags) */ + u8_t flags; + + /** Is this URL resource used or not */ + u8_t is_used; +}; + +enum http_verdict { + HTTP_VERDICT_DROP, + HTTP_VERDICT_ACCEPT, +}; + +/** + * @typedef http_url_cb_t + * @brief Default URL callback. + * + * @details This callback is called if there is a connection to unknown URL. + * + * @param ctx The context to use. + * @param type Connection type (websocket or HTTP) + * + * @return HTTP_VERDICT_DROP if connection is to be dropped, + * HTTP_VERDICT_ACCEPT if the application wants to accept the unknown URL. + */ +typedef enum http_verdict (*http_url_cb_t)(struct http_ctx *ctx, + enum http_connection_type type); + +/* Collection of URLs that this server will handle */ +struct http_server_urls { + /* First item is the default handler and it is always there. + */ + struct http_root_url default_url; + + /** Callback that is called when unknown (default) URL is received */ + http_url_cb_t default_cb; + + struct http_root_url urls[CONFIG_HTTP_SERVER_NUM_URLS]; +}; + +/** + * @typedef http_recv_cb_t + * @brief Network data receive callback. + * + * @details The recv callback is called after a network data is + * received. + * + * @param ctx The context to use. + * @param pkt Network buffer that is received. If the pkt is not NULL, + * then the callback will own the buffer and it needs to to unref the pkt + * as soon as it has finished working with it. On EOF, pkt will be NULL. + * @param status Value is set to 0 if some data or the connection is + * at EOF, <0 if there was an error receiving data, in this case the + * pkt parameter is set to NULL. + * @param flags Flags related to http. For example contains information + * if the data is text or binary etc. + * @param user_data The user data given in init call. + */ +typedef void (*http_recv_cb_t)(struct http_ctx *ctx, + struct net_pkt *pkt, + int status, + u32_t flags, + void *user_data); + +/** + * @typedef http_connect_cb_t + * @brief Connection callback. + * + * @details The connect callback is called after there was a connection to + * non-default URL. + * + * @param ctx The context to use. + * @param type Connection type (websocket or HTTP) + * @param user_data The user data given in init call. + */ +typedef void (*http_connect_cb_t)(struct http_ctx *ctx, + enum http_connection_type type, + void *user_data); + +/** + * @typedef http_send_cb_t + * @brief Network data send callback. + * + * @details The send callback is called after a network data is + * sent. + * + * @param ctx The context to use. + * @param status Value is set to 0 if all data was sent ok, <0 if + * there was an error sending data. >0 amount of data that was + * sent when not all data was sent ok. + * @param user_data_send The user data given in http_send() call. + * @param user_data The user data given in init call. + */ +typedef void (*http_send_cb_t)(struct http_ctx *ctx, + int status, + void *user_data_send, + void *user_data); + +/** + * @typedef http_close_cb_t + * @brief Close callback. + * + * @details The close callback is called after a connection was shutdown. + * + * @param ctx The context to use. + * @param status Error code for the closing. + * @param user_data The user data given in init call. + */ +typedef void (*http_close_cb_t)(struct http_ctx *ctx, + int status, + void *user_data); + +/** Websocket and HTTP callbacks */ +struct http_cb { + /** Function that is called when a connection is established. + */ + http_connect_cb_t connect; + + /** Function that is called when data is received from network. + */ + http_recv_cb_t recv; + + /** Function that is called when net_pkt is sent. + */ + http_send_cb_t send; + + /** Function that is called when connection is shutdown. + */ + http_close_cb_t close; +}; + +#if defined(CONFIG_HTTP_CLIENT) +/* Is there more data to come */ +enum http_final_call { + HTTP_DATA_MORE = 0, + HTTP_DATA_FINAL = 1, +}; + +/* Some generic configuration options, these can be overridden if needed. */ +#if !defined(HTTP_STATUS_STR_SIZE) +#define HTTP_STATUS_STR_SIZE 32 +#endif + +/** + * @typedef http_response_cb_t + * @brief Callback used when a response has been received from peer. + * + * @param ctx HTTP context. + * @param data Received data buffer + * @param buflen Data buffer len (as specified by user) + * @param datalen Received data len, if this is larger than buflen, + * then some data was skipped. + * @param final_data Does this data buffer contain all the data or + * is there still more data to come. + * @param user_data A valid pointer on some user data or NULL + */ +typedef void (*http_response_cb_t)(struct http_ctx *ctx, + u8_t *data, + size_t buflen, + size_t datalen, + enum http_final_call final_data, + void *user_data); + +/** + * HTTP client request. This contains all the data that is needed when doing + * a HTTP request. + */ +struct http_request { + /** The HTTP method: GET, HEAD, OPTIONS, POST, ... */ + enum http_method method; + + /** The URL for this request, for example: /index.html */ + const char *url; + + /** The HTTP protocol: 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. + */ + 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; + + /** Payload, may be NULL */ + const char *payload; + + /** Payload size, may be 0 */ + u16_t payload_size; +}; +#endif /* CONFIG_HTTP_CLIENT */ + +/** + * Http context information. This contains all the data that is + * needed when working with http API. + */ +struct http_ctx { + /** Net app context. The http connection is handled via + * the net app API. + */ + struct net_app_ctx app_ctx; + + /** Local endpoint IP address */ + struct sockaddr local; + + /** Original server address */ + struct sockaddr *server_addr; + + /** Pending data to be sent */ + struct net_pkt *pending; + +#if defined(CONFIG_HTTP_CLIENT) + /** Server name */ + const char *server; +#endif /* CONFIG_HTTP_CLIENT */ + + struct { +#if defined(CONFIG_HTTP_CLIENT) + /** Semaphore to signal HTTP connection creation. */ + struct k_sem connect_wait; + + /** HTTP request information */ + struct { + /** + * Semaphore to signal HTTP request completion + */ + struct k_sem wait; + + /** Hostname to be used in the request */ + const char *host; + + /** User provided data */ + void *user_data; + + /** What method we used here (GET, POST, HEAD etc.) + */ + enum http_method method; + } req; + + /** HTTP response information */ + struct { + /** 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. + */ + u8_t *body_start; + + /** Where the response is stored, this is to be + * provided by the user. + */ + u8_t *response_buf; + + /** Response buffer maximum length */ + size_t response_buf_len; + + /** Length of the data in the result buf. If the value + * is larger than response_buf_len, then it means that + * the data is truncated and could not be fully copied + * into response_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]; + + u8_t cl_present:1; + u8_t body_found:1; + u8_t message_complete:1; + } rsp; +#endif /* CONFIG_HTTP_CLIENT */ + + /** HTTP parser for parsing the initial request */ + struct http_parser parser; + + /** HTTP parser settings */ + struct http_parser_settings parser_settings; + +#if defined(CONFIG_HTTP_SERVER) + /** Collection of HTTP header fields */ + struct http_field_value field_values[CONFIG_HTTP_HEADERS]; + + /** Collection of HTTP URLs that this context will handle. */ + struct http_server_urls *urls; + + /** Where the request is stored, this needs to be provided by + * the user. + */ + u8_t *request_buf; + + /** Request buffer maximum length */ + size_t request_buf_len; + + /** Length of the data in the request buf. */ + size_t data_len; + + /** Number of header field elements */ + u16_t field_values_ctr; +#endif /* CONFIG_HTTP_SERVER */ + + /** HTTP Request URL */ + const char *url; + + /** URL's length */ + u16_t url_len; + } http; + +#if defined(CONFIG_NET_DEBUG_HTTP_CONN) + sys_snode_t node; +#endif /* CONFIG_HTTP_DEBUG_HTTP_CONN */ + + /** HTTP callbacks */ + struct http_cb cb; + + /** User specified data that is passed in callbacks. */ + u8_t *user_data; + + /** State of the websocket */ + enum http_state state; + + /** Network buffer allocation timeout */ + s32_t timeout; + + /** Websocket endpoint address */ + struct sockaddr *addr; + + /** Websocket endpoint port */ + u16_t port; + + /** Is this context setup or not */ + u8_t is_init : 1; + + /** Is this context setup for client or server */ + u8_t is_client : 1; + + /** Is this instance supporting TLS or not. */ + u8_t is_tls : 1; + + /** Are we connected or not (only used in client) */ + u8_t is_connected : 1; +}; + +#if defined(CONFIG_HTTP_SERVER) +/** + * @brief Create a HTTP listener. + * + * @details Note that the context must be valid for the whole duration of the + * http life cycle. This usually means that it cannot be allocated from + * stack. + * + * @param ctx Http context. This init function will initialize it. + * @param urls Array of URLs that the server instance will serve. If the + * server receives a HTTP request into one of the URLs, it will call user + * supplied callback. If no such URL is registered, a default handler will + * be called (if set by the user). If no data handler is found, the request + * is dropped. + * @param server_addr Socket address of the local network interface and TCP + * port where the data is being waited. If the socket family is set to + * AF_UNSPEC, then both IPv4 and IPv6 is started to be listened. If the + * address is set to be INADDR_ANY (for IPv4) or unspecified address (all bits + * zeros for IPv6), then the HTTP server will select proper IP address to bind + * to. If caller has not specified HTTP listening port, then port 80 is being + * listened. The parameter can be left NULL in which case a listener to port 80 + * using IPv4 and IPv6 is created. Note that if IPv4 or IPv6 is disabled, then + * the corresponding disabled service listener is not created. + * @param request_buf Caller-supplied buffer where the HTTP request will be + * stored + * @param request_buf_len Length of the caller-supplied buffer. + * @param server_banner Print information about started service. This is only + * printed if HTTP debugging is activated. The parameter can be set to NULL if + * no extra prints are needed. + * @param user_data User specific data that is passed as is to the connection + * callbacks. + * + * @return 0 if ok, <0 if error. + */ +int http_server_init(struct http_ctx *ctx, + struct http_server_urls *urls, + struct sockaddr *server_addr, + u8_t *request_buf, + size_t request_buf_len, + const char *server_banner, + void *user_data); + +#if defined(CONFIG_HTTPS) +/** + * @brief Initialize TLS support for this http context + * + * @param ctx Http context + * @param server_banner Print information about started service. This is only + * printed if net_app debugging is activated. The parameter can be set to NULL + * if no extra prints are needed. + * @param personalization_data Personalization data (Device specific + * identifiers) for random number generator. (Can be NULL). + * @param personalization_data_len Length of the personalization data. + * @param cert_cb User supplied callback that setups the certificates. + * @param entropy_src_cb User supplied callback that setup the entropy. This + * can be set to NULL, in which case default entropy source is used. + * @param pool Memory pool for RX data reads. + * @param stack TLS thread stack. + * @param stack_len TLS thread stack size. + * + * @return Return 0 if ok, <0 if error. + */ +int http_server_set_tls(struct http_ctx *ctx, + const char *server_banner, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_cert_cb_t cert_cb, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + k_thread_stack_t *stack, + size_t stack_len); + +#endif /* CONFIG_HTTPS */ + +/** + * @brief Enable HTTP server that is related to this context. + * + * @detail The HTTP server will start to serve request after this. + * + * @param ctx Http context. + * + * @return 0 if server is enabled, <0 otherwise + */ +int http_server_enable(struct http_ctx *ctx); + +/** + * @brief Disable HTTP server that is related to this context. + * + * @detail The HTTP server will stop to serve request after this. + * + * @param ctx Http context. + * + * @return 0 if server is disabled, <0 if there was an error + */ +int http_server_disable(struct http_ctx *ctx); + +/** + * @brief Add an URL to a list of URLs that are tied to certain webcontext. + * + * @param urls URL struct that will contain all the URLs the user wants to + * register. + * @param url URL string. + * @param flags Flags for the URL. + * + * @return NULL if the URL is already registered, pointer to URL if + * registering was ok. + */ +struct http_root_url *http_server_add_url(struct http_server_urls *urls, + const char *url, u8_t flags); + +/** + * @brief Delete the URL from list of URLs that are tied to certain + * webcontext. + * + * @param urls URL struct that will contain all the URLs the user has + * registered. + * @param url URL string. + * + * @return 0 if ok, <0 if error. + */ +int http_server_del_url(struct http_server_urls *urls, const char *url); + +/** + * @brief Add default URL handler. + * + * @detail If no URL handler is found, then call this handler. There can + * be only one default handler in the URL struct. The callback can decide + * if the connection request is dropped or passed. + * + * @param urls URL struct that will contain all the URLs the user has + * registered. + * @param cb Callback that is called when non-registered URL is requested. + * + * @return NULL if default URL is already registered, pointer to default + * URL if registering was ok. + */ +struct http_root_url *http_server_add_default(struct http_server_urls *urls, + http_url_cb_t cb); + +/** + * @brief Delete the default URL handler. + * + * @detail Unregister the previously registered default URL handler. + * + * @param urls URL struct that will contain all the URLs the user has + * registered. + * + * @return 0 if ok, <0 if error. + */ +int http_server_del_default(struct http_server_urls *urls); + +#else /* CONFIG_HTTP_SERVER */ + +static inline int http_server_init(struct http_ctx *ctx, + struct http_server_urls *urls, + struct sockaddr *server_addr, + u8_t *request_buf, + size_t request_buf_len, + const char *server_banner) +{ + ARG_UNUSED(ctx); + ARG_UNUSED(urls); + ARG_UNUSED(server_addr); + ARG_UNUSED(request_buf); + ARG_UNUSED(request_buf_len); + ARG_UNUSED(server_banner); + + return -ENOTSUP; +} + +#if defined(CONFIG_HTTPS) +static inline int http_server_set_tls(struct http_ctx *ctx, + const char *server_banner, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_cert_cb_t cert_cb, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + k_thread_stack_t *stack, + size_t stack_len) +{ + ARG_UNUSED(ctx); + ARG_UNUSED(server_banner); + ARG_UNUSED(personalization_data); + ARG_UNUSED(personalization_data_len); + ARG_UNUSED(cert_cb); + ARG_UNUSED(entropy_src_cb); + ARG_UNUSED(pool); + ARG_UNUSED(stack); + ARG_UNUSED(stack_len); + + return -ENOTSUP; +} +#endif /* CONFIG_HTTPS */ + +static inline int http_server_enable(struct http_ctx *ctx) +{ + ARG_UNUSED(ctx); + return -ENOTSUP; +} + +static inline int http_server_disable(struct http_ctx *ctx) +{ + ARG_UNUSED(ctx); + return -ENOTSUP; +} + +static inline +struct http_root_url *http_server_add_url(struct http_server_urls *urls, + const char *url, u8_t flags) +{ + ARG_UNUSED(urls); + ARG_UNUSED(url); + ARG_UNUSED(flags); + + return NULL; +} +#endif /* CONFIG_HTTP_SERVER */ + +#if defined(CONFIG_HTTP_CLIENT) +/** + * @brief Generic function to send a HTTP request to the network. Normally + * applications would not need to use this function. + * + * @param ctx HTTP context. + * @param req HTTP request to perform. + * @param timeout Timeout when doing net_buf allocations. + * @param user_data User data related to this request. This is passed + * to send callback if user has specified that. + * + * @return Return 0 if ok, and <0 if error. + */ +int http_request(struct http_ctx *ctx, + struct http_request *req, + s32_t timeout, + void *user_data); + +/** + * @brief Cancel a pending request. + * + * @param ctx HTTP context. + * + * @return Return 0 if ok, and <0 if error. + */ +int http_request_cancel(struct http_ctx *ctx); + +/** + * @brief Send a HTTP request to peer. + * + * @param http_ctx HTTP context. + * @param req HTTP request to perform. + * @param cb Callback to call when the response has been received from peer. + * @param response_buf Caller-supplied buffer where the HTTP response will be + * stored + * @param response_buf_len Length of the caller-supplied buffer. + * @param user_data A valid pointer on some user data or NULL + * @param timeout Amount of time to wait for a reply. If the timeout is 0, + * then we return immediately and the callback (if set) will be called later. + * + * @return Return 0 if ok, and <0 if error. + */ +int http_client_send_req(struct http_ctx *http_ctx, + struct http_request *req, + http_response_cb_t cb, + u8_t *response_buf, + size_t response_buf_len, + void *user_data, + s32_t timeout); + +/** + * @brief Send a HTTP GET request to peer. + * + * @param http_ctx HTTP context. + * @param url URL to use. + * @param host Host field in HTTP header. If set to NULL, then server + * name is used. + * @param extra_header_fields Any extra header fields that caller wants + * to add. This can be set to NULL. The format is "name: value\\r\\n" + * Example: "Accept: text/plain\\r\\nConnection: Close\\r\\n" + * @param cb Callback to call when the response has been received from peer. + * @param response_buf Caller-supplied buffer where the HTTP request will be + * stored + * @param response_buf_len Length of the caller-supplied buffer. + * @param user_data A valid pointer on some user data or NULL + * @param timeout Amount of time to wait for a reply. If the timeout is 0, + * then we return immediately and the callback (if set) will be called later. + * + * @return Return 0 if ok, and <0 if error. + */ +static inline int http_client_send_get_req(struct http_ctx *http_ctx, + const char *url, + const char *host, + const char *extra_header_fields, + http_response_cb_t cb, + u8_t *response_buf, + size_t response_buf_len, + void *user_data, + s32_t timeout) +{ + struct http_request req = { + .method = HTTP_GET, + .url = url, + .host = host, + .protocol = " " HTTP_PROTOCOL HTTP_CRLF, + .header_fields = extra_header_fields, + }; + + return http_client_send_req(http_ctx, &req, cb, response_buf, + response_buf_len, user_data, timeout); +} + +/** + * @brief Initialize user-supplied HTTP context. + * + * @detail Caller can set the various fields in http_ctx after this call + * if needed. + * + * @param http_ctx HTTP context. + * @param server HTTP server address or host name. If host name is given, + * then DNS resolver support (CONFIG_DNS_RESOLVER) must be enabled. If caller + * sets the server parameter as NULL, then the server_addr parameter must be + * set by the user and it is used when connecting to the server. + * @param server_port HTTP server TCP port. If server parameter is NULL, then + * this value is ignored. + * @param server_addr HTTP server IP address and port. This is only used if + * server parameter is not set. + * @param timeout If server address needs to be resolved, then this value is + * used as a timeout. + * + * @return Return 0 if ok, <0 if error. + */ +int http_client_init(struct http_ctx *http_ctx, + const char *server, + u16_t server_port, + struct sockaddr *server_addr, + s32_t timeout); + +#if defined(CONFIG_HTTPS) +/** + * @brief Initialize user-supplied HTTP context when using HTTPS. + * + * @detail Caller can set the various fields in http_ctx after this call + * if needed. + * + * @param ctx HTTPS context. + * @param request_buf Caller-supplied buffer where the TLS request will be + * stored + * @param request_buf_len Length of the caller-supplied buffer. + * @param personalization_data Personalization data (Device specific + * identifiers) for random number generator. (Can be NULL). + * @param personalization_data_len Length of the personalization data. + * @param cert_cb User-supplied callback that setups the certificates. + * @param cert_host Hostname that is used to verify the server certificate. + * This value is used when HTTP client API calls mbedtls_ssl_set_hostname() + * which sets the hostname to check against the received server certificate. + * See https://tls.mbed.org/kb/how-to/use-sni for more details. + * This can be left NULL in which case mbedtls will silently skip certificate + * verification entirely. This option is only used if MBEDTLS_X509_CRT_PARSE_C + * is enabled in mbedtls config file. + * @param entropy_src_cb User-supplied callback that setup the entropy. This + * can be set to NULL, in which case default entropy source is used. + * @param pool Memory pool for RX data reads. + * @param https_stack HTTPS thread stack. + * @param https_stack_len HTTP thread stack size. + * + * @return Return 0 if ok, <0 if error. + */ +int http_client_set_tls(struct http_ctx *ctx, + u8_t *request_buf, + size_t request_buf_len, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_ca_cert_cb_t cert_cb, + const char *cert_host, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + k_thread_stack_t *https_stack, + size_t https_stack_size); +#endif /* CONFIG_HTTPS */ +#endif /* CONFIG_HTTP_CLIENT */ + +/** + * @brief Close a network connection to peer. + * + * @param ctx Http context. + * + * @return 0 if ok, <0 if error. + */ +int http_close(struct http_ctx *ctx); + +/** + * @brief Release this http context. + * + * @details No network data will be received via this context after this + * call. + * + * @param ctx Http context. + * + * @return 0 if ok, <0 if error. + */ +int http_release(struct http_ctx *ctx); + +/** + * @brief Set various callbacks that are called at various stage of ws session. + * + * @param ctx Http context. + * @param connect_cb Connect callback. + * @param recv_cb Data receive callback. + * @param send_cb Data sent callback. + * @param close_cb Close callback. + * + * @return 0 if ok, <0 if error. + */ +int http_set_cb(struct http_ctx *ctx, + http_connect_cb_t connect_cb, + http_recv_cb_t recv_cb, + http_send_cb_t send_cb, + http_close_cb_t close_cb); + +/** + * @brief Send a message to peer. The data can be either HTTP or websocket + * data. + * + * @details This does not modify the network packet but sends it as is. + * + * @param ctx Http context. + * @param pkt Network packet to send + * @param user_send_data User specific data to this connection. This is passed + * as a parameter to sent cb after the packet has been sent. + * + * @return 0 if ok, <0 if error. + */ +int http_send_msg_raw(struct http_ctx *ctx, struct net_pkt *pkt, + void *user_send_data); + +/** + * @brief Prepare HTTP data to be sent to peer. If there is enough data, the + * function will then send it too. + * + * @param ctx Http context. + * @param payload Application data to send + * @param payload_len Length of application data + * @param user_send_data User specific data to this connection. This is passed + * as a parameter to sent cb after the packet has been sent. + * + * @return 0 if ok, <0 if error. + */ +int http_prepare_and_send(struct http_ctx *ctx, const char *payload, + size_t payload_len, void *user_send_data); + +/** + * @brief Send HTTP data chunk to peer. + * + * @param ctx Http context. + * @param payload Application data to send + * @param payload_len Length of application data + * @param user_send_data User specific data to this connection. This is passed + * as a parameter to sent cb after the packet has been sent. + * + * @return 0 if ok, <0 if error. + */ +int http_send_chunk(struct http_ctx *ctx, const char *payload, + size_t payload_len, void *user_send_data); + +/** + * @brief Send any pending data immediately. + * + * @param ctx Http context. + * @param user_send_data Optional user_data for that is used as a parameter in + * send callback if that is specified. + * + * @return 0 if ok, <0 if error. + */ +int http_send_flush(struct http_ctx *ctx, void *user_send_data); + +/** + * @brief Send HTTP error message to peer. + * + * @param ctx Http context. + * @param code HTTP error code + * @param html_payload Extra payload, can be null + * @param html_len Payload length + * + * @return 0 if ok, <0 if error. + */ +int http_send_error(struct http_ctx *ctx, int code, u8_t *html_payload, + size_t html_len); + +/** + * @brief Add HTTP header field to the message. + * + * @details This can be called multiple times to add pieces of HTTP header + * fields into the message. Caller must put the "\\r\\n" characters to the + * input http_header_field variable. + * + * @param ctx Http context. + * @param http_header_field All or part of HTTP header to be added. + * @param user_send_data User data value that is used in send callback. + * Note that this value is only used if this function call causes a HTTP + * message to be sent. + * + * @return <0 if error, other value tells how many bytes were added + */ +int http_add_header(struct http_ctx *ctx, const char *http_header_field, + void *user_send_data); + +/** + * @brief Add HTTP header field to the message. + * + * @details This can be called multiple times to add pieces of HTTP header + * fields into the message. If name is "Foo" and value is "bar", then this + * function will add "Foo: bar\\r\\n" to message. + * + * @param ctx Http context. + * @param name Name of the header field + * @param value Value of the header field + * @param user_send_data User data value that is used in send callback. + * Note that this value is only used if this function call causes a HTTP + * message to be sent. + * + * @return <0 if error, other value tells how many bytes were added + */ +int http_add_header_field(struct http_ctx *ctx, const char *name, + const char *value, void *user_send_data); + +/** + * @brief Find a handler function for a given URL. + * + * @details This is internal function, do not call this from application. + * + * @param ctx Http context. + * @param flags Tells if the URL is either HTTP or websocket URL + * + * @return URL handler or NULL if no such handler was found. + */ +struct http_root_url *http_url_find(struct http_ctx *ctx, + enum http_url_flags flags); + +#define http_change_state(ctx, new_state) \ + _http_change_state(ctx, new_state, __func__, __LINE__) + +/** + * @brief Change the state of the HTTP engine + * + * @details This is internal function, do not call this from application. + * + * @param ctx HTTP context. + * @param new_state New state of the context. + * @param func Function that changed the state (for debugging) + * @param line Line number of the function (for debugging) + */ +void _http_change_state(struct http_ctx *ctx, + enum http_state new_state, + const char *func, int line); + +#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_NET_DEBUG_HTTP_CONN) +/** + * @typedef http_server_cb_t + * @brief HTTP connection monitoring callback. + * + * @details This callback is called by server if new HTTP connection is + * established or disconnected. Requires that HTTP connection monitoring + * is enabled. + * + * @param ctx The HTTP context to use. + * @param user_data User specified data. + */ +typedef void (*http_server_cb_t)(struct http_ctx *entry, + void *user_data); + +/** + * @brief Go through all the HTTP connections in the server and call + * user provided callback for each connection. + * + * @param cb Callback to call for each HTTP connection. + * @param user_data User provided data that is passed to callback. + */ +void http_server_conn_foreach(http_server_cb_t cb, void *user_data); + +/** + * @brief Register a callback that is called if HTTP connection is + * established or disconnected. + * + * @details This is normally called only by "net http monitor" shell command. + * + * @param cb Callback to call for each HTTP connection created or + * deleted. + * @param user_data User provided data that is passed to callback. + */ +void http_server_conn_monitor(http_server_cb_t cb, void *user_data); + +/** + * @brief HTTP connection was established. + * + * @details This is internal function, do not call this from application. + * + * @param ctx HTTP context. + */ +void http_server_conn_add(struct http_ctx *ctx); + +/** + * @brief HTTP connection was disconnected. + * + * @details This is internal function, do not call this from application. + * + * @param ctx HTTP context. + */ +void http_server_conn_del(struct http_ctx *ctx); #else -#include +#define http_server_conn_foreach(...) +#define http_server_conn_monitor(...) +#define http_server_conn_add(...) +#define http_server_conn_del(...) +#endif /* CONFIG_HTTP_SERVER && CONFIG_NET_DEBUG_HTTP_CONN */ + +/** + * @} + */ + +#ifdef __cplusplus +} #endif #endif /* __HTTP_H__ */ diff --git a/include/net/http_app.h b/include/net/http_app.h deleted file mode 100644 index a601c788847..00000000000 --- a/include/net/http_app.h +++ /dev/null @@ -1,1092 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * - * @brief HTTP server and client implementation for Zephyr. - */ - -#ifndef __HTTP_APP_H__ -#define __HTTP_APP_H__ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if !defined(ZEPHYR_USER_AGENT) -#define ZEPHYR_USER_AGENT "Zephyr" -#endif - -#if !defined(CONFIG_HTTP_SERVER_NUM_URLS) -#define CONFIG_HTTP_SERVER_NUM_URLS 1 -#endif - -#if !defined(CONFIG_HTTP_HEADERS) -#define CONFIG_HTTP_HEADERS 1 -#endif - -#if !defined(HTTP_PROTOCOL) -#define HTTP_PROTOCOL "HTTP/1.1" -#endif - -/** - * @brief HTTP client and server library - * @defgroup http HTTP Library - * @ingroup networking - * @{ - */ - -#define HTTP_CRLF "\r\n" - -struct http_ctx; - -enum http_state { - HTTP_STATE_CLOSED, - HTTP_STATE_WAITING_HEADER, - HTTP_STATE_RECEIVING_HEADER, - HTTP_STATE_HEADER_RECEIVED, - HTTP_STATE_OPEN, -}; - -enum http_url_flags { - HTTP_URL_STANDARD = 0, - HTTP_URL_WEBSOCKET, -}; - -enum http_connection_type { - HTTP_CONNECTION = 1, - WS_CONNECTION, -}; - -/* HTTP header fields struct */ -struct http_field_value { - /** Field name, this variable will point to the beginning of the string - * containing the HTTP field name - */ - const char *key; - - /** Value, this variable will point to the beginning of the string - * containing the field value - */ - const char *value; - - /** Length of the field name */ - u16_t key_len; - - /** Length of the field value */ - u16_t value_len; -}; - -/* HTTP root URL struct, used for pattern matching */ -struct http_root_url { - /** URL */ - const char *root; - - /** URL specific user data */ - u8_t *user_data; - - /** Length of the URL */ - u16_t root_len; - - /** Flags for this URL (values are from enum http_url_flags) */ - u8_t flags; - - /** Is this URL resource used or not */ - u8_t is_used; -}; - -enum http_verdict { - HTTP_VERDICT_DROP, - HTTP_VERDICT_ACCEPT, -}; - -/** - * @typedef http_url_cb_t - * @brief Default URL callback. - * - * @details This callback is called if there is a connection to unknown URL. - * - * @param ctx The context to use. - * @param type Connection type (websocket or HTTP) - * - * @return HTTP_VERDICT_DROP if connection is to be dropped, - * HTTP_VERDICT_ACCEPT if the application wants to accept the unknown URL. - */ -typedef enum http_verdict (*http_url_cb_t)(struct http_ctx *ctx, - enum http_connection_type type); - -/* Collection of URLs that this server will handle */ -struct http_server_urls { - /* First item is the default handler and it is always there. - */ - struct http_root_url default_url; - - /** Callback that is called when unknown (default) URL is received */ - http_url_cb_t default_cb; - - struct http_root_url urls[CONFIG_HTTP_SERVER_NUM_URLS]; -}; - -/** - * @typedef http_recv_cb_t - * @brief Network data receive callback. - * - * @details The recv callback is called after a network data is - * received. - * - * @param ctx The context to use. - * @param pkt Network buffer that is received. If the pkt is not NULL, - * then the callback will own the buffer and it needs to to unref the pkt - * as soon as it has finished working with it. On EOF, pkt will be NULL. - * @param status Value is set to 0 if some data or the connection is - * at EOF, <0 if there was an error receiving data, in this case the - * pkt parameter is set to NULL. - * @param flags Flags related to http. For example contains information - * if the data is text or binary etc. - * @param user_data The user data given in init call. - */ -typedef void (*http_recv_cb_t)(struct http_ctx *ctx, - struct net_pkt *pkt, - int status, - u32_t flags, - void *user_data); - -/** - * @typedef http_connect_cb_t - * @brief Connection callback. - * - * @details The connect callback is called after there was a connection to - * non-default URL. - * - * @param ctx The context to use. - * @param type Connection type (websocket or HTTP) - * @param user_data The user data given in init call. - */ -typedef void (*http_connect_cb_t)(struct http_ctx *ctx, - enum http_connection_type type, - void *user_data); - -/** - * @typedef http_send_cb_t - * @brief Network data send callback. - * - * @details The send callback is called after a network data is - * sent. - * - * @param ctx The context to use. - * @param status Value is set to 0 if all data was sent ok, <0 if - * there was an error sending data. >0 amount of data that was - * sent when not all data was sent ok. - * @param user_data_send The user data given in http_send() call. - * @param user_data The user data given in init call. - */ -typedef void (*http_send_cb_t)(struct http_ctx *ctx, - int status, - void *user_data_send, - void *user_data); - -/** - * @typedef http_close_cb_t - * @brief Close callback. - * - * @details The close callback is called after a connection was shutdown. - * - * @param ctx The context to use. - * @param status Error code for the closing. - * @param user_data The user data given in init call. - */ -typedef void (*http_close_cb_t)(struct http_ctx *ctx, - int status, - void *user_data); - -/** Websocket and HTTP callbacks */ -struct http_cb { - /** Function that is called when a connection is established. - */ - http_connect_cb_t connect; - - /** Function that is called when data is received from network. - */ - http_recv_cb_t recv; - - /** Function that is called when net_pkt is sent. - */ - http_send_cb_t send; - - /** Function that is called when connection is shutdown. - */ - http_close_cb_t close; -}; - -#if defined(CONFIG_HTTP_CLIENT) -/* Is there more data to come */ -enum http_final_call { - HTTP_DATA_MORE = 0, - HTTP_DATA_FINAL = 1, -}; - -/* Some generic configuration options, these can be overridden if needed. */ -#if !defined(HTTP_STATUS_STR_SIZE) -#define HTTP_STATUS_STR_SIZE 32 -#endif - -/** - * @typedef http_response_cb_t - * @brief Callback used when a response has been received from peer. - * - * @param ctx HTTP context. - * @param data Received data buffer - * @param buflen Data buffer len (as specified by user) - * @param datalen Received data len, if this is larger than buflen, - * then some data was skipped. - * @param final_data Does this data buffer contain all the data or - * is there still more data to come. - * @param user_data A valid pointer on some user data or NULL - */ -typedef void (*http_response_cb_t)(struct http_ctx *ctx, - u8_t *data, - size_t buflen, - size_t datalen, - enum http_final_call final_data, - void *user_data); - -/** - * HTTP client request. This contains all the data that is needed when doing - * a HTTP request. - */ -struct http_request { - /** The HTTP method: GET, HEAD, OPTIONS, POST, ... */ - enum http_method method; - - /** The URL for this request, for example: /index.html */ - const char *url; - - /** The HTTP protocol: 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. - */ - 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; - - /** Payload, may be NULL */ - const char *payload; - - /** Payload size, may be 0 */ - u16_t payload_size; -}; -#endif /* CONFIG_HTTP_CLIENT */ - -/** - * Http context information. This contains all the data that is - * needed when working with http API. - */ -struct http_ctx { - /** Net app context. The http connection is handled via - * the net app API. - */ - struct net_app_ctx app_ctx; - - /** Local endpoint IP address */ - struct sockaddr local; - - /** Original server address */ - struct sockaddr *server_addr; - - /** Pending data to be sent */ - struct net_pkt *pending; - -#if defined(CONFIG_HTTP_CLIENT) - /** Server name */ - const char *server; -#endif /* CONFIG_HTTP_CLIENT */ - - struct { -#if defined(CONFIG_HTTP_CLIENT) - /** Semaphore to signal HTTP connection creation. */ - struct k_sem connect_wait; - - /** HTTP request information */ - struct { - /** - * Semaphore to signal HTTP request completion - */ - struct k_sem wait; - - /** Hostname to be used in the request */ - const char *host; - - /** User provided data */ - void *user_data; - - /** What method we used here (GET, POST, HEAD etc.) - */ - enum http_method method; - } req; - - /** HTTP response information */ - struct { - /** 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. - */ - u8_t *body_start; - - /** Where the response is stored, this is to be - * provided by the user. - */ - u8_t *response_buf; - - /** Response buffer maximum length */ - size_t response_buf_len; - - /** Length of the data in the result buf. If the value - * is larger than response_buf_len, then it means that - * the data is truncated and could not be fully copied - * into response_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]; - - u8_t cl_present:1; - u8_t body_found:1; - u8_t message_complete:1; - } rsp; -#endif /* CONFIG_HTTP_CLIENT */ - - /** HTTP parser for parsing the initial request */ - struct http_parser parser; - - /** HTTP parser settings */ - struct http_parser_settings parser_settings; - -#if defined(CONFIG_HTTP_SERVER) - /** Collection of HTTP header fields */ - struct http_field_value field_values[CONFIG_HTTP_HEADERS]; - - /** Collection of HTTP URLs that this context will handle. */ - struct http_server_urls *urls; - - /** Where the request is stored, this needs to be provided by - * the user. - */ - u8_t *request_buf; - - /** Request buffer maximum length */ - size_t request_buf_len; - - /** Length of the data in the request buf. */ - size_t data_len; - - /** Number of header field elements */ - u16_t field_values_ctr; -#endif /* CONFIG_HTTP_SERVER */ - - /** HTTP Request URL */ - const char *url; - - /** URL's length */ - u16_t url_len; - } http; - -#if defined(CONFIG_NET_DEBUG_HTTP_CONN) - sys_snode_t node; -#endif /* CONFIG_HTTP_DEBUG_HTTP_CONN */ - - /** HTTP callbacks */ - struct http_cb cb; - - /** User specified data that is passed in callbacks. */ - u8_t *user_data; - - /** State of the websocket */ - enum http_state state; - - /** Network buffer allocation timeout */ - s32_t timeout; - - /** Websocket endpoint address */ - struct sockaddr *addr; - - /** Websocket endpoint port */ - u16_t port; - - /** Is this context setup or not */ - u8_t is_init : 1; - - /** Is this context setup for client or server */ - u8_t is_client : 1; - - /** Is this instance supporting TLS or not. */ - u8_t is_tls : 1; - - /** Are we connected or not (only used in client) */ - u8_t is_connected : 1; -}; - -#if defined(CONFIG_HTTP_SERVER) -/** - * @brief Create a HTTP listener. - * - * @details Note that the context must be valid for the whole duration of the - * http life cycle. This usually means that it cannot be allocated from - * stack. - * - * @param ctx Http context. This init function will initialize it. - * @param urls Array of URLs that the server instance will serve. If the - * server receives a HTTP request into one of the URLs, it will call user - * supplied callback. If no such URL is registered, a default handler will - * be called (if set by the user). If no data handler is found, the request - * is dropped. - * @param server_addr Socket address of the local network interface and TCP - * port where the data is being waited. If the socket family is set to - * AF_UNSPEC, then both IPv4 and IPv6 is started to be listened. If the - * address is set to be INADDR_ANY (for IPv4) or unspecified address (all bits - * zeros for IPv6), then the HTTP server will select proper IP address to bind - * to. If caller has not specified HTTP listening port, then port 80 is being - * listened. The parameter can be left NULL in which case a listener to port 80 - * using IPv4 and IPv6 is created. Note that if IPv4 or IPv6 is disabled, then - * the corresponding disabled service listener is not created. - * @param request_buf Caller-supplied buffer where the HTTP request will be - * stored - * @param request_buf_len Length of the caller-supplied buffer. - * @param server_banner Print information about started service. This is only - * printed if HTTP debugging is activated. The parameter can be set to NULL if - * no extra prints are needed. - * @param user_data User specific data that is passed as is to the connection - * callbacks. - * - * @return 0 if ok, <0 if error. - */ -int http_server_init(struct http_ctx *ctx, - struct http_server_urls *urls, - struct sockaddr *server_addr, - u8_t *request_buf, - size_t request_buf_len, - const char *server_banner, - void *user_data); - -#if defined(CONFIG_HTTPS) && defined(CONFIG_NET_APP_SERVER) -/** - * @brief Initialize TLS support for this http context - * - * @param ctx Http context - * @param server_banner Print information about started service. This is only - * printed if net_app debugging is activated. The parameter can be set to NULL - * if no extra prints are needed. - * @param personalization_data Personalization data (Device specific - * identifiers) for random number generator. (Can be NULL). - * @param personalization_data_len Length of the personalization data. - * @param cert_cb User supplied callback that setups the certificates. - * @param entropy_src_cb User supplied callback that setup the entropy. This - * can be set to NULL, in which case default entropy source is used. - * @param pool Memory pool for RX data reads. - * @param stack TLS thread stack. - * @param stack_len TLS thread stack size. - * - * @return Return 0 if ok, <0 if error. - */ -int http_server_set_tls(struct http_ctx *ctx, - const char *server_banner, - u8_t *personalization_data, - size_t personalization_data_len, - net_app_cert_cb_t cert_cb, - net_app_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *stack, - size_t stack_len); - -#endif /* CONFIG_HTTPS && CONFIG_NET_APP_SERVER */ - -/** - * @brief Enable HTTP server that is related to this context. - * - * @details The HTTP server will start to serve request after this. - * - * @param ctx Http context. - * - * @return 0 if server is enabled, <0 otherwise - */ -int http_server_enable(struct http_ctx *ctx); - -/** - * @brief Disable HTTP server that is related to this context. - * - * @details The HTTP server will stop to serve request after this. - * - * @param ctx Http context. - * - * @return 0 if server is disabled, <0 if there was an error - */ -int http_server_disable(struct http_ctx *ctx); - -/** - * @brief Add an URL to a list of URLs that are tied to certain webcontext. - * - * @param urls URL struct that will contain all the URLs the user wants to - * register. - * @param url URL string. - * @param flags Flags for the URL. - * - * @return NULL if the URL is already registered, pointer to URL if - * registering was ok. - */ -struct http_root_url *http_server_add_url(struct http_server_urls *urls, - const char *url, u8_t flags); - -/** - * @brief Delete the URL from list of URLs that are tied to certain - * webcontext. - * - * @param urls URL struct that will contain all the URLs the user has - * registered. - * @param url URL string. - * - * @return 0 if ok, <0 if error. - */ -int http_server_del_url(struct http_server_urls *urls, const char *url); - -/** - * @brief Add default URL handler. - * - * @details If no URL handler is found, then call this handler. There can - * be only one default handler in the URL struct. The callback can decide - * if the connection request is dropped or passed. - * - * @param urls URL struct that will contain all the URLs the user has - * registered. - * @param cb Callback that is called when non-registered URL is requested. - * - * @return NULL if default URL is already registered, pointer to default - * URL if registering was ok. - */ -struct http_root_url *http_server_add_default(struct http_server_urls *urls, - http_url_cb_t cb); - -/** - * @brief Delete the default URL handler. - * - * @details Unregister the previously registered default URL handler. - * - * @param urls URL struct that will contain all the URLs the user has - * registered. - * - * @return 0 if ok, <0 if error. - */ -int http_server_del_default(struct http_server_urls *urls); - -#else /* CONFIG_HTTP_SERVER */ - -static inline int http_server_init(struct http_ctx *ctx, - struct http_server_urls *urls, - struct sockaddr *server_addr, - u8_t *request_buf, - size_t request_buf_len, - const char *server_banner) -{ - ARG_UNUSED(ctx); - ARG_UNUSED(urls); - ARG_UNUSED(server_addr); - ARG_UNUSED(request_buf); - ARG_UNUSED(request_buf_len); - ARG_UNUSED(server_banner); - - return -ENOTSUP; -} - -#if defined(CONFIG_HTTPS) && defined(CONFIG_NET_APP_SERVER) -static inline int http_server_set_tls(struct http_ctx *ctx, - const char *server_banner, - u8_t *personalization_data, - size_t personalization_data_len, - net_app_cert_cb_t cert_cb, - net_app_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *stack, - size_t stack_len) -{ - ARG_UNUSED(ctx); - ARG_UNUSED(server_banner); - ARG_UNUSED(personalization_data); - ARG_UNUSED(personalization_data_len); - ARG_UNUSED(cert_cb); - ARG_UNUSED(entropy_src_cb); - ARG_UNUSED(pool); - ARG_UNUSED(stack); - ARG_UNUSED(stack_len); - - return -ENOTSUP; -} -#endif /* CONFIG_HTTPS && CONFIG_NET_APP_SERVER */ - -static inline int http_server_enable(struct http_ctx *ctx) -{ - ARG_UNUSED(ctx); - return -ENOTSUP; -} - -static inline int http_server_disable(struct http_ctx *ctx) -{ - ARG_UNUSED(ctx); - return -ENOTSUP; -} - -static inline -struct http_root_url *http_server_add_url(struct http_server_urls *urls, - const char *url, u8_t flags) -{ - ARG_UNUSED(urls); - ARG_UNUSED(url); - ARG_UNUSED(flags); - - return NULL; -} -#endif /* CONFIG_HTTP_SERVER */ - -#if defined(CONFIG_HTTP_CLIENT) -/** - * @brief Generic function to send a HTTP request to the network. Normally - * applications would not need to use this function. - * - * @param ctx HTTP context. - * @param req HTTP request to perform. - * @param timeout Timeout when doing net_buf allocations. - * @param user_data User data related to this request. This is passed - * to send callback if user has specified that. - * - * @return Return 0 if ok, and <0 if error. - */ -int http_request(struct http_ctx *ctx, - struct http_request *req, - s32_t timeout, - void *user_data); - -/** - * @brief Cancel a pending request. - * - * @param ctx HTTP context. - * - * @return Return 0 if ok, and <0 if error. - */ -int http_request_cancel(struct http_ctx *ctx); - -/** - * @brief Send a HTTP request to peer. - * - * @param http_ctx HTTP context. - * @param req HTTP request to perform. - * @param cb Callback to call when the response has been received from peer. - * @param response_buf Caller-supplied buffer where the HTTP response will be - * stored - * @param response_buf_len Length of the caller-supplied buffer. - * @param user_data A valid pointer on some user data or NULL - * @param timeout Amount of time to wait for a reply. If the timeout is 0, - * then we return immediately and the callback (if set) will be called later. - * - * @return Return 0 if ok, and <0 if error. - */ -int http_client_send_req(struct http_ctx *http_ctx, - struct http_request *req, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout); - -/** - * @brief Send a HTTP GET request to peer. - * - * @param http_ctx HTTP context. - * @param url URL to use. - * @param host Host field in HTTP header. If set to NULL, then server - * name is used. - * @param extra_header_fields Any extra header fields that caller wants - * to add. This can be set to NULL. The format is "name: value\\r\\n" - * Example: "Accept: text/plain\\r\\nConnection: Close\\r\\n" - * @param cb Callback to call when the response has been received from peer. - * @param response_buf Caller-supplied buffer where the HTTP request will be - * stored - * @param response_buf_len Length of the caller-supplied buffer. - * @param user_data A valid pointer on some user data or NULL - * @param timeout Amount of time to wait for a reply. If the timeout is 0, - * then we return immediately and the callback (if set) will be called later. - * - * @return Return 0 if ok, and <0 if error. - */ -static inline int http_client_send_get_req(struct http_ctx *http_ctx, - const char *url, - const char *host, - const char *extra_header_fields, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout) -{ - struct http_request req = { - .method = HTTP_GET, - .url = url, - .host = host, - .protocol = " " HTTP_PROTOCOL, - .header_fields = extra_header_fields, - }; - - return http_client_send_req(http_ctx, &req, cb, response_buf, - response_buf_len, user_data, timeout); -} - -/** - * @brief Initialize user-supplied HTTP context. - * - * @details Caller can set the various fields in http_ctx after this call - * if needed. - * - * @param http_ctx HTTP context. - * @param server HTTP server address or host name. If host name is given, - * then DNS resolver support (CONFIG_DNS_RESOLVER) must be enabled. If caller - * sets the server parameter as NULL, then the server_addr parameter must be - * set by the user and it is used when connecting to the server. - * @param server_port HTTP server TCP port. If server parameter is NULL, then - * this value is ignored. - * @param server_addr HTTP server IP address and port. This is only used if - * server parameter is not set. - * @param timeout If server address needs to be resolved, then this value is - * used as a timeout. - * - * @return Return 0 if ok, <0 if error. - */ -int http_client_init(struct http_ctx *http_ctx, - const char *server, - u16_t server_port, - struct sockaddr *server_addr, - s32_t timeout); - -#if defined(CONFIG_HTTPS) -/** - * @brief Initialize user-supplied HTTP context when using HTTPS. - * - * @details Caller can set the various fields in http_ctx after this call - * if needed. - * - * @param ctx HTTPS context. - * @param request_buf Caller-supplied buffer where the TLS request will be - * stored - * @param request_buf_len Length of the caller-supplied buffer. - * @param personalization_data Personalization data (Device specific - * identifiers) for random number generator. (Can be NULL). - * @param personalization_data_len Length of the personalization data. - * @param cert_cb User-supplied callback that setups the certificates. - * @param cert_host Hostname that is used to verify the server certificate. - * This value is used when HTTP client API calls mbedtls_ssl_set_hostname() - * which sets the hostname to check against the received server certificate. - * See https://tls.mbed.org/kb/how-to/use-sni for more details. - * This can be left NULL in which case mbedtls will silently skip certificate - * verification entirely. This option is only used if MBEDTLS_X509_CRT_PARSE_C - * is enabled in mbedtls config file. - * @param entropy_src_cb User-supplied callback that setup the entropy. This - * can be set to NULL, in which case default entropy source is used. - * @param pool Memory pool for RX data reads. - * @param https_stack HTTPS thread stack. - * @param https_stack_len HTTP thread stack size. - * - * @return Return 0 if ok, <0 if error. - */ -int http_client_set_tls(struct http_ctx *ctx, - u8_t *request_buf, - size_t request_buf_len, - u8_t *personalization_data, - size_t personalization_data_len, - net_app_ca_cert_cb_t cert_cb, - const char *cert_host, - net_app_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *https_stack, - size_t https_stack_size); -#endif /* CONFIG_HTTPS */ -#endif /* CONFIG_HTTP_CLIENT */ - -/** - * @brief Close a network connection to peer. - * - * @param ctx Http context. - * - * @return 0 if ok, <0 if error. - */ -int http_close(struct http_ctx *ctx); - -/** - * @brief Release this http context. - * - * @details No network data will be received via this context after this - * call. - * - * @param ctx Http context. - * - * @return 0 if ok, <0 if error. - */ -int http_release(struct http_ctx *ctx); - -/** - * @brief Set various callbacks that are called at various stage of ws session. - * - * @param ctx Http context. - * @param connect_cb Connect callback. - * @param recv_cb Data receive callback. - * @param send_cb Data sent callback. - * @param close_cb Close callback. - * - * @return 0 if ok, <0 if error. - */ -int http_set_cb(struct http_ctx *ctx, - http_connect_cb_t connect_cb, - http_recv_cb_t recv_cb, - http_send_cb_t send_cb, - http_close_cb_t close_cb); - -/** - * @brief Send a message to peer. The data can be either HTTP or websocket - * data. - * - * @details This does not modify the network packet but sends it as is. - * - * @param ctx Http context. - * @param pkt Network packet to send - * @param user_send_data User specific data to this connection. This is passed - * as a parameter to sent cb after the packet has been sent. - * - * @return 0 if ok, <0 if error. - */ -int http_send_msg_raw(struct http_ctx *ctx, struct net_pkt *pkt, - void *user_send_data); - -/** - * @brief Prepare HTTP data to be sent to peer. If there is enough data, the - * function will then send it too. - * - * @param ctx Http context. - * @param payload Application data to send - * @param payload_len Length of application data - * @param user_send_data User specific data to this connection. This is passed - * as a parameter to sent cb after the packet has been sent. - * - * @return 0 if ok, <0 if error. - */ -int http_prepare_and_send(struct http_ctx *ctx, const char *payload, - size_t payload_len, void *user_send_data); - -/** - * @brief Send HTTP data chunk to peer. - * - * @param ctx Http context. - * @param payload Application data to send - * @param payload_len Length of application data - * @param user_send_data User specific data to this connection. This is passed - * as a parameter to sent cb after the packet has been sent. - * - * @return 0 if ok, <0 if error. - */ -int http_send_chunk(struct http_ctx *ctx, const char *payload, - size_t payload_len, void *user_send_data); - -/** - * @brief Send any pending data immediately. - * - * @param ctx Http context. - * @param user_send_data Optional user_data for that is used as a parameter in - * send callback if that is specified. - * - * @return 0 if ok, <0 if error. - */ -int http_send_flush(struct http_ctx *ctx, void *user_send_data); - -/** - * @brief Send HTTP error message to peer. - * - * @param ctx Http context. - * @param code HTTP error code - * @param description HTTP error description - * @param html_payload Extra payload, can be null - * @param html_len Payload length - * - * @return 0 if ok, <0 if error. - */ -int http_send_error(struct http_ctx *ctx, int code, const char *description, - u8_t *html_payload, size_t html_len); - -/** - * @brief Add HTTP header field to the message. - * - * @details This can be called multiple times to add pieces of HTTP header - * fields into the message. Caller must put the "\\r\\n" characters to the - * input http_header_field variable. - * - * @param ctx Http context. - * @param http_header_field All or part of HTTP header to be added. - * @param user_send_data User data value that is used in send callback. - * Note that this value is only used if this function call causes a HTTP - * message to be sent. - * - * @return <0 if error, other value tells how many bytes were added - */ -int http_add_header(struct http_ctx *ctx, const char *http_header_field, - void *user_send_data); - -/** - * @brief Add HTTP header field to the message. - * - * @details This can be called multiple times to add pieces of HTTP header - * fields into the message. If name is "Foo" and value is "bar", then this - * function will add "Foo: bar\\r\\n" to message. - * - * @param ctx Http context. - * @param name Name of the header field - * @param value Value of the header field - * @param user_send_data User data value that is used in send callback. - * Note that this value is only used if this function call causes a HTTP - * message to be sent. - * - * @return <0 if error, other value tells how many bytes were added - */ -int http_add_header_field(struct http_ctx *ctx, const char *name, - const char *value, void *user_send_data); - -/** - * @brief Find a handler function for a given URL. - * - * @details This is internal function, do not call this from application. - * - * @param ctx Http context. - * @param flags Tells if the URL is either HTTP or websocket URL - * - * @return URL handler or NULL if no such handler was found. - */ -struct http_root_url *http_url_find(struct http_ctx *ctx, - enum http_url_flags flags); - -#define http_change_state(ctx, new_state) \ - _http_change_state(ctx, new_state, __func__, __LINE__) - -/** - * @brief Change the state of the HTTP engine - * - * @details This is internal function, do not call this from application. - * - * @param ctx HTTP context. - * @param new_state New state of the context. - * @param func Function that changed the state (for debugging) - * @param line Line number of the function (for debugging) - */ -void _http_change_state(struct http_ctx *ctx, - enum http_state new_state, - const char *func, int line); - -#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_NET_DEBUG_HTTP_CONN) -/** - * @typedef http_server_cb_t - * @brief HTTP connection monitoring callback. - * - * @details This callback is called by server if new HTTP connection is - * established or disconnected. Requires that HTTP connection monitoring - * is enabled. - * - * @param ctx The HTTP context to use. - * @param user_data User specified data. - */ -typedef void (*http_server_cb_t)(struct http_ctx *entry, - void *user_data); - -/** - * @brief Go through all the HTTP connections in the server and call - * user provided callback for each connection. - * - * @param cb Callback to call for each HTTP connection. - * @param user_data User provided data that is passed to callback. - */ -void http_server_conn_foreach(http_server_cb_t cb, void *user_data); - -/** - * @brief Register a callback that is called if HTTP connection is - * established or disconnected. - * - * @details This is normally called only by "net http monitor" shell command. - * - * @param cb Callback to call for each HTTP connection created or - * deleted. - * @param user_data User provided data that is passed to callback. - */ -void http_server_conn_monitor(http_server_cb_t cb, void *user_data); - -/** - * @brief HTTP connection was established. - * - * @details This is internal function, do not call this from application. - * - * @param ctx HTTP context. - */ -void http_server_conn_add(struct http_ctx *ctx); - -/** - * @brief HTTP connection was disconnected. - * - * @details This is internal function, do not call this from application. - * - * @param ctx HTTP context. - */ -void http_server_conn_del(struct http_ctx *ctx); -#else -#define http_server_conn_foreach(...) -#define http_server_conn_monitor(...) -#define http_server_conn_add(...) -#define http_server_conn_del(...) -#endif /* CONFIG_HTTP_SERVER && CONFIG_NET_DEBUG_HTTP_CONN */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __HTTP_APP_H__ */ diff --git a/include/net/http_legacy.h b/include/net/http_legacy.h deleted file mode 100644 index 0dc0c2889be..00000000000 --- a/include/net/http_legacy.h +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef __HTTP_LEGACY_H__ -#define __HTTP_LEGACY_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief HTTP client and server library - * @defgroup http_legacy HTTP Library - * @ingroup networking - * @deprecated This library is deprecated. - * @{ - */ - -#if defined(CONFIG_HTTPS) -#if defined(CONFIG_MBEDTLS) -#if !defined(CONFIG_MBEDTLS_CFG_FILE) -#include "mbedtls/config.h" -#else -#include CONFIG_MBEDTLS_CFG_FILE -#endif /* CONFIG_MBEDTLS_CFG_FILE */ - -#if defined(MBEDTLS_PLATFORM_C) -#include "mbedtls/platform.h" -#else -#include -#define mbedtls_time_t time_t -#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS -#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE -#endif /* MBEDTLS_PLATFORM_C */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif /* CONFIG_MBEDTLS */ -#endif /* CONFIG_HTTPS */ - -#define HTTP_CRLF "\r\n" - -#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) -void http_heap_init(void); -#else -#define http_heap_init() -#endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */ - -typedef int (*http_send_data_t)(struct net_pkt *pkt, - net_context_send_cb_t cb, - s32_t timeout, - void *token, - void *user_data); - -#if defined(CONFIG_HTTPS) -/* Internal information for managing HTTPS data */ -struct https_context { - struct net_pkt *rx_pkt; - struct net_buf *frag; - struct k_sem tx_sem; - struct k_fifo rx_fifo; - struct k_fifo tx_fifo; - int remaining; -}; - -/** - * @typedef https_entropy_src_cb_t - * @brief Callback used when the API user needs to setup the entropy source. - * @detail This is the same as mbedtls_entropy_f_source_ptr callback. - * - * @param data Callback-specific data pointer - * @param output Data to fill - * @param len Maximum size to provide - * @param olen The actual amount of bytes put into the buffer (Can be 0) - * - * @deprecated This typedef is deprecated. - */ -typedef int (*https_entropy_src_cb_t)(void *data, unsigned char *output, - size_t len, size_t *olen); -#endif /* CONFIG_HTTPS */ - -#if defined(CONFIG_HTTP_CLIENT) - -#include -#include - -/* Is there more data to come */ -enum http_final_call { - HTTP_DATA_MORE = 0, - HTTP_DATA_FINAL = 1, -}; - -#ifndef HTTP_PROTOCOL -#define HTTP_PROTOCOL "HTTP/1.1" -#endif - -/* Some generic configuration options, these can be overridden if needed. */ -#ifndef HTTP_STATUS_STR_SIZE -#define HTTP_STATUS_STR_SIZE 32 -#endif - -/* It seems enough to hold 'Content-Length' and its value */ -#define HTTP_CONTENT_LEN_SIZE 48 - -/* Default HTTP Header Field values for HTTP Requests if using the - * HTTP_HEADER_FIELDS define. - */ -#ifndef HTTP_ACCEPT -#define HTTP_ACCEPT "text/plain" -#endif - -#ifndef HTTP_ACCEPT_ENC -#define HTTP_ACCEPT_ENC "identity" -#endif - -#ifndef HTTP_ACCEPT_LANG -#define HTTP_ACCEPT_LANG "en-US" -#endif - -#ifndef HTTP_CONNECTION -#define HTTP_CONNECTION "Close" -#endif - -#ifndef HTTP_USER_AGENT -#define HTTP_USER_AGENT "Zephyr-HTTP-Client/1.8" -#endif - -/* This can be used in http_client_send_get_req() when supplying - * extra_header_fields parameter. - */ -#ifndef HTTP_HEADER_FIELDS -#define HTTP_HEADER_FIELDS \ - "Accept: " HTTP_ACCEPT HTTP_CRLF \ - "Accept-Encoding: " HTTP_ACCEPT_ENC HTTP_CRLF \ - "Accept-Language: " HTTP_ACCEPT_LANG HTTP_CRLF \ - "User-Agent: " HTTP_USER_AGENT HTTP_CRLF \ - "Connection: " HTTP_CONNECTION HTTP_CRLF -#endif - -struct http_client_ctx; - -/** - * @typedef http_receive_cb_t - * @brief Callback used when TCP data has been received from peer. - * - * @param ctx HTTP context. - * @param pkt Network packet. - * - * @deprecated This typedef is deprecated. - */ -typedef void (*http_receive_cb_t)(struct http_client_ctx *ctx, - struct net_pkt *pkt); - -/** - * @typedef http_response_cb_t - * @brief Callback used when a response has been received from peer. - * - * @param ctx HTTP context. - * @param data Received data buffer - * @param buflen Data buffer len (as specified by user) - * @param datalen Received data len, if this is larger than buflen, - * then some data was skipped. - * @param final_data Does this data buffer contain all the data or - * is there still more data to come. - * @param user_data A valid pointer on some user data or NULL - * - * @deprecated This typedef is deprecated. - */ -typedef void (*http_response_cb_t)(struct http_client_ctx *ctx, - u8_t *data, size_t buflen, - size_t datalen, - enum http_final_call final_data, - void *user_data); - -#if defined(CONFIG_HTTPS) -/** - * @typedef https_ca_cert_cb_t - * @brief Callback used when the API user needs to setup the - * HTTPS certs. - * - * @param ctx HTTPS client context. - * @param ca_cert MBEDTLS certificate. This is of type mbedtls_x509_crt - * if MBEDTLS_X509_CRT_PARSE_C is defined. - * - * @return 0 if ok, <0 if there is an error - * - * @deprecated This typedef is deprecated. - */ -typedef int (*https_ca_cert_cb_t)(struct http_client_ctx *ctx, - void *ca_cert); -#endif /* CONFIG_HTTPS */ - -/** - * HTTP client context information. This contains all the data that is - * needed when doing HTTP requests. - * - * @deprecated This struct is deprecated. - */ -struct http_client_ctx { - struct http_parser parser; - struct http_parser_settings settings; - - /** Server name */ - const char *server; - -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) - /** Network packet (net_pkt) memory pool for network contexts attached - * to this http_client context. - */ - net_pkt_get_slab_func_t tx_slab; - - /** Network data net_buf pool for network contexts attached to this - * http_client context. - */ - net_pkt_get_pool_func_t data_pool; -#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ - - /** Is this instance HTTPS or not. - */ - bool is_https; - -#if defined(CONFIG_DNS_RESOLVER) - /** Remember the DNS query id so that it can be cancelled - * if the HTTP context is released and the query is active - * at that time. - */ - u16_t dns_id; -#endif - - struct { - /** Local socket address */ - struct sockaddr local; - - /** Remote (server) socket address */ - struct sockaddr remote; - - /** IP stack network context */ - struct net_context *ctx; - - /** Network timeout */ - s32_t timeout; - - /** User can define this callback if it wants to have - * special handling of the received raw data. Note that this - * callback is not called for HTTPS data. - */ - http_receive_cb_t receive_cb; - - /** Internal function that is called when HTTP data is - * received from network. - */ - net_context_recv_cb_t recv_cb; - - /** Internal function that is called when HTTP data is sent to - * network. - */ - http_send_data_t send_data; - } tcp; - - /** HTTP request information */ - struct { - /** - * Semaphore to signal HTTP request completion - */ - struct k_sem wait; - - /** Hostname to be used in the request */ - const char *host; - - /** User provided data */ - void *user_data; - - /** What method we used here (GET, POST, HEAD etc.) - */ - enum http_method method; - } req; - - /** HTTP response information */ - struct { - /** 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 response is stored, this is to be provided - * by the user. - */ - u8_t *response_buf; - - /** Where the body starts. - */ - u8_t *body_start; - - /** Response buffer maximum length */ - size_t response_buf_len; - - /** Length of the data in the result buf. If the value is - * larger than response_buf_len, then it means that the data - * is truncated and could not be fully copied into - * response_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]; - - u8_t cl_present:1; - u8_t body_found:1; - u8_t message_complete:1; - } rsp; - -#if defined(CONFIG_HTTPS) - struct { - /** HTTPS stack for mbedtls library. */ - k_thread_stack_t stack; - - /** HTTPS stack size. */ - int stack_size; - - /** HTTPS thread id */ - k_tid_t tid; - - /** HTTPS thread */ - struct k_thread thread; - - /** Memory pool for RX data */ - struct k_mem_pool *pool; - - /** Hostname to be used in the certificate verification */ - const char *cert_host; - - /** mbedtls related configuration. */ - struct { - struct https_context ssl_ctx; - https_ca_cert_cb_t cert_cb; - https_entropy_src_cb_t entropy_src_cb; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; - mbedtls_x509_crt ca_cert; - u8_t *personalization_data; - size_t personalization_data_len; - } mbedtls; - } https; -#endif /* CONFIG_HTTPS */ -}; - -/** - * HTTP client request. This contains all the data that is needed when doing - * a HTTP request. - * - * @deprecated This struct is deprecated. - */ -struct http_client_request { - /** The HTTP method: GET, HEAD, OPTIONS, POST, ... */ - enum http_method method; - - /** The URL for this request, for example: /index.html */ - const char *url; - - /** The HTTP protocol: 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. - */ - 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; - - /** Payload, may be NULL */ - const char *payload; - - /** Payload size, may be 0 */ - u16_t payload_size; -}; - -/** - * @brief Generic function to send a HTTP request to the network. Normally - * applications would not need to use this function. - * - * @param ctx HTTP client context. - * @param req HTTP request to perform. - * @param timeout Timeout when doing net_buf allocations. - * - * @return Return 0 if ok, and <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_request(struct http_client_ctx *ctx, - struct http_client_request *req, - s32_t timeout); - -/** - * @brief Send a HTTP request to peer. - * - * @param http_ctx HTTP context. - * @param req HTTP request to perform. - * @param cb Callback to call when the response has been received from peer. - * @param response_buf Caller-supplied buffer where the HTTP response will be - * stored - * @param response_buf_len Length of the caller-supplied buffer. - * @param user_data A valid pointer on some user data or NULL - * @param timeout Amount of time to wait for a reply. If the timeout is 0, - * then we return immediately and the callback (if set) will be called later. - * - * @return Return 0 if ok, and <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_client_send_req(struct http_client_ctx *http_ctx, - struct http_client_request *req, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout); - -/** - * @brief Send a HTTP GET request to peer. - * - * @param http_ctx HTTP context. - * @param url URL to use. - * @param host Host field in HTTP header. If set to NULL, then server - * name is used. - * @param extra_header_fields Any extra header fields that caller wants - * to add. This can be set to NULL. The format is "name: value\r\n" - * Example: "Accept: text/plain\r\nConnection: Close\r\n" - * @param cb Callback to call when the response has been received from peer. - * @param response_buf Caller-supplied buffer where the HTTP request will be - * stored - * @param response_buf_len Length of the caller-supplied buffer. - * @param user_data A valid pointer on some user data or NULL - * @param timeout Amount of time to wait for a reply. If the timeout is 0, - * then we return immediately and the callback (if set) will be called later. - * - * @return Return 0 if ok, and <0 if error. - * - * @deprecated This api is deprecated. - */ -static inline -int __deprecated http_client_send_get_req(struct http_client_ctx *http_ctx, - const char *url, - const char *host, - const char *extra_header_fields, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout) -{ - struct http_client_request req = { - .method = HTTP_GET, - .url = url, - .host = host, - .protocol = " " HTTP_PROTOCOL HTTP_CRLF, - .header_fields = extra_header_fields, - }; - - return http_client_send_req(http_ctx, &req, cb, response_buf, - response_buf_len, user_data, timeout); -} - -/** - * @brief Send a HTTP POST request to peer. - * - * @param http_ctx HTTP context. - * @param url URL to use. - * @param host Host field in HTTP header. If set to NULL, then server - * name is used. - * @param extra_header_fields Any extra header fields that caller wants - * to add. This can be set to NULL. The format is "name: value\r\n" - * Example: "Accept: text/plain\r\nConnection: Close\r\n" - * @param content_type Content type of the data. - * @param payload Payload data. - * @param cb Callback to call when the response has been received from peer. - * @param response_buf Caller-supplied buffer where the HTTP response will be - * stored - * @param response_buf_len Length of the caller-supplied buffer. - * @param user_data A valid pointer on some user data or NULL - * @param timeout Amount of time to wait for a reply. If the timeout is 0, - * then we return immediately and the callback (if set) will be called later. - * - * @return Return 0 if ok, and <0 if error. - * - * @deprecated This api is deprecated. - */ -static inline -int __deprecated http_client_send_post_req(struct http_client_ctx *http_ctx, - const char *url, - const char *host, - const char *extra_header_fields, - const char *content_type, - const char *payload, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout) -{ - struct http_client_request req = { - .method = HTTP_POST, - .url = url, - .host = host, - .protocol = " " HTTP_PROTOCOL HTTP_CRLF, - .header_fields = extra_header_fields, - .content_type_value = content_type, - .payload = payload, - }; - - return http_client_send_req(http_ctx, &req, cb, response_buf, - response_buf_len, user_data, timeout); -} - -/** - * @brief Initialize user-supplied HTTP context. - * - * @detail Caller can set the various fields in http_ctx after this call - * if needed. - * - * @param http_ctx HTTP context. - * @param server HTTP server address or host name. If host name is given, - * then DNS resolver support (CONFIG_DNS_RESOLVER) must be enabled. If caller - * sets the server parameter as NULL, then no attempt is done to figure out - * the remote address and caller must set the address in http_ctx.tcp.remote - * itself. - * @param server_port HTTP server TCP port. - * - * @return Return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_client_init(struct http_client_ctx *http_ctx, - const char *server, u16_t server_port); - -#if defined(CONFIG_HTTPS) -/** - * @brief Initialize user-supplied HTTP context when using HTTPS. - * - * @detail Caller can set the various fields in http_ctx after this call - * if needed. - * - * @param http_ctx HTTPS context. - * @param server HTTPS server address or host name. If host name is given, - * then DNS resolver support (CONFIG_DNS_RESOLVER) must be enabled. If caller - * sets the server parameter as NULL, then no attempt is done to figure out - * the remote address and caller must set the address in http_ctx.tcp.remote - * itself. - * @param server_port HTTPS server TCP port. - * @param personalization_data Personalization data (Device specific - * identifiers) for random number generator. (Can be NULL). - * @param personalization_data_len Length of the personalization data. - * @param cert_cb User-supplied callback that setups the certificates. - * @param cert_host Hostname that is used to verify the server certificate. - * This value is used when HTTP client API calls mbedtls_ssl_set_hostname() - * which sets the hostname to check against the received server certificate. - * See https://tls.mbed.org/kb/how-to/use-sni for more details. - * This can be left NULL in which case mbedtls will silently skip certificate - * verification entirely. This option is only used if MBEDTLS_X509_CRT_PARSE_C - * is enabled in mbedtls config file. - * @param entropy_src_cb User-supplied callback that setup the entropy. This - * can be set to NULL, in which case default entropy source is used. - * @param pool Memory pool for RX data reads. - * @param https_stack HTTPS thread stack. - * @param https_stack_len HTTP thread stack size. - * - * @return Return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated https_client_init(struct http_client_ctx *http_ctx, - const char *server, u16_t server_port, - u8_t *personalization_data, - size_t personalization_data_len, - https_ca_cert_cb_t cert_cb, - const char *cert_host, - https_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t https_stack, - size_t https_stack_size); -#endif /* CONFIG_HTTPS */ - -/** - * @brief Release all the resources allocated for HTTP context. - * - * @param http_ctx HTTP context. - * - * @deprecated This api is deprecated. - */ -void __deprecated http_client_release(struct http_client_ctx *http_ctx); - -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) -/** - * @brief Configure the net_pkt pool for this context. - * - * @details Use of this function is optional and if the pools are not set, - * then the default TX and DATA pools are used. This needs to be called before - * http init function, as that will setup net_context which needs the net_pkt - * pool information. - * - * @param ctx HTTP client context - * @param tx_slab Function which is used when allocating TX network packet. - * This can be NULL in which case default TX memory pool is used. - * @param data_pool Function which is used when allocating data network buffer. - * This can be NULL in which case default DATA net_buf pool is used. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_client_set_net_pkt_pool(struct http_client_ctx *ctx, - net_pkt_get_slab_func_t tx_slab, - net_pkt_get_pool_func_t data_pool); -#else -#define http_client_set_net_pkt_pool(...) -#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ -#endif /* CONFIG_HTTP_CLIENT */ - -#if defined(CONFIG_HTTP_SERVER) - -#include -#include - -struct http_server_ctx; - -/** - * @deprecated This enum is deprecated. - */ -enum http_url_flags { - HTTP_URL_STANDARD = 0, -}; - -/* HTTP header fields struct */ -/** - * @deprecated This struct is deprecated. - */ -struct http_field_value { - /** Field name, this variable will point to the beginning of the string - * containing the HTTP field name - */ - const char *key; - - /** Value, this variable will point to the beginning of the string - * containing the field value - */ - const char *value; - - /** Length of the field name */ - u16_t key_len; - - /** Length of the field value */ - u16_t value_len; -}; - -typedef int (*http_url_cb_t)(struct http_server_ctx *ctx); - -/* HTTP root URL struct, used for pattern matching */ -/** - * @deprecated This struct is deprecated. - */ -struct http_root_url { - /** URL */ - const char *root; - - /** Callback that is called when this URL is received */ - http_url_cb_t write_cb; - - /** Length of the URL */ - u16_t root_len; - - /** Flags for this URL (values are from enum http_url_flags) */ - u8_t flags; - - /** Is this URL resource used or not */ - u8_t is_used; -}; - -/* Collection of URLs that this server will handle */ -/** - * @deprecated This struct is deprecated. - */ -struct http_server_urls { - /* First item is the default handler and it is always there. - */ - struct http_root_url default_url; - struct http_root_url urls[CONFIG_HTTP_SERVER_NUM_URLS]; -}; - -#if defined(CONFIG_HTTPS) -/** - * @typedef https_server_cert_cb_t - * @brief Callback used when the API user needs to setup the - * HTTPS certs. - * - * @param ctx HTTPS server context. - * @param cert MBEDTLS certificate - * @param pkey MBEDTLS private key - * - * @return 0 if ok, <0 if there is an error - * - * @deprecated This typedef is deprecated. - */ -typedef int (*https_server_cert_cb_t)(struct http_server_ctx *ctx, - mbedtls_x509_crt *cert, - mbedtls_pk_context *pkey); -#endif /* CONFIG_HTTPS */ - -/* The HTTP server context struct */ -/** - * @deprecated This struct is deprecated. - */ -struct http_server_ctx { - /** Collection of URLs that this server context will handle */ - struct http_server_urls *urls; - -#if defined(CONFIG_NET_IPV4) - /** IPv4 stack network context for listening */ - struct net_context *net_ipv4_ctx; -#endif -#if defined(CONFIG_NET_IPV6) - /** IPv6 stack network context for listening */ - struct net_context *net_ipv6_ctx; -#endif - - /** Function that is called when data is received from network. */ - net_context_recv_cb_t recv_cb; - - /** Function that is called when data is sent to network. */ - http_send_data_t send_data; - -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) - /** Network packet (net_pkt) memory pool for network contexts attached - * to this http server context. - */ - net_pkt_get_slab_func_t tx_slab; - - /** Network data net_buf pool for network contexts attached to this - * http server context. - */ - net_pkt_get_pool_func_t data_pool; -#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ - -#if defined(CONFIG_NET_DEBUG_HTTP_CONN) - sys_snode_t node; -#endif - - /** Network timeout */ - s32_t timeout; - - /** Running status of the server. If true, then the server is enabled. - * If false then it is disabled and will not serve clients. - * The server is disabled by default after initialization and will need - * to be enabled manually. - */ - bool enabled; - - /** Is this instance HTTPS or not. - */ - bool is_https; - - struct { - /** From which net_context the request came from */ - struct net_context *net_ctx; - - /** HTTP request timer. After sending a response to the - * client, it is possible to wait for any request back via - * the same socket. If no response is received, then this - * timeout is activated and connection is tore down. - */ - struct k_delayed_work timer; - - /** HTTP parser */ - struct http_parser parser; - - /** HTTP parser settings */ - struct http_parser_settings settings; - - /** Collection of header fields */ - struct http_field_value - field_values[CONFIG_HTTP_HEADER_FIELD_ITEMS]; - - /** HTTP Request URL */ - const char *url; - - /** Where the request is stored, this is to be provided - * by the user. - */ - u8_t *request_buf; - - /** Request buffer maximum length */ - size_t request_buf_len; - - /** Length of the data in the request buf. */ - size_t data_len; - - /** Number of header field elements */ - u16_t field_values_ctr; - - /** URL's length */ - u16_t url_len; - } req; - -#if defined(CONFIG_HTTPS) - struct { - /** HTTPS stack for mbedtls library. */ - k_thread_stack_t stack; - - /** HTTPS stack size. */ - int stack_size; - - /** HTTPS thread id */ - k_tid_t tid; - - /** HTTPS thread */ - struct k_thread thread; - - /** Memory pool for RX data */ - struct k_mem_pool *pool; - - /** mbedtls related configuration. */ - struct { - struct https_context ssl_ctx; - https_server_cert_cb_t cert_cb; - https_entropy_src_cb_t entropy_src_cb; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; - mbedtls_x509_crt srvcert; - mbedtls_pk_context pkey; - u8_t *personalization_data; - size_t personalization_data_len; - } mbedtls; - } https; -#endif /* CONFIG_HTTPS */ -}; - -/** - * @brief Initialize user-supplied HTTP context. - * - * @detail Caller can set the various callback fields in http_ctx and - * http_ctx.req.parser after this call if needed. - * - * @param http_ctx HTTP context. - * @param urls Array of URLs that the server instance will serve. If the - * server receives a HTTP request into one of the URLs, it will call user - * supplied callback. If no such URL is registered, a default handler will - * be called (if set by the user). If no data handler is found, the request - * is dropped. - * @param server_addr Socket address of the local network interface and TCP - * port where the data is being waited. If the socket family is set to - * AF_UNSPEC, then both IPv4 and IPv6 is started to be listened. If the - * address is set to be INADDR_ANY (for IPv4) or unspecified address (all bits - * zeros for IPv6), then the HTTP server will select proper IP address to bind - * to. If caller has not specified HTTP listening port, then port 80 is being - * listened. The parameter can be left NULL in which case a listener to port 80 - * using IPv4 and IPv6 is created. Note that if IPv4 or IPv6 is disabled, then - * the corresponding disabled service listener is not created. - * @param request_buf Caller-supplied buffer where the HTTP request will be - * stored - * @param request_buf_len Length of the caller-supplied buffer. - * @param server_banner Print information about started service. This is only - * printed if HTTP debugging is activated. The parameter can be set to NULL if - * no extra prints are needed. - * - * @return Return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_server_init(struct http_server_ctx *http_ctx, - struct http_server_urls *urls, - struct sockaddr *server_addr, - u8_t *request_buf, - size_t request_buf_len, - const char *server_banner); - -#if defined(CONFIG_HTTPS) -/** - * @brief Initialize user-supplied HTTP context. This function must be - * used if HTTPS server is created. - * - * @detail Caller can set the various callback fields in http_ctx and - * http_ctx.req.parser after this call if needed. - * - * @param http_ctx HTTP context. - * @param urls Array of URLs that the server instance will serve. If the - * server receives a HTTP request into one of the URLs, it will call user - * supplied callback. If no such URL is registered, a default handler will - * be called (if set by the user). If no data handler is found, the request - * is dropped. - * @param server_addr Socket address of the local network interface and TCP - * port where the data is being waited. If the socket family is set to - * AF_UNSPEC, then both IPv4 and IPv6 is started to be listened. If the - * address is set to be INADDR_ANY (for IPv4) or unspecified address (all bits - * zeros for IPv6), then the HTTP server will select proper IP address to bind - * to. If caller has not specified HTTP listening port, then port 80 is being - * listened. The parameter can be left NULL in which case a listener to port 80 - * using IPv4 and IPv6 is created. Note that if IPv4 or IPv6 is disabled, then - * the corresponding disabled service listener is not created. - * @param request_buf Caller-supplied buffer where the HTTP request will be - * stored - * @param request_buf_len Length of the caller-supplied buffer. - * @param server_banner Print information about started service. This is only - * printed if HTTP debugging is activated. The parameter can be set to NULL if - * no extra prints are needed. - * @param personalization_data Personalization data (Device specific - * identifiers) for random number generator. (Can be NULL). - * @param personalization_data_len Length of the personalization data. - * @param cert_cb User-supplied callback that setups the certificates. - * @param entropy_src_cb User-supplied callback that setup the entropy. This - * can be set to NULL, in which case default entropy source is used. - * @param pool Memory pool for RX data reads. - * @param https_stack HTTPS thread stack. - * @param https_stack_len HTTP thread stack size. - * - * @return Return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated https_server_init(struct http_server_ctx *http_ctx, - struct http_server_urls *urls, - struct sockaddr *server_addr, - u8_t *request_buf, - size_t request_buf_len, - const char *server_banner, - u8_t *personalization_data, - size_t personalization_data_len, - https_server_cert_cb_t cert_cb, - https_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t https_stack, - size_t https_stack_len); -#endif /* CONFIG_HTTPS */ - -/** - * @brief Release all the resources allocated for HTTP context. - * - * @param http_ctx HTTP context. - * - * @deprecated This api is deprecated. - */ -void __deprecated http_server_release(struct http_server_ctx *http_ctx); - -/** - * @brief Enable HTTP server that is related to this context. - * - * @detail The HTTP server will start to serve request after this. - * - * @param http_ctx HTTP context. - * - * @return Previous status of the server. - * - * @deprecated This api is deprecated. - */ -bool __deprecated http_server_enable(struct http_server_ctx *http_ctx); - -/** - * @brief Disable HTTP server that is related to this context. - * - * @detail The HTTP server will stop to serve request after this. - * - * @param http_ctx HTTP context. - * - * @return Previous status of the server. - * - * @deprecated This api is deprecated. - */ -bool __deprecated http_server_disable(struct http_server_ctx *http_ctx); - -/** - * @brief Helper function that can be used to fill the server (local) sockaddr - * struct. - * - * @param addr Sockaddr struct to be filled. - * @param myaddr Address that the local IP address. If left NULL, then the - * proper system address is used. - * @param port TCP port to use. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_server_set_local_addr(struct sockaddr *addr, - const char *myaddr, - u16_t port); - -/** - * @brief Add a handler for a given URL. - * - * @detail Register a handler which is called if the server receives a - * request to a given URL. - * - * @param urls URL struct that will contain all the URLs the user wants to - * register. - * @param url URL string. - * @param flags Flags for the URL. - * @param write_cb Callback that is called when URL is requested. - * - * @return NULL if the URL is already registered, pointer to URL if - * registering was ok. - * - * @deprecated This api is deprecated. - */ -struct http_root_url * __deprecated http_server_add_url( - struct http_server_urls *urls, - const char *url, u8_t flags, - http_url_cb_t write_cb); - -/** - * @brief Delete the handler for a given URL. - * - * @detail Unregister the previously registered URL handler. - * - * @param urls URL struct that will contain all the URLs the user has - * registered. - * @param url URL string. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_server_del_url(struct http_server_urls *urls, - const char *url); - -/** - * @brief Add default URL handler. - * - * @detail If no URL handler is found, then call this handler. There can - * be only one default handler in the URL struct. - * - * @param urls URL struct that will contain all the URLs the user has - * registered. - * @param write_cb Callback that is called when non-registered URL is - * requested. - * - * @return NULL if default URL is already registered, pointer to default - * URL if registering was ok. - * - * @deprecated This api is deprecated. - */ -struct http_root_url * __deprecated http_server_add_default( - struct http_server_urls *urls, - http_url_cb_t write_cb); - -/** - * @brief Delete the default URL handler. - * - * @detail Unregister the previously registered default URL handler. - * - * @param urls URL struct that will contain all the URLs the user has - * registered. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_server_del_default(struct http_server_urls *urls); - -/** - * @brief Send HTTP response to client. - * - * @detail After sending a response, an optional timeout is started - * which will wait for any new requests from the peer. - * - * @param ctx HTTP context. - * @param http_header HTTP headers to send. - * @param html_payload HTML payload to send. - * @param timeout Timeout to wait until the connection is shutdown. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_response_wait(struct http_server_ctx *ctx, - const char *http_header, - const char *html_payload, s32_t timeout); - -/** - * @brief Send HTTP response to client. - * - * @detail The connection to peer is torn down right after the response - * is sent. - * - * @param ctx HTTP context. - * @param http_header HTTP headers to send. - * @param html_payload HTML payload to send. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_response(struct http_server_ctx *ctx, - const char *http_header, - const char *html_payload); - -/** - * @brief Send HTTP 400 response to client. - * - * @detail HTTP 400 error code indicates a Bad Request - * - * @param ctx HTTP context. - * @param html_payload HTML payload to send. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_response_400(struct http_server_ctx *ctx, - const char *html_payload); - -/** - * @brief Send HTTP 403 response to client. - * - * @detail HTTP 403 error code indicates a Forbidden Request - * - * @param ctx HTTP context. - * @param html_payload HTML payload to send. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_response_403(struct http_server_ctx *ctx, - const char *html_payload); - -/** - * @brief Send HTTP 404 response to client. - * - * @detail HTTP 404 error code indicates a Not Found resource. - * - * @param ctx HTTP context. - * @param html_payload HTML payload to send. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_response_404(struct http_server_ctx *ctx, - const char *html_payload); - -/** - * @brief Send some data to the client. - * - * @detail Send a piece of data to the client. If html_payload is NULL, then - * we close the connection. - * - * @param ctx HTTP context. - * @param http_header HTTP header to be sent. Can be NULL. - * @param html_payload HTML payload to send. - * @param timeout Timeout to wait until the connection is teared down. - * - * @return 0 if ok, <0 if error. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_response_send_data(struct http_server_ctx *ctx, - const char *http_header, - const char *html_payload, - s32_t timeout); - -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) -/** - * @brief Configure the net_pkt pool for this context. - * - * @details Use of this function is optional and if the pools are not set, - * then the default TX and DATA pools are used. This needs to be called before - * http init function, as that will setup net_context which needs the net_pkt - * pool information. - * - * @param ctx HTTP server context - * @param tx_slab Function which is used when allocating TX network packet. - * This can be NULL in which case default TX memory pool is used. - * @param data_pool Function which is used when allocating data network buffer. - * This can be NULL in which case default DATA net_buf pool is used. - * - * @deprecated This api is deprecated. - */ -int __deprecated http_server_set_net_pkt_pool(struct http_server_ctx *ctx, - net_pkt_get_slab_func_t tx_slab, - net_pkt_get_pool_func_t data_pool); -#else -#define http_server_set_net_pkt_pool(...) -#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ - -#endif /* CONFIG_HTTP_SERVER */ - -#if defined(CONFIG_NET_DEBUG_HTTP_CONN) && defined(CONFIG_HTTP_SERVER) -typedef void (*http_server_cb_t)(struct http_server_ctx *entry, - void *user_data); - -void __deprecated http_server_conn_foreach(http_server_cb_t cb, - void *user_data); -void __deprecated http_server_conn_monitor(http_server_cb_t cb, - void *user_data); -#else -#define http_server_conn_foreach(...) -#define http_server_conn_monitor(...) -#endif /* CONFIG_NET_DEBUG_HTTP_CONN */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __HTTP_LEGACY_H__ */ diff --git a/subsys/net/ip/net_shell.c b/subsys/net/ip/net_shell.c index 87a16b67d92..bbf9edd9b62 100644 --- a/subsys/net/ip/net_shell.c +++ b/subsys/net/ip/net_shell.c @@ -1398,37 +1398,6 @@ static char *http_str_output(char *output, int outlen, const char *str, int len) return output; } -#if !defined(CONFIG_HTTP_APP) -static void http_server_cb(struct http_server_ctx *entry, - void *user_data) -{ - int *count = user_data; - static char output[MAX_HTTP_OUTPUT_LEN]; - - /* +7 for []:port */ - char addr_local[ADDR_LEN + 7]; - char addr_remote[ADDR_LEN + 7] = ""; - - get_addresses(entry->req.net_ctx, addr_local, sizeof(addr_local), - addr_remote, sizeof(addr_remote)); - - if (*count == 0) { - printk(" HTTP ctx Local \t" - "Remote \tURL\n"); - } - - (*count)++; - - printk("[%2d] %c%c %p %16s\t%16s\t%s\n", - *count, entry->enabled ? 'E' : 'D', - entry->is_https ? 'S' : ' ', - entry, addr_local, addr_remote, - http_str_output(output, sizeof(output) - 1, - entry->req.url, entry->req.url_len)); -} -#endif - -#if defined(CONFIG_HTTP_APP) static void http_server_cb(struct http_ctx *entry, void *user_data) { int *count = user_data; @@ -1465,7 +1434,6 @@ static void http_server_cb(struct http_ctx *entry, void *user_data) entry->http.url, entry->http.url_len)); } } -#endif /* CONFIG_HTTP_APP */ #endif /* CONFIG_NET_DEBUG_HTTP_CONN && CONFIG_HTTP_SERVER */ int net_shell_cmd_http(int argc, char *argv[]) diff --git a/subsys/net/lib/http/CMakeLists.txt b/subsys/net/lib/http/CMakeLists.txt index bc6c9a488aa..8e74190d58a 100644 --- a/subsys/net/lib/http/CMakeLists.txt +++ b/subsys/net/lib/http/CMakeLists.txt @@ -7,15 +7,10 @@ endif() zephyr_library_sources_if_kconfig(http_parser.c) zephyr_library_sources_if_kconfig(http_parser_url.c) -if(CONFIG_HTTP_APP) - zephyr_library_sources(http_app.c) +zephyr_library_sources(http.c) - zephyr_library_sources_ifdef(CONFIG_HTTP_SERVER http_app_server.c) - zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_app_client.c) -else() - zephyr_library_sources_ifdef(CONFIG_HTTP_SERVER http_server.c) - zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_client.c) -endif() +zephyr_library_sources_ifdef(CONFIG_HTTP_SERVER http_server.c) +zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_client.c) zephyr_link_interface_ifdef(CONFIG_MBEDTLS mbedTLS) zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) diff --git a/subsys/net/lib/http/Kconfig b/subsys/net/lib/http/Kconfig index b7c945c270c..c6b2ff44738 100644 --- a/subsys/net/lib/http/Kconfig +++ b/subsys/net/lib/http/Kconfig @@ -11,12 +11,6 @@ config HTTP if HTTP -config HTTP_APP - bool "Use new HTTP API built on top of net-app API" - default y - -if HTTP_APP - config HTTP_SERVER bool "HTTP server support" default n @@ -60,60 +54,6 @@ config HTTP_SERVER_CONNECTIONS served at a time, so set CONFIG_NET_APP_SERVER_NUM_CONN and CONFIG_NET_TCP_BACKLOG_SIZE to 1 in that case. -endif # HTTP_APP - -if !HTTP_APP - -config HTTP_SERVER - bool "HTTP server support" - default n - select HTTP_PARSER - select HTTP_PARSER_URL - help - Enables HTTP server routines. - -config HTTP_CLIENT - bool "HTTP client support" - default n - select HTTP_PARSER - select HTTP_PARSER_URL - help - Enables HTTP client routines. - -config HTTP_HEADER_FIELD_ITEMS - int "HTTP header field max number of items" - depends on HTTP_SERVER - default 8 - help - Number of HTTP header field items that an HTTP server - application will handle - -config HTTPS - bool "HTTPS support" - default n - select MBEDTLS - help - Enables HTTPS support. - -config HTTPS_STACK_SIZE - int "HTTPS thread stack size" - default 8192 - depends on HTTPS - help - HTTPS thread stack size. The mbedtls routines will use this stack - thus it is by default very large. - -config HTTP_SERVER_CONNECTIONS - int "Max number of concurrent HTTP server connections" - default NET_MAX_CONTEXTS - depends on HTTP_SERVER - help - This value determines how many simultaneous HTTP connections the - HTTP server can serve. Note that only 1 HTTPS connection can be - served at a time. - -endif # !HTTP_APP - config HTTP_SERVER_NUM_URLS int "Max number of URLs that the HTTP server will handle" default 8 diff --git a/subsys/net/lib/http/http_app.c b/subsys/net/lib/http/http.c similarity index 100% rename from subsys/net/lib/http/http_app.c rename to subsys/net/lib/http/http.c diff --git a/subsys/net/lib/http/http_app_client.c b/subsys/net/lib/http/http_app_client.c deleted file mode 100644 index 6b37261144e..00000000000 --- a/subsys/net/lib/http/http_app_client.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#if defined(CONFIG_NET_DEBUG_HTTP) -#if defined(CONFIG_HTTPS) -#define SYS_LOG_DOMAIN "https/client" -#else -#define SYS_LOG_DOMAIN "http/client" -#endif -#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG -#define NET_LOG_ENABLED 1 -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../../ip/net_private.h" - -#define BUF_ALLOC_TIMEOUT 100 - -#define RC_STR(rc) (rc == 0 ? "OK" : "ERROR") - -#define HTTP_EOF "\r\n\r\n" - -#define HTTP_HOST "Host" -#define HTTP_CONTENT_TYPE "Content-Type" -#define HTTP_CONTENT_LEN "Content-Length" -#define HTTP_CONT_LEN_SIZE 6 - -/* Default network activity timeout in seconds */ -#define HTTP_NETWORK_TIMEOUT K_SECONDS(CONFIG_HTTP_CLIENT_NETWORK_TIMEOUT) - -int client_reset(struct http_ctx *ctx) -{ - http_parser_init(&ctx->http.parser, HTTP_RESPONSE); - - memset(ctx->http.rsp.http_status, 0, - sizeof(ctx->http.rsp.http_status)); - - ctx->http.rsp.cl_present = 0; - ctx->http.rsp.content_length = 0; - ctx->http.rsp.processed = 0; - ctx->http.rsp.body_found = 0; - ctx->http.rsp.message_complete = 0; - ctx->http.rsp.body_start = NULL; - - memset(ctx->http.rsp.response_buf, 0, ctx->http.rsp.response_buf_len); - ctx->http.rsp.data_len = 0; - - return 0; -} - -int http_request(struct http_ctx *ctx, struct http_request *req, s32_t timeout, - void *user_data) -{ - const char *method = http_method_str(req->method); - int ret; - - if (ctx->pending) { - net_pkt_unref(ctx->pending); - ctx->pending = NULL; - } - - ret = http_add_header(ctx, method, user_data); - if (ret < 0) { - goto out; - } - - ret = http_add_header(ctx, " ", user_data); - if (ret < 0) { - goto out; - } - - ret = http_add_header(ctx, req->url, user_data); - if (ret < 0) { - goto out; - } - - ret = http_add_header(ctx, req->protocol, user_data); - if (ret < 0) { - goto out; - } - - ret = http_add_header(ctx, HTTP_CRLF, user_data); - if (ret < 0) { - goto out; - } - - if (req->host) { - ret = http_add_header_field(ctx, HTTP_HOST, req->host, - user_data); - if (ret < 0) { - goto out; - } - } - - if (req->header_fields) { - ret = http_add_header(ctx, req->header_fields, user_data); - if (ret < 0) { - goto out; - } - } - - if (req->content_type_value) { - ret = http_add_header_field(ctx, HTTP_CONTENT_TYPE, - req->content_type_value, - user_data); - if (ret < 0) { - goto out; - } - } - - if (req->payload && req->payload_size) { - char content_len_str[HTTP_CONT_LEN_SIZE]; - - ret = snprintk(content_len_str, HTTP_CONT_LEN_SIZE, - "%u", req->payload_size); - if (ret <= 0 || ret >= HTTP_CONT_LEN_SIZE) { - ret = -ENOMEM; - goto out; - } - - ret = http_add_header_field(ctx, HTTP_CONTENT_LEN, - content_len_str, user_data); - if (ret < 0) { - goto out; - } - - ret = http_add_header(ctx, HTTP_CRLF, user_data); - if (ret < 0) { - goto out; - } - - ret = http_prepare_and_send(ctx, req->payload, - req->payload_size, user_data); - if (ret < 0) { - goto out; - } - } else { - ret = http_add_header(ctx, HTTP_EOF, user_data); - if (ret < 0) { - goto out; - } - } - - http_send_flush(ctx, user_data); - -out: - if (ctx->pending) { - net_pkt_unref(ctx->pending); - ctx->pending = NULL; - } - - return ret; -} - -#if defined(CONFIG_NET_DEBUG_HTTP) -static void sprint_addr(char *buf, int len, - sa_family_t family, - struct sockaddr *addr) -{ - if (family == AF_INET6) { - net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf, len); - } else if (family == AF_INET) { - net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf, len); - } else { - NET_DBG("Invalid protocol family"); - } -} -#endif - -static inline void print_info(struct http_ctx *ctx, - enum http_method method) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) - char local[NET_IPV6_ADDR_LEN]; - char remote[NET_IPV6_ADDR_LEN]; - - sprint_addr(local, NET_IPV6_ADDR_LEN, - ctx->app_ctx.default_ctx->local.sa_family, - &ctx->app_ctx.default_ctx->local); - - sprint_addr(remote, NET_IPV6_ADDR_LEN, - ctx->app_ctx.default_ctx->remote.sa_family, - &ctx->app_ctx.default_ctx->remote); - - NET_DBG("HTTP %s (%s) %s -> %s port %d", - http_method_str(method), ctx->http.req.host, local, remote, - ntohs(net_sin(&ctx->app_ctx.default_ctx->remote)->sin_port)); -#endif -} - -int http_client_send_req(struct http_ctx *ctx, - struct http_request *req, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout) -{ - int ret; - - if (!response_buf || response_buf_len == 0) { - return -EINVAL; - } - - ctx->http.rsp.response_buf = response_buf; - ctx->http.rsp.response_buf_len = response_buf_len; - - client_reset(ctx); - - if (!req->host) { - req->host = ctx->server; - } - - ctx->http.req.host = req->host; - ctx->http.req.method = req->method; - ctx->http.req.user_data = user_data; - - ctx->http.rsp.cb = cb; - - ret = net_app_connect(&ctx->app_ctx, HTTP_NETWORK_TIMEOUT); - if (ret < 0) { - NET_DBG("Cannot connect to server (%d)", ret); - return ret; - } - - /* We might wait longer than timeout if the first connection - * establishment takes long time (like with HTTPS) - */ - if (k_sem_take(&ctx->http.connect_wait, HTTP_NETWORK_TIMEOUT)) { - NET_DBG("Connection timed out"); - ret = -ETIMEDOUT; - goto out; - } - - print_info(ctx, ctx->http.req.method); - - ret = http_request(ctx, req, timeout, user_data); - if (ret < 0) { - NET_DBG("Send error (%d)", ret); - goto out; - } - - if (timeout != 0 && k_sem_take(&ctx->http.req.wait, timeout)) { - ret = -ETIMEDOUT; - goto out; - } - - if (timeout == 0) { - return -EINPROGRESS; - } - - return 0; - -out: - return ret; -} - -static void print_header_field(size_t len, const char *str) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) -#define MAX_OUTPUT_LEN 128 - char output[MAX_OUTPUT_LEN]; - - /* The value of len does not count \0 so we need to increase it - * by one. - */ - if ((len + 1) > sizeof(output)) { - len = sizeof(output) - 1; - } - - snprintk(output, len + 1, "%s", str); - - NET_DBG("[%zd] %s", len, output); -#endif -} - -static int on_url(struct http_parser *parser, const char *at, size_t length) -{ - ARG_UNUSED(parser); - - print_header_field(length, at); - - return 0; -} - -static int on_status(struct http_parser *parser, const char *at, size_t length) -{ - u16_t len; - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - - len = min(length, sizeof(ctx->http.rsp.http_status) - 1); - memcpy(ctx->http.rsp.http_status, at, len); - ctx->http.rsp.http_status[len] = 0; - - NET_DBG("HTTP response status %s", ctx->http.rsp.http_status); - - return 0; -} - -static int on_header_field(struct http_parser *parser, const char *at, - size_t length) -{ - const char *content_len = HTTP_CONTENT_LEN; - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - u16_t len; - - len = strlen(content_len); - if (length >= len && memcmp(at, content_len, len) == 0) { - ctx->http.rsp.cl_present = true; - } - - print_header_field(length, at); - - return 0; -} - -#define MAX_NUM_DIGITS 16 - -static int on_header_value(struct http_parser *parser, const char *at, - size_t length) -{ - char str[MAX_NUM_DIGITS]; - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - - if (ctx->http.rsp.cl_present) { - if (length <= MAX_NUM_DIGITS - 1) { - long int num; - - memcpy(str, at, length); - str[length] = 0; - - num = strtol(str, NULL, 10); - if (num == LONG_MIN || num == LONG_MAX) { - return -EINVAL; - } - - ctx->http.rsp.content_length = num; - } - - ctx->http.rsp.cl_present = false; - } - - print_header_field(length, at); - - return 0; -} - -static int on_body(struct http_parser *parser, const char *at, size_t length) -{ - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - - ctx->http.rsp.body_found = 1; - ctx->http.rsp.processed += length; - - NET_DBG("Processed %zd length %zd", ctx->http.rsp.processed, length); - - if (!ctx->http.rsp.body_start && - (u8_t *)at != (u8_t *)ctx->http.rsp.response_buf) { - ctx->http.rsp.body_start = (u8_t *)at; - } - - if (ctx->http.rsp.cb) { - NET_DBG("Calling callback for partitioned %zd len data", - ctx->http.rsp.data_len); - - ctx->http.rsp.cb(ctx, - ctx->http.rsp.response_buf, - ctx->http.rsp.response_buf_len, - ctx->http.rsp.data_len, - HTTP_DATA_MORE, - ctx->http.req.user_data); - - /* Re-use the result buffer and start to fill it again */ - ctx->http.rsp.data_len = 0; - ctx->http.rsp.body_start = NULL; - } - - return 0; -} - -static int on_headers_complete(struct http_parser *parser) -{ - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - - if (parser->status_code >= 500 && parser->status_code < 600) { - NET_DBG("Status %d, skipping body", parser->status_code); - - return 1; - } - - if ((ctx->http.req.method == HTTP_HEAD || - ctx->http.req.method == HTTP_OPTIONS) - && ctx->http.rsp.content_length > 0) { - NET_DBG("No body expected"); - return 1; - } - - NET_DBG("Headers complete"); - - return 0; -} - -static int on_message_begin(struct http_parser *parser) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - - NET_DBG("-- HTTP %s response (headers) --", - http_method_str(ctx->http.req.method)); -#else - ARG_UNUSED(parser); -#endif - return 0; -} - -static int on_message_complete(struct http_parser *parser) -{ - struct http_ctx *ctx = CONTAINER_OF(parser, - struct http_ctx, - http.parser); - - NET_DBG("-- HTTP %s response (complete) --", - http_method_str(ctx->http.req.method)); - - if (ctx->http.rsp.cb) { - ctx->http.rsp.cb(ctx, - ctx->http.rsp.response_buf, - ctx->http.rsp.response_buf_len, - ctx->http.rsp.data_len, - HTTP_DATA_FINAL, - ctx->http.req.user_data); - } - - ctx->http.rsp.message_complete = 1; - - k_sem_give(&ctx->http.req.wait); - - return 0; -} - -static int on_chunk_header(struct http_parser *parser) -{ - ARG_UNUSED(parser); - - return 0; -} - -static int on_chunk_complete(struct http_parser *parser) -{ - ARG_UNUSED(parser); - - return 0; -} - -static void http_received(struct net_app_ctx *app_ctx, - struct net_pkt *pkt, - int status, - void *user_data) -{ - struct http_ctx *ctx = user_data; - size_t start = ctx->http.rsp.data_len; - u16_t len = 0; - struct net_buf *frag, *prev_frag = NULL; - size_t recv_len; - size_t pkt_len; - - recv_len = net_pkt_appdatalen(pkt); - if (recv_len == 0) { - /* don't print info about zero-length app data buffers */ - goto quit; - } - - if (status) { - NET_DBG("[%p] Status %d <%s>", ctx, status, RC_STR(status)); - goto out; - } - - /* Get rid of possible IP headers in the first fragment. */ - frag = pkt->frags; - - pkt_len = net_pkt_get_len(pkt); - - if (recv_len < pkt_len) { - net_buf_pull(frag, pkt_len - recv_len); - net_pkt_set_appdata(pkt, frag->data); - } - - NET_DBG("[%p] Received %zd bytes http data", ctx, recv_len); - - while (frag) { - /* If this fragment cannot be copied to result buf, - * then parse what we have which will cause the callback to be - * called in function on_body(), and continue copying. - */ - if ((ctx->http.rsp.data_len + frag->len) > - ctx->http.rsp.response_buf_len) { - - /* If the caller has not supplied a callback, then - * we cannot really continue if the request buffer - * overflows. Set the data_len to mark how many bytes - * should be needed in the response_buf. - */ - if (!ctx->cb.recv) { - ctx->http.rsp.data_len = recv_len; - goto out; - } - - http_parser_execute(&ctx->http.parser, - &ctx->http.parser_settings, - ctx->http.rsp.response_buf + start, - len); - - ctx->http.rsp.data_len = 0; - len = 0; - start = 0; - } - - memcpy(ctx->http.rsp.response_buf + ctx->http.rsp.data_len, - frag->data, frag->len); - - ctx->http.rsp.data_len += frag->len; - len += frag->len; - - prev_frag = frag; - frag = frag->frags; - pkt->frags = frag; - - prev_frag->frags = NULL; - net_pkt_frag_unref(prev_frag); - } - -out: - http_parser_execute(&ctx->http.parser, - &ctx->http.parser_settings, - ctx->http.rsp.response_buf + start, - len); - - net_pkt_unref(pkt); - return; - -quit: - http_parser_init(&ctx->http.parser, HTTP_RESPONSE); - ctx->http.rsp.data_len = 0; - net_pkt_unref(pkt); -} - -static void http_data_sent(struct net_app_ctx *app_ctx, - int status, - void *user_data_send, - void *user_data) -{ - struct http_ctx *ctx = user_data; - - if (!user_data_send) { - /* This is the token field in the net_context_send(). - * If this is not set, then it is TCP ACK messages - * that are generated by the stack. We just ignore those. - */ - return; - } - - if (ctx->cb.send) { - ctx->cb.send(ctx, status, user_data_send, ctx->user_data); - } -} - -static void http_connected(struct net_app_ctx *app_ctx, - int status, - void *user_data) -{ - struct http_ctx *ctx = user_data; - - if (status < 0) { - return; - } - - if (ctx->cb.connect) { - ctx->cb.connect(ctx, HTTP_CONNECTION, ctx->user_data); - } - - if (ctx->is_connected) { - return; - } - - ctx->is_connected = true; - - k_sem_give(&ctx->http.connect_wait); -} - -static void http_closed(struct net_app_ctx *app_ctx, - int status, - void *user_data) -{ - struct http_ctx *ctx = user_data; - - ARG_UNUSED(app_ctx); - ARG_UNUSED(status); - - NET_DBG("[%p] connection closed", ctx); - - ctx->is_connected = false; - - if (ctx->cb.close) { - ctx->cb.close(ctx, 0, ctx->user_data); - } -} - -int http_client_init(struct http_ctx *ctx, - const char *server, - u16_t server_port, - struct sockaddr *server_addr, - s32_t timeout) -{ - int ret; - - memset(ctx, 0, sizeof(*ctx)); - - ret = net_app_init_tcp_client(&ctx->app_ctx, - NULL, /* use any local address */ - server_addr, - server, - server_port, - timeout, - ctx); - if (ret < 0) { - NET_DBG("Cannot init HTTP client (%d)", ret); - return ret; - } - - ret = net_app_set_cb(&ctx->app_ctx, http_connected, http_received, - http_data_sent, http_closed); - if (ret < 0) { - NET_ERR("Cannot set callbacks (%d)", ret); - return ret; - } - - ctx->http.parser_settings.on_body = on_body; - ctx->http.parser_settings.on_chunk_complete = on_chunk_complete; - ctx->http.parser_settings.on_chunk_header = on_chunk_header; - ctx->http.parser_settings.on_headers_complete = on_headers_complete; - ctx->http.parser_settings.on_header_field = on_header_field; - ctx->http.parser_settings.on_header_value = on_header_value; - ctx->http.parser_settings.on_message_begin = on_message_begin; - ctx->http.parser_settings.on_message_complete = on_message_complete; - ctx->http.parser_settings.on_status = on_status; - ctx->http.parser_settings.on_url = on_url; - - k_sem_init(&ctx->http.req.wait, 0, 1); - k_sem_init(&ctx->http.connect_wait, 0, 1); - - ctx->server = server; - ctx->is_init = true; - ctx->is_client = true; - - return 0; -} - -int http_request_cancel(struct http_ctx *ctx) -{ - if (!ctx->is_init) { - return -EINVAL; - } - - if (!ctx->is_client) { - return -EINVAL; - } - - client_reset(ctx); - - return 0; -} - -#if defined(CONFIG_HTTPS) -int http_client_set_tls(struct http_ctx *ctx, - u8_t *request_buf, - size_t request_buf_len, - u8_t *personalization_data, - size_t personalization_data_len, - net_app_ca_cert_cb_t cert_cb, - const char *cert_host, - net_app_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *https_stack, - size_t https_stack_size) -{ - int ret; - - ret = net_app_client_tls(&ctx->app_ctx, - request_buf, - request_buf_len, - personalization_data, - personalization_data_len, - cert_cb, - cert_host, - entropy_src_cb, - pool, - https_stack, - https_stack_size); - if (ret < 0) { - NET_DBG("Cannot init TLS (%d)", ret); - return ret; - } - - ctx->is_tls = true; - - return 0; -} -#endif /* CONFIG_HTTPS */ diff --git a/subsys/net/lib/http/http_app_server.c b/subsys/net/lib/http/http_app_server.c deleted file mode 100644 index b38e22717e2..00000000000 --- a/subsys/net/lib/http/http_app_server.c +++ /dev/null @@ -1,922 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#if defined(CONFIG_NET_DEBUG_HTTP) -#if defined(CONFIG_HTTPS) -#define SYS_LOG_DOMAIN "https/server" -#else -#define SYS_LOG_DOMAIN "http/server" -#endif -#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG -#define NET_LOG_ENABLED 1 -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define BUF_ALLOC_TIMEOUT 100 - -#define HTTP_DEFAULT_PORT 80 -#define HTTPS_DEFAULT_PORT 443 - -#define RC_STR(rc) (rc == 0 ? "OK" : "ERROR") - -/* Max length of the error description in HTTP error reply */ -#define MAX_DESCRIPTION_LEN 20 - -#define HTTP_STATUS_400_BR "Bad Request" - -#if defined(CONFIG_NET_DEBUG_HTTP_CONN) -/** List of http connections */ -static sys_slist_t http_conn; - -static http_server_cb_t ctx_mon; -static void *mon_user_data; - -void http_server_conn_add(struct http_ctx *ctx) -{ - sys_slist_prepend(&http_conn, &ctx->node); - - if (ctx_mon) { - ctx_mon(ctx, mon_user_data); - } -} - -void http_server_conn_del(struct http_ctx *ctx) -{ - sys_slist_find_and_remove(&http_conn, &ctx->node); -} - -void http_server_conn_foreach(http_server_cb_t cb, void *user_data) -{ - struct http_ctx *ctx; - - SYS_SLIST_FOR_EACH_CONTAINER(&http_conn, ctx, node) { - cb(ctx, user_data); - } -} - -void http_server_conn_monitor(http_server_cb_t cb, void *user_data) -{ - ctx_mon = cb; - mon_user_data = user_data; -} -#endif /* CONFIG_NET_DEBUG_HTTP_CONN */ - -const char * const http_state_str(enum http_state state) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) - switch (state) { - case HTTP_STATE_CLOSED: - return "CLOSED"; - case HTTP_STATE_WAITING_HEADER: - return "WAITING_HEADER"; - case HTTP_STATE_RECEIVING_HEADER: - return "RECEIVING HEADER"; - case HTTP_STATE_HEADER_RECEIVED: - return "HEADER_RECEIVED"; - case HTTP_STATE_OPEN: - return "OPEN"; - } -#else /* CONFIG_NET_DEBUG_HTTP */ - ARG_UNUSED(state); -#endif /* CONFIG_NET_DEBUG_HTTP */ - - return ""; -} - -#if defined(CONFIG_NET_DEBUG_HTTP) -static void validate_state_transition(struct http_ctx *ctx, - enum http_state current, - enum http_state new) -{ - static const u16_t valid_transitions[] = { - [HTTP_STATE_CLOSED] = 1 << HTTP_STATE_WAITING_HEADER, - [HTTP_STATE_WAITING_HEADER] = - 1 << HTTP_STATE_RECEIVING_HEADER | - 1 << HTTP_STATE_CLOSED, - [HTTP_STATE_RECEIVING_HEADER] = - 1 << HTTP_STATE_HEADER_RECEIVED | - 1 << HTTP_STATE_CLOSED | - 1 << HTTP_STATE_OPEN, - [HTTP_STATE_HEADER_RECEIVED] = - 1 << HTTP_STATE_OPEN | - 1 << HTTP_STATE_CLOSED, - [HTTP_STATE_OPEN] = 1 << HTTP_STATE_CLOSED, - }; - - if (!(valid_transitions[current] & 1 << new)) { - NET_DBG("[%p] Invalid state transition: %s (%d) => %s (%d)", - ctx, http_state_str(current), current, - http_state_str(new), new); - } -} -#endif /* CONFIG_NET_DEBUG_HTTP */ - -void _http_change_state(struct http_ctx *ctx, - enum http_state new_state, - const char *func, int line) -{ - if (ctx->state == new_state) { - return; - } - - NET_ASSERT(new_state >= HTTP_STATE_CLOSED && - new_state <= HTTP_STATE_OPEN); - - NET_DBG("[%p] state %s (%d) => %s (%d) [%s():%d]", - ctx, http_state_str(ctx->state), ctx->state, - http_state_str(new_state), new_state, - func, line); - -#if defined(CONFIG_NET_DEBUG_HTTP) - validate_state_transition(ctx, ctx->state, new_state); -#endif /* CONFIG_NET_DEBUG_HTTP */ - - ctx->state = new_state; -} - -static void http_data_sent(struct net_app_ctx *app_ctx, - int status, - void *user_data_send, - void *user_data) -{ - struct http_ctx *ctx = user_data; - - if (!user_data_send) { - /* This is the token field in the net_context_send(). - * If this is not set, then it is TCP ACK messages - * that are generated by the stack. We just ignore those. - */ - return; - } - - if (ctx->state == HTTP_STATE_OPEN && ctx->cb.send) { - ctx->cb.send(ctx, status, user_data_send, ctx->user_data); - } -} - -int http_send_error(struct http_ctx *ctx, int code, const char *description, - u8_t *html_payload, size_t html_len) -{ - char msg[sizeof(HTTP_PROTOCOL " xxx " HTTP_CRLF HTTP_CRLF) + - MAX_DESCRIPTION_LEN]; - int ret; - - if (ctx->pending) { - net_pkt_unref(ctx->pending); - ctx->pending = NULL; - } - - if (code < 100 || code > 999) { - return -EINVAL; - } - - snprintk(msg, sizeof(msg), "%s %d %s%s%s", HTTP_PROTOCOL, code, - description, HTTP_CRLF, HTTP_CRLF); - - ret = http_add_header(ctx, msg, NULL); - if (ret < 0) { - goto quit; - } - - if (html_payload) { - ret = http_prepare_and_send(ctx, html_payload, html_len, NULL); - if (ret < 0) { - goto quit; - } - } - - ret = http_send_flush(ctx, NULL); - -quit: - if (ret < 0) { - net_pkt_unref(ctx->pending); - ctx->pending = NULL; - } - - return ret; -} - -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) -static char *sprint_ipaddr(char *buf, int buflen, const struct sockaddr *addr) -{ - if (addr->sa_family == AF_INET6) { -#if defined(CONFIG_NET_IPV6) - char ipaddr[NET_IPV6_ADDR_LEN]; - - net_addr_ntop(addr->sa_family, - &net_sin6(addr)->sin6_addr, - ipaddr, sizeof(ipaddr)); - snprintk(buf, buflen, "[%s]:%u", ipaddr, - ntohs(net_sin6(addr)->sin6_port)); -#endif - } else if (addr->sa_family == AF_INET) { -#if defined(CONFIG_NET_IPV4) - char ipaddr[NET_IPV4_ADDR_LEN]; - - net_addr_ntop(addr->sa_family, - &net_sin(addr)->sin_addr, - ipaddr, sizeof(ipaddr)); - snprintk(buf, buflen, "%s:%u", ipaddr, - ntohs(net_sin(addr)->sin_port)); -#endif - } else { - snprintk(buf, buflen, "", addr->sa_family); - } - - return buf; -} - -static struct net_context *get_server_ctx(struct net_app_ctx *ctx, - const struct sockaddr *dst) -{ - int i; - - if (!dst) { - return NULL; - } - - for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) { - struct net_context *tmp; - u16_t port, rport; - - if (!ctx->server.net_ctxs[i]) { - continue; - } - - tmp = ctx->server.net_ctxs[i]; - - if (IS_ENABLED(CONFIG_NET_IPV4) && - tmp->remote.sa_family == AF_INET && - dst->sa_family == AF_INET) { - struct in_addr *addr4 = &net_sin(dst)->sin_addr; - struct in_addr *remote4; - - remote4 = &net_sin(&tmp->remote)->sin_addr; - rport = net_sin(&tmp->remote)->sin_port; - port = net_sin(dst)->sin_port; - - if (net_ipv4_addr_cmp(addr4, remote4) && - port == rport) { - return tmp; - } - - } else if (IS_ENABLED(CONFIG_NET_IPV6) && - tmp->remote.sa_family == AF_INET6 && - dst->sa_family == AF_INET6) { - struct in6_addr *addr6 = &net_sin6(dst)->sin6_addr; - struct in6_addr *remote6; - - remote6 = &net_sin6(&tmp->remote)->sin6_addr; - rport = net_sin6(&tmp->remote)->sin6_port; - port = net_sin6(dst)->sin6_port; - - if (net_ipv6_addr_cmp(addr6, remote6) && - port == rport) { - return tmp; - } - } - } - - return NULL; -} -#endif /* CONFIG_NET_DEBUG_HTTP */ - -static inline void new_client(struct http_ctx *ctx, - enum http_connection_type type, - struct net_app_ctx *app_ctx) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) -#if defined(CONFIG_NET_IPV6) -#define PORT_LEN sizeof("[]:xxxxx") -#define ADDR_LEN NET_IPV6_ADDR_LEN -#elif defined(CONFIG_NET_IPV4) -#define PORT_LEN sizeof(":xxxxx") -#define ADDR_LEN NET_IPV4_ADDR_LEN -#endif - char buf[ADDR_LEN + PORT_LEN]; - struct net_context *net_ctx; - const char *type_str = "HTTP"; - - net_ctx = get_server_ctx(app_ctx, ctx->addr); - if (net_ctx) { - NET_INFO("[%p] %s connection from %s (%p)", ctx, type_str, - sprint_ipaddr(buf, sizeof(buf), &net_ctx->remote), - net_ctx); - } else { - NET_INFO("[%p] %s connection", ctx, type_str); - } -#endif /* CONFIG_NET_DEBUG_HTTP */ -} - -static void url_connected(struct http_ctx *ctx, - enum http_connection_type type) -{ - new_client(ctx, type, &ctx->app_ctx); - - if (ctx->cb.connect) { - ctx->cb.connect(ctx, type, ctx->user_data); - } -} - -struct http_root_url *http_server_add_url(struct http_server_urls *my, - const char *url, u8_t flags) -{ - int i; - - for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) { - if (my->urls[i].is_used) { - continue; - } - - my->urls[i].is_used = true; - my->urls[i].root = url; - - /* This will speed-up some future operations */ - my->urls[i].root_len = strlen(url); - my->urls[i].flags = flags; - - NET_DBG("[%d] %s URL %s", i, - flags == HTTP_URL_STANDARD ? "HTTP" : - (flags == HTTP_URL_WEBSOCKET ? "WS" : ""), - url); - - return &my->urls[i]; - } - - return NULL; -} - -int http_server_del_url(struct http_server_urls *my, const char *url) -{ - int i; - - for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) { - if (!my->urls[i].is_used) { - continue; - } - - if (strncmp(my->urls[i].root, url, my->urls[i].root_len)) { - continue; - } - - my->urls[i].is_used = false; - my->urls[i].root = NULL; - - return 0; - } - - return -ENOENT; -} - -struct http_root_url *http_server_add_default(struct http_server_urls *my, - http_url_cb_t cb) -{ - if (my->default_url.is_used) { - return NULL; - } - - my->default_url.is_used = true; - my->default_url.root = NULL; - my->default_url.root_len = 0; - my->default_url.flags = 0; - my->default_cb = cb; - - return &my->default_url; -} - -int http_server_del_default(struct http_server_urls *my) -{ - if (!my->default_url.is_used) { - return -ENOENT; - } - - my->default_url.is_used = false; - - return 0; -} - -static int http_url_cmp(const char *url, u16_t url_len, - const char *root_url, u16_t root_url_len) -{ - if (url_len < root_url_len) { - return -EINVAL; - } - - if (memcmp(url, root_url, root_url_len) == 0) { - if (url_len == root_url_len) { - return 0; - } - - /* Here we evaluate the following conditions: - * root_url = /images, url = /images/ -> OK - * root_url = /images/, url = /images/img.png -> OK - * root_url = /images/, url = /images_and_docs -> ERROR - */ - if (url_len > root_url_len) { - /* Do not match root_url = / and url = /foobar */ - if (root_url_len > 1 && - root_url[root_url_len - 1] == '/') { - return 0; - } - - if (url[root_url_len] == '/') { - return 0; - } - } - } - - return -EINVAL; -} - -struct http_root_url *http_url_find(struct http_ctx *ctx, - enum http_url_flags flags) -{ - u16_t url_len = ctx->http.url_len; - const char *url = ctx->http.url; - struct http_root_url *root_url; - u8_t i; - int ret; - - for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) { - if (!ctx->http.urls) { - continue; - } - - root_url = &ctx->http.urls->urls[i]; - if (!root_url || !root_url->is_used) { - continue; - } - - ret = http_url_cmp(url, url_len, - root_url->root, root_url->root_len); - if (!ret && flags == root_url->flags) { - return root_url; - } - } - - return NULL; -} - -static int http_process_recv(struct http_ctx *ctx) -{ - struct http_root_url *root_url; - int ret; - - root_url = http_url_find(ctx, HTTP_URL_STANDARD); - if (!root_url) { - if (!ctx->http.urls) { - NET_DBG("[%p] No URL handlers found", ctx); - ret = -ENOENT; - goto out; - } - - root_url = &ctx->http.urls->default_url; - if (!root_url || !root_url->is_used) { - NET_DBG("[%p] No default handler found", ctx); - ret = -ENOENT; - goto out; - } - - if (ctx->http.urls->default_cb) { - ret = ctx->http.urls->default_cb(ctx, - HTTP_CONNECTION); - if (ret != HTTP_VERDICT_ACCEPT) { - ret = -ECONNREFUSED; - goto out; - } - } - } - - http_change_state(ctx, HTTP_STATE_OPEN); - url_connected(ctx, HTTP_CONNECTION); - - ret = 0; - -out: - return ret; -} - -static void http_closed(struct net_app_ctx *app_ctx, - int status, - void *user_data) -{ - struct http_ctx *ctx = user_data; - - ARG_UNUSED(app_ctx); - ARG_UNUSED(status); - - http_change_state(ctx, HTTP_STATE_CLOSED); - - NET_DBG("[%p] http closed", ctx); - - http_server_conn_del(ctx); - - if (ctx->cb.close) { - ctx->cb.close(ctx, 0, ctx->user_data); - } -} - -static void http_received(struct net_app_ctx *app_ctx, - struct net_pkt *pkt, - int status, - void *user_data) -{ - struct http_ctx *ctx = user_data; - size_t start = ctx->http.data_len; - u16_t len = 0; - struct net_buf *frag; - int parsed_len; - size_t recv_len; - size_t pkt_len; - - recv_len = net_pkt_appdatalen(pkt); - if (recv_len == 0) { - /* don't print info about zero-length app data buffers */ - goto quit; - } - - if (status) { - NET_DBG("[%p] Status %d <%s>", ctx, status, RC_STR(status)); - goto out; - } - - /* Get rid of possible IP headers in the first fragment. */ - frag = pkt->frags; - - pkt_len = net_pkt_get_len(pkt); - - if (recv_len < pkt_len) { - net_buf_pull(frag, pkt_len - recv_len); - net_pkt_set_appdata(pkt, frag->data); - } - - NET_DBG("[%p] Received %zd bytes http data", ctx, recv_len); - - if (ctx->state == HTTP_STATE_OPEN) { - /* We have active websocket session and there is no longer - * any HTTP traffic in the connection. Give the data to - * application to send. - */ - goto http_only; - } - - while (frag) { - /* If this fragment cannot be copied to result buf, - * then parse what we have which will cause the callback to be - * called in function on_body(), and continue copying. - */ - if ((ctx->http.data_len + frag->len) > - ctx->http.request_buf_len) { - - if (ctx->state == HTTP_STATE_HEADER_RECEIVED) { - goto http_ready; - } - - /* If the caller has not supplied a callback, then - * we cannot really continue if the request buffer - * overflows. Set the data_len to mark how many bytes - * should be needed in the response_buf. - */ - if (http_process_recv(ctx) < 0) { - ctx->http.data_len = recv_len; - goto out; - } - - parsed_len = - http_parser_execute(&ctx->http.parser, - &ctx->http.parser_settings, - ctx->http.request_buf + - start, - len); - if (parsed_len <= 0) { - goto fail; - } - - ctx->http.data_len = 0; - len = 0; - start = 0; - } - - memcpy(ctx->http.request_buf + ctx->http.data_len, - frag->data, frag->len); - - ctx->http.data_len += frag->len; - len += frag->len; - frag = frag->frags; - } - -out: - parsed_len = http_parser_execute(&ctx->http.parser, - &ctx->http.parser_settings, - ctx->http.request_buf + start, - len); - if (parsed_len < 0) { -fail: - NET_DBG("[%p] Received %zd bytes, only parsed %d " - "bytes (%s %s)", - ctx, recv_len, parsed_len, - http_errno_name(ctx->http.parser.http_errno), - http_errno_description( - ctx->http.parser.http_errno)); - } - - if (ctx->http.parser.http_errno != HPE_OK) { - http_send_error(ctx, 400, HTTP_STATUS_400_BR, NULL, 0); - } else { - if (ctx->state == HTTP_STATE_HEADER_RECEIVED) { - goto http_ready; - } - - http_process_recv(ctx); - } - -quit: - http_parser_init(&ctx->http.parser, HTTP_REQUEST); - ctx->http.data_len = 0; - ctx->http.field_values_ctr = 0; - net_pkt_unref(pkt); - - return; - -http_only: - if (ctx->cb.recv) { - ctx->cb.recv(ctx, pkt, 0, 0, ctx->user_data); - } - - return; - -http_ready: - http_change_state(ctx, HTTP_STATE_OPEN); - url_connected(ctx, HTTP_CONNECT); - net_pkt_unref(pkt); -} - -#if defined(CONFIG_HTTPS) -int http_server_set_tls(struct http_ctx *ctx, - const char *server_banner, - u8_t *personalization_data, - size_t personalization_data_len, - net_app_cert_cb_t cert_cb, - net_app_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *stack, - size_t stack_len) -{ - int ret; - - if (!ctx->is_tls) { - /* Change the default port if user did not set it */ - if (!ctx->server_addr) { - net_sin(&ctx->local)->sin_port = - htons(HTTPS_DEFAULT_PORT); - -#if defined(CONFIG_NET_IPV6) - net_sin6(&ctx->app_ctx.ipv6.local)->sin6_port = - htons(HTTPS_DEFAULT_PORT); -#endif -#if defined(CONFIG_NET_IPV4) - net_sin(&ctx->app_ctx.ipv4.local)->sin_port = - htons(HTTPS_DEFAULT_PORT); -#endif - } - - ret = net_app_server_tls(&ctx->app_ctx, - ctx->http.request_buf, - ctx->http.request_buf_len, - server_banner, - personalization_data, - personalization_data_len, - cert_cb, - entropy_src_cb, - pool, - stack, - stack_len); - if (ret < 0) { - NET_ERR("Cannot init TLS (%d)", ret); - goto quit; - } - - ctx->is_tls = true; - return 0; - } - - return -EALREADY; - -quit: - net_app_release(&ctx->app_ctx); - return ret; -} -#endif - -static int on_header_field(struct http_parser *parser, - const char *at, size_t length) -{ - struct http_ctx *ctx = parser->data; - - if (ctx->http.field_values_ctr >= CONFIG_HTTP_HEADERS) { - return 0; - } - - http_change_state(ctx, HTTP_STATE_RECEIVING_HEADER); - - ctx->http.field_values[ctx->http.field_values_ctr].key = at; - ctx->http.field_values[ctx->http.field_values_ctr].key_len = length; - - return 0; -} - -static int on_header_value(struct http_parser *parser, - const char *at, size_t length) -{ - struct http_ctx *ctx = parser->data; - - if (ctx->http.field_values_ctr >= CONFIG_HTTP_HEADERS) { - return 0; - } - - ctx->http.field_values[ctx->http.field_values_ctr].value = at; - ctx->http.field_values[ctx->http.field_values_ctr].value_len = length; - - ctx->http.field_values_ctr++; - - return 0; -} - -static int on_url(struct http_parser *parser, const char *at, size_t length) -{ - struct http_ctx *ctx = parser->data; - - ctx->http.url = at; - ctx->http.url_len = length; - - http_change_state(ctx, HTTP_STATE_WAITING_HEADER); - - http_server_conn_add(ctx); - - return 0; -} - -static int on_headers_complete(struct http_parser *parser) -{ - ARG_UNUSED(parser); - - return 0; -} - -static int init_http_parser(struct http_ctx *ctx) -{ - memset(ctx->http.field_values, 0, sizeof(ctx->http.field_values)); - - ctx->http.parser_settings.on_header_field = on_header_field; - ctx->http.parser_settings.on_header_value = on_header_value; - ctx->http.parser_settings.on_url = on_url; - ctx->http.parser_settings.on_headers_complete = on_headers_complete; - - http_parser_init(&ctx->http.parser, HTTP_REQUEST); - - ctx->http.parser.data = ctx; - - return 0; -} - -static inline void new_server(struct http_ctx *ctx, - const char *server_banner, - const struct sockaddr *addr) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) -#if defined(CONFIG_NET_IPV6) -#define PORT_STR sizeof("[]:xxxxx") - char buf[NET_IPV6_ADDR_LEN + PORT_STR]; -#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) -#define PORT_STR sizeof(":xxxxx") - char buf[NET_IPV4_ADDR_LEN + PORT_STR]; -#endif - - if (addr) { - NET_INFO("%s %s (%p)", server_banner, - sprint_ipaddr(buf, sizeof(buf), addr), ctx); - } else { - NET_INFO("%s (%p)", server_banner, ctx); - } -#endif /* CONFIG_NET_DEBUG_HTTP */ -} - -static void init_net(struct http_ctx *ctx, - struct sockaddr *server_addr, - u16_t port) -{ - memset(&ctx->local, 0, sizeof(ctx->local)); - - if (server_addr) { - memcpy(&ctx->local, server_addr, sizeof(ctx->local)); - } else { - ctx->local.sa_family = AF_UNSPEC; - net_sin(&ctx->local)->sin_port = htons(port); - } -} - -int http_server_init(struct http_ctx *ctx, - struct http_server_urls *urls, - struct sockaddr *server_addr, - u8_t *request_buf, - size_t request_buf_len, - const char *server_banner, - void *user_data) -{ - int ret = 0; - - if (!ctx) { - return -EINVAL; - } - - if (ctx->is_init) { - return -EALREADY; - } - - if (!request_buf || request_buf_len == 0) { - NET_ERR("Request buf must be set"); - return -EINVAL; - } - - memset(ctx, 0, sizeof(*ctx)); - - init_net(ctx, server_addr, HTTP_DEFAULT_PORT); - - if (server_banner) { - new_server(ctx, server_banner, server_addr); - } - - /* Timeout for network buffer allocations */ - ctx->timeout = BUF_ALLOC_TIMEOUT; - - ctx->http.request_buf = request_buf; - ctx->http.request_buf_len = request_buf_len; - ctx->http.urls = urls; - ctx->http.data_len = 0; - ctx->user_data = user_data; - ctx->server_addr = server_addr; - - ret = net_app_init_tcp_server(&ctx->app_ctx, - &ctx->local, - HTTP_DEFAULT_PORT, - ctx); - if (ret < 0) { - NET_ERR("Cannot create http server (%d)", ret); - return ret; - } - - ret = net_app_set_cb(&ctx->app_ctx, NULL, http_received, - http_data_sent, http_closed); - if (ret < 0) { - NET_ERR("Cannot set callbacks (%d)", ret); - goto quit; - } - - init_http_parser(ctx); - - ctx->is_init = true; - return 0; - -quit: - net_app_release(&ctx->app_ctx); - return ret; -} - -int http_server_enable(struct http_ctx *ctx) -{ - int ret; - - NET_ASSERT(ctx); - - net_app_server_enable(&ctx->app_ctx); - - ret = net_app_listen(&ctx->app_ctx); - if (ret < 0) { - NET_ERR("Cannot wait connection (%d)", ret); - return false; - } - - return 0; -} - -int http_server_disable(struct http_ctx *ctx) -{ - NET_ASSERT(ctx); - - net_app_server_disable(&ctx->app_ctx); - - return 0; -} diff --git a/subsys/net/lib/http/http_client.c b/subsys/net/lib/http/http_client.c index 280b392f8f4..a0598cd3e6f 100644 --- a/subsys/net/lib/http/http_client.c +++ b/subsys/net/lib/http/http_client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Intel Corporation + * Copyright (c) 2017 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,183 +10,269 @@ #else #define SYS_LOG_DOMAIN "http/client" #endif +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG #define NET_LOG_ENABLED 1 #endif -#define RX_EXTRA_DEBUG 0 - +#include +#include +#include +#include #include -#include +#include #include -#include -#include - +#include #include -#if defined(CONFIG_HTTPS) -#if defined(MBEDTLS_DEBUG_C) -#include -/* - Debug levels (from ext/lib/crypto/mbedtls/include/mbedtls/debug.h) - * - 0 No debug - * - 1 Error - * - 2 State change - * - 3 Informational - * - 4 Verbose - */ -#define DEBUG_THRESHOLD 0 -#endif -#endif /* CONFIG_HTTPS */ +#include "../../ip/net_private.h" #define BUF_ALLOC_TIMEOUT 100 -#define HTTPS_STARTUP_TIMEOUT K_SECONDS(5) +#define RC_STR(rc) (rc == 0 ? "OK" : "ERROR") -/* HTTP client defines */ #define HTTP_EOF "\r\n\r\n" -#define HTTP_HOST "Host: " -#define HTTP_CONTENT_TYPE "Content-Type: " -#define HTTP_CONT_LEN_SIZE 64 +#define HTTP_HOST "Host" +#define HTTP_CONTENT_TYPE "Content-Type" +#define HTTP_CONTENT_LEN "Content-Length" +#define HTTP_CONT_LEN_SIZE 6 -/* Default network activity timeout in seconds */ -#define HTTP_NETWORK_TIMEOUT K_SECONDS(CONFIG_HTTP_CLIENT_NETWORK_TIMEOUT) +int client_reset(struct http_ctx *ctx) +{ + http_parser_init(&ctx->http.parser, HTTP_RESPONSE); -struct waiter { - struct http_client_ctx *ctx; - struct k_sem wait; -}; + memset(ctx->http.rsp.http_status, 0, + sizeof(ctx->http.rsp.http_status)); -int http_request(struct http_client_ctx *ctx, - struct http_client_request *req, - s32_t timeout) + ctx->http.rsp.cl_present = 0; + ctx->http.rsp.content_length = 0; + ctx->http.rsp.processed = 0; + ctx->http.rsp.body_found = 0; + ctx->http.rsp.message_complete = 0; + ctx->http.rsp.body_start = NULL; + + memset(ctx->http.rsp.response_buf, 0, ctx->http.rsp.response_buf_len); + ctx->http.rsp.data_len = 0; + + return 0; +} + +int http_request(struct http_ctx *ctx, struct http_request *req, s32_t timeout, + void *user_data) { const char *method = http_method_str(req->method); - struct net_pkt *pkt; - int ret = -ENOMEM; + int ret; - pkt = net_pkt_get_tx(ctx->tcp.ctx, BUF_ALLOC_TIMEOUT); - if (!pkt) { - return -ENOMEM; + if (ctx->pending) { + net_pkt_unref(ctx->pending); + ctx->pending = NULL; } - if (!net_pkt_append_all(pkt, strlen(method), (u8_t *)method, - BUF_ALLOC_TIMEOUT)) { + ret = http_add_header(ctx, method, user_data); + if (ret < 0) { goto out; } - /* Space after method string. */ - if (!net_pkt_append_all(pkt, 1, (u8_t *)" ", BUF_ALLOC_TIMEOUT)) { + ret = http_add_header(ctx, " ", user_data); + if (ret < 0) { goto out; } - if (!net_pkt_append_all(pkt, strlen(req->url), (u8_t *)req->url, - BUF_ALLOC_TIMEOUT)) { + ret = http_add_header(ctx, req->url, user_data); + if (ret < 0) { goto out; } - if (!net_pkt_append_all(pkt, strlen(req->protocol), - (u8_t *)req->protocol, BUF_ALLOC_TIMEOUT)) { + ret = http_add_header(ctx, req->protocol, user_data); + if (ret < 0) { + goto out; + } + + ret = http_add_header(ctx, HTTP_CRLF, user_data); + if (ret < 0) { goto out; } if (req->host) { - if (!net_pkt_append_all(pkt, strlen(HTTP_HOST), - (u8_t *)HTTP_HOST, - BUF_ALLOC_TIMEOUT)) { - goto out; - } - - if (!net_pkt_append_all(pkt, strlen(req->host), - (u8_t *)req->host, - BUF_ALLOC_TIMEOUT)) { - goto out; - } - - if (!net_pkt_append_all(pkt, strlen(HTTP_CRLF), - (u8_t *)HTTP_CRLF, - BUF_ALLOC_TIMEOUT)) { + ret = http_add_header_field(ctx, HTTP_HOST, req->host, + user_data); + if (ret < 0) { goto out; } } if (req->header_fields) { - if (!net_pkt_append_all(pkt, strlen(req->header_fields), - (u8_t *)req->header_fields, - BUF_ALLOC_TIMEOUT)) { + ret = http_add_header(ctx, req->header_fields, user_data); + if (ret < 0) { goto out; } } if (req->content_type_value) { - if (!net_pkt_append_all(pkt, strlen(HTTP_CONTENT_TYPE), - (u8_t *)HTTP_CONTENT_TYPE, - BUF_ALLOC_TIMEOUT)) { - goto out; - } - - if (!net_pkt_append_all(pkt, strlen(req->content_type_value), - (u8_t *)req->content_type_value, - BUF_ALLOC_TIMEOUT)) { + ret = http_add_header_field(ctx, HTTP_CONTENT_TYPE, + req->content_type_value, + user_data); + if (ret < 0) { goto out; } } if (req->payload && req->payload_size) { char content_len_str[HTTP_CONT_LEN_SIZE]; + int i; ret = snprintk(content_len_str, HTTP_CONT_LEN_SIZE, - HTTP_CRLF "Content-Length: %u" - HTTP_CRLF HTTP_CRLF, - req->payload_size); + "%u", req->payload_size); if (ret <= 0 || ret >= HTTP_CONT_LEN_SIZE) { ret = -ENOMEM; goto out; } - if (!net_pkt_append_all(pkt, ret, (u8_t *)content_len_str, - BUF_ALLOC_TIMEOUT)) { - ret = -ENOMEM; + ret = http_add_header_field(ctx, HTTP_CONTENT_LEN, + content_len_str, user_data); + if (ret < 0) { goto out; } - if (!net_pkt_append_all(pkt, req->payload_size, - (u8_t *)req->payload, - BUF_ALLOC_TIMEOUT)) { - ret = -ENOMEM; + ret = http_add_header(ctx, HTTP_CRLF, user_data); + if (ret < 0) { goto out; } + + for (i = 0; i < req->payload_size;) { + ret = http_send_chunk(ctx, + req->payload + i, + req->payload_size - i, + user_data); + if (ret < 0) { + NET_ERR("Cannot send data to peer (%d)", ret); + return ret; + } + + i += ret; + } } else { - if (!net_pkt_append_all(pkt, strlen(HTTP_EOF), - (u8_t *)HTTP_EOF, - BUF_ALLOC_TIMEOUT)) { + ret = http_add_header(ctx, HTTP_EOF, user_data); + if (ret < 0) { goto out; } } -#if defined(CONFIG_NET_IPV6) - if (net_pkt_family(pkt) == AF_INET6) { - net_pkt_set_appdatalen(pkt, net_pkt_get_len(pkt) - - net_pkt_ip_hdr_len(pkt) - - net_pkt_ipv6_ext_opt_len(pkt)); - } else -#endif - { - net_pkt_set_appdatalen(pkt, net_pkt_get_len(pkt) - - net_pkt_ip_hdr_len(pkt)); - } - - ret = ctx->tcp.send_data(pkt, NULL, timeout, NULL, ctx); - if (ret == 0) { - return 0; - } + http_send_flush(ctx, user_data); out: - net_pkt_unref(pkt); + if (ctx->pending) { + net_pkt_unref(ctx->pending); + ctx->pending = NULL; + } return ret; } +#if defined(CONFIG_NET_DEBUG_HTTP) +static void sprint_addr(char *buf, int len, + sa_family_t family, + struct sockaddr *addr) +{ + if (family == AF_INET6) { + net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf, len); + } else if (family == AF_INET) { + net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf, len); + } else { + NET_DBG("Invalid protocol family"); + } +} +#endif + +static inline void print_info(struct http_ctx *ctx, + enum http_method method) +{ +#if defined(CONFIG_NET_DEBUG_HTTP) + char local[NET_IPV6_ADDR_LEN]; + char remote[NET_IPV6_ADDR_LEN]; + + sprint_addr(local, NET_IPV6_ADDR_LEN, + ctx->app_ctx.default_ctx->local.sa_family, + &ctx->app_ctx.default_ctx->local); + + sprint_addr(remote, NET_IPV6_ADDR_LEN, + ctx->app_ctx.default_ctx->remote.sa_family, + &ctx->app_ctx.default_ctx->remote); + + NET_DBG("HTTP %s (%s) %s -> %s port %d", + http_method_str(method), ctx->http.req.host, local, remote, + ntohs(net_sin(&ctx->app_ctx.default_ctx->remote)->sin_port)); +#endif +} + +int http_client_send_req(struct http_ctx *ctx, + struct http_request *req, + http_response_cb_t cb, + u8_t *response_buf, + size_t response_buf_len, + void *user_data, + s32_t timeout) +{ + int ret; + + if (!response_buf || response_buf_len == 0) { + return -EINVAL; + } + + ctx->http.rsp.response_buf = response_buf; + ctx->http.rsp.response_buf_len = response_buf_len; + + client_reset(ctx); + + if (!req->host) { + req->host = ctx->server; + } + + ctx->http.req.host = req->host; + ctx->http.req.method = req->method; + ctx->http.req.user_data = user_data; + + ctx->http.rsp.cb = cb; + + ret = net_app_connect(&ctx->app_ctx, timeout); + if (ret < 0) { + NET_DBG("Cannot connect to server (%d)", ret); + return ret; + } + + /* We might wait longer than timeout if the first connection + * establishment takes long time (like with HTTPS) + */ + if (k_sem_take(&ctx->http.connect_wait, timeout)) { + NET_DBG("Connection timed out"); + ret = -ETIMEDOUT; + goto out; + } + + print_info(ctx, ctx->http.req.method); + + ret = http_request(ctx, req, timeout, user_data); + if (ret < 0) { + NET_DBG("Send error (%d)", ret); + goto out; + } + + if (timeout != 0 && k_sem_take(&ctx->http.req.wait, timeout)) { + ret = -ETIMEDOUT; + goto out; + } + + if (timeout == 0) { + return -EINPROGRESS; + } + + return 0; + +out: + return ret; +} + static void print_header_field(size_t len, const char *str) { #if defined(CONFIG_NET_DEBUG_HTTP) @@ -217,15 +303,16 @@ static int on_url(struct http_parser *parser, const char *at, size_t length) static int on_status(struct http_parser *parser, const char *at, size_t length) { - struct http_client_ctx *ctx; u16_t len; + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); - ctx = CONTAINER_OF(parser, struct http_client_ctx, parser); - len = min(length, sizeof(ctx->rsp.http_status) - 1); - memcpy(ctx->rsp.http_status, at, len); - ctx->rsp.http_status[len] = 0; + len = min(length, sizeof(ctx->http.rsp.http_status) - 1); + memcpy(ctx->http.rsp.http_status, at, len); + ctx->http.rsp.http_status[len] = 0; - NET_DBG("HTTP response status %s", ctx->rsp.http_status); + NET_DBG("HTTP response status %s", ctx->http.rsp.http_status); return 0; } @@ -233,15 +320,15 @@ static int on_status(struct http_parser *parser, const char *at, size_t length) static int on_header_field(struct http_parser *parser, const char *at, size_t length) { - char *content_len = "Content-Length"; - struct http_client_ctx *ctx; + const char *content_len = HTTP_CONTENT_LEN; + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); u16_t len; - ctx = CONTAINER_OF(parser, struct http_client_ctx, parser); - len = strlen(content_len); if (length >= len && memcmp(at, content_len, len) == 0) { - ctx->rsp.cl_present = true; + ctx->http.rsp.cl_present = true; } print_header_field(length, at); @@ -254,26 +341,27 @@ static int on_header_field(struct http_parser *parser, const char *at, static int on_header_value(struct http_parser *parser, const char *at, size_t length) { - struct http_client_ctx *ctx; char str[MAX_NUM_DIGITS]; + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); - ctx = CONTAINER_OF(parser, struct http_client_ctx, parser); - - if (ctx->rsp.cl_present) { + if (ctx->http.rsp.cl_present) { if (length <= MAX_NUM_DIGITS - 1) { long int num; memcpy(str, at, length); str[length] = 0; + num = strtol(str, NULL, 10); if (num == LONG_MIN || num == LONG_MAX) { return -EINVAL; } - ctx->rsp.content_length = num; + ctx->http.rsp.content_length = num; } - ctx->rsp.cl_present = false; + ctx->http.rsp.cl_present = false; } print_header_field(length, at); @@ -283,35 +371,34 @@ static int on_header_value(struct http_parser *parser, const char *at, static int on_body(struct http_parser *parser, const char *at, size_t length) { - struct http_client_ctx *ctx = CONTAINER_OF(parser, - struct http_client_ctx, - parser); + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); - ctx->rsp.body_found = 1; - ctx->rsp.processed += length; + ctx->http.rsp.body_found = 1; + ctx->http.rsp.processed += length; - NET_DBG("Processed %zd length %zd", ctx->rsp.processed, length); + NET_DBG("Processed %zd length %zd", ctx->http.rsp.processed, length); - if (!ctx->rsp.body_start && - (u8_t *)at != (u8_t *)ctx->rsp.response_buf) { - /* This fragment contains the start of the body */ - ctx->rsp.body_start = (u8_t *)at; + if (!ctx->http.rsp.body_start && + (u8_t *)at != (u8_t *)ctx->http.rsp.response_buf) { + ctx->http.rsp.body_start = (u8_t *)at; } - if (ctx->rsp.cb) { + if (ctx->http.rsp.cb) { NET_DBG("Calling callback for partitioned %zd len data", - ctx->rsp.data_len); + ctx->http.rsp.data_len); - ctx->rsp.cb(ctx, - ctx->rsp.response_buf, - ctx->rsp.response_buf_len, - ctx->rsp.data_len, - HTTP_DATA_MORE, - ctx->req.user_data); + ctx->http.rsp.cb(ctx, + ctx->http.rsp.response_buf, + ctx->http.rsp.response_buf_len, + ctx->http.rsp.data_len, + HTTP_DATA_MORE, + ctx->http.req.user_data); /* Re-use the result buffer and start to fill it again */ - ctx->rsp.data_len = 0; - ctx->rsp.body_start = NULL; + ctx->http.rsp.data_len = 0; + ctx->http.rsp.body_start = NULL; } return 0; @@ -319,9 +406,9 @@ static int on_body(struct http_parser *parser, const char *at, size_t length) static int on_headers_complete(struct http_parser *parser) { - struct http_client_ctx *ctx = CONTAINER_OF(parser, - struct http_client_ctx, - parser); + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); if (parser->status_code >= 500 && parser->status_code < 600) { NET_DBG("Status %d, skipping body", parser->status_code); @@ -329,8 +416,9 @@ static int on_headers_complete(struct http_parser *parser) return 1; } - if ((ctx->req.method == HTTP_HEAD || ctx->req.method == HTTP_OPTIONS) - && ctx->rsp.content_length > 0) { + if ((ctx->http.req.method == HTTP_HEAD || + ctx->http.req.method == HTTP_OPTIONS) + && ctx->http.rsp.content_length > 0) { NET_DBG("No body expected"); return 1; } @@ -343,12 +431,12 @@ static int on_headers_complete(struct http_parser *parser) static int on_message_begin(struct http_parser *parser) { #if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) - struct http_client_ctx *ctx = CONTAINER_OF(parser, - struct http_client_ctx, - parser); + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); NET_DBG("-- HTTP %s response (headers) --", - http_method_str(ctx->req.method)); + http_method_str(ctx->http.req.method)); #else ARG_UNUSED(parser); #endif @@ -357,24 +445,25 @@ static int on_message_begin(struct http_parser *parser) static int on_message_complete(struct http_parser *parser) { - struct http_client_ctx *ctx = CONTAINER_OF(parser, - struct http_client_ctx, - parser); + struct http_ctx *ctx = CONTAINER_OF(parser, + struct http_ctx, + http.parser); NET_DBG("-- HTTP %s response (complete) --", - http_method_str(ctx->req.method)); + http_method_str(ctx->http.req.method)); - if (ctx->rsp.cb) { - ctx->rsp.cb(ctx, - ctx->rsp.response_buf, - ctx->rsp.response_buf_len, - ctx->rsp.data_len, - HTTP_DATA_FINAL, - ctx->req.user_data); + if (ctx->http.rsp.cb) { + ctx->http.rsp.cb(ctx, + ctx->http.rsp.response_buf, + ctx->http.rsp.response_buf_len, + ctx->http.rsp.data_len, + HTTP_DATA_FINAL, + ctx->http.req.user_data); } - ctx->rsp.message_complete = 1; - k_sem_give(&ctx->req.wait); + ctx->http.rsp.message_complete = 1; + + k_sem_give(&ctx->http.req.wait); return 0; } @@ -393,1388 +482,257 @@ static int on_chunk_complete(struct http_parser *parser) return 0; } -static void http_receive_cb(struct http_client_ctx *ctx, - struct net_pkt *pkt) +static void http_received(struct net_app_ctx *app_ctx, + struct net_pkt *pkt, + int status, + void *user_data) { - size_t start = ctx->rsp.data_len; - size_t len = 0; - struct net_buf *frag; - int header_len; + struct http_ctx *ctx = user_data; + size_t start = ctx->http.rsp.data_len; + u16_t len = 0; + struct net_buf *frag, *prev_frag = NULL; + size_t recv_len; + size_t pkt_len; - if (!pkt) { - return; + recv_len = net_pkt_appdatalen(pkt); + if (recv_len == 0) { + /* don't print info about zero-length app data buffers */ + goto quit; + } + + if (status) { + NET_DBG("[%p] Status %d <%s>", ctx, status, RC_STR(status)); + goto out; } /* Get rid of possible IP headers in the first fragment. */ frag = pkt->frags; - header_len = net_pkt_appdata(pkt) - frag->data; + pkt_len = net_pkt_get_len(pkt); - NET_DBG("Received %d bytes data", net_pkt_appdatalen(pkt)); + if (recv_len < pkt_len) { + net_buf_pull(frag, pkt_len - recv_len); + net_pkt_set_appdata(pkt, frag->data); + } - /* After this pull, the frag->data points directly to application data. - */ - net_buf_pull(frag, header_len); + NET_DBG("[%p] Received %zd bytes http data", ctx, recv_len); while (frag) { /* If this fragment cannot be copied to result buf, * then parse what we have which will cause the callback to be * called in function on_body(), and continue copying. */ - if (ctx->rsp.data_len + frag->len > ctx->rsp.response_buf_len) { + if ((ctx->http.rsp.data_len + frag->len) > + ctx->http.rsp.response_buf_len) { /* If the caller has not supplied a callback, then - * we cannot really continue if the response buffer + * we cannot really continue if the request buffer * overflows. Set the data_len to mark how many bytes * should be needed in the response_buf. */ - if (!ctx->rsp.cb) { - ctx->rsp.data_len = net_pkt_get_len(pkt); + if (!ctx->cb.recv) { + ctx->http.rsp.data_len = recv_len; goto out; } - http_parser_execute(&ctx->parser, - &ctx->settings, - ctx->rsp.response_buf + start, + http_parser_execute(&ctx->http.parser, + &ctx->http.parser_settings, + ctx->http.rsp.response_buf + start, len); - ctx->rsp.data_len = 0; + ctx->http.rsp.data_len = 0; len = 0; start = 0; } - memcpy(ctx->rsp.response_buf + ctx->rsp.data_len, + memcpy(ctx->http.rsp.response_buf + ctx->http.rsp.data_len, frag->data, frag->len); - ctx->rsp.data_len += frag->len; + ctx->http.rsp.data_len += frag->len; len += frag->len; + + prev_frag = frag; frag = frag->frags; + pkt->frags = frag; + + prev_frag->frags = NULL; + net_pkt_frag_unref(prev_frag); } out: - /* The parser's error can be catched outside, reading the - * http_errno struct member - */ - http_parser_execute(&ctx->parser, &ctx->settings, - ctx->rsp.response_buf + start, len); + http_parser_execute(&ctx->http.parser, + &ctx->http.parser_settings, + ctx->http.rsp.response_buf + start, + len); net_pkt_unref(pkt); + return; + +quit: + http_parser_init(&ctx->http.parser, HTTP_RESPONSE); + ctx->http.rsp.data_len = 0; + net_pkt_unref(pkt); } -int client_reset(struct http_client_ctx *ctx) +static void http_data_sent(struct net_app_ctx *app_ctx, + int status, + void *user_data_send, + void *user_data) { - http_parser_init(&ctx->parser, HTTP_RESPONSE); + struct http_ctx *ctx = user_data; - memset(ctx->rsp.http_status, 0, sizeof(ctx->rsp.http_status)); - - ctx->rsp.cl_present = 0; - ctx->rsp.content_length = 0; - ctx->rsp.processed = 0; - ctx->rsp.body_found = 0; - ctx->rsp.message_complete = 0; - ctx->rsp.body_start = NULL; - - memset(ctx->rsp.response_buf, 0, ctx->rsp.response_buf_len); - ctx->rsp.data_len = 0; - - return 0; -} - -static void tcp_disconnect(struct http_client_ctx *ctx) -{ - if (ctx->tcp.ctx) { - net_context_put(ctx->tcp.ctx); - ctx->tcp.ctx = NULL; - } -} - -static void recv_cb(struct net_context *net_ctx, struct net_pkt *pkt, - int status, void *data) -{ - struct http_client_ctx *ctx = data; - - ARG_UNUSED(net_ctx); - - if (status) { + if (!user_data_send) { + /* This is the token field in the net_context_send(). + * If this is not set, then it is TCP ACK messages + * that are generated by the stack. We just ignore those. + */ return; } - if (!pkt || net_pkt_appdatalen(pkt) == 0) { - /* - * This block most likely handles a TCP_FIN message. - * (this means the connection is now closed) - * If we get here, and rsp.message_complete is still 0 - * this means the HTTP client is still waiting to parse a - * response body. - * This will will never happen now. Instead of generating - * an ETIMEDOUT error in the future, let's unlock the - * req.wait semaphore and let the app deal with whatever - * data was parsed in the header (IE: http status, etc). - */ - if (ctx->rsp.message_complete == 0) { - k_sem_give(&ctx->req.wait); - } - - goto out; + if (ctx->cb.send) { + ctx->cb.send(ctx, status, user_data_send, ctx->user_data); } +} - /* receive_cb must take ownership of the received packet */ - if (ctx->tcp.receive_cb) { - ctx->tcp.receive_cb(ctx, pkt); +static void http_connected(struct net_app_ctx *app_ctx, + int status, + void *user_data) +{ + struct http_ctx *ctx = user_data; + + if (status < 0) { return; } -out: - if (pkt) { - net_pkt_unref(pkt); + if (ctx->cb.connect) { + ctx->cb.connect(ctx, HTTP_CONNECTION, ctx->user_data); } + + if (ctx->is_connected) { + return; + } + + ctx->is_connected = true; + + k_sem_give(&ctx->http.connect_wait); } -static int get_local_addr(struct http_client_ctx *ctx) +static void http_closed(struct net_app_ctx *app_ctx, + int status, + void *user_data) { - if (ctx->tcp.local.sa_family == AF_INET6) { -#if defined(CONFIG_NET_IPV6) - struct in6_addr *dst = &net_sin6(&ctx->tcp.remote)->sin6_addr; + struct http_ctx *ctx = user_data; - net_ipaddr_copy(&net_sin6(&ctx->tcp.local)->sin6_addr, - net_if_ipv6_select_src_addr(NULL, dst)); -#else - return -EPFNOSUPPORT; -#endif - } else if (ctx->tcp.local.sa_family == AF_INET) { -#if defined(CONFIG_NET_IPV4) - struct net_if *iface = net_if_get_default(); - - /* For IPv4 we take the first address in the interface */ - net_ipaddr_copy(&net_sin(&ctx->tcp.local)->sin_addr, - &iface->ipv4.unicast[0].address.in_addr); -#else - return -EPFNOSUPPORT; -#endif - } - - return 0; -} - -static int tcp_connect(struct http_client_ctx *ctx) -{ - socklen_t addrlen = sizeof(struct sockaddr_in); - int ret; - - if (ctx->tcp.ctx && net_context_is_used(ctx->tcp.ctx) && - net_context_get_state(ctx->tcp.ctx) == NET_CONTEXT_CONNECTED) { - /* If we are already connected, then just return */ - return -EALREADY; - } - - if (ctx->tcp.remote.sa_family == AF_INET6) { - addrlen = sizeof(struct sockaddr_in6); - - /* If we are reconnecting, then make sure the source port - * is re-calculated so that the peer will not get confused - * which connection the connection is related to. - * This was seen in Linux which dropped packets when the same - * source port was for a new connection after the old connection - * was terminated. - */ - net_sin6(&ctx->tcp.local)->sin6_port = 0; - } else { - net_sin(&ctx->tcp.local)->sin_port = 0; - } - - ret = get_local_addr(ctx); - if (ret < 0) { - NET_DBG("Cannot get local address (%d)", ret); - return ret; - } - - ret = net_context_get(ctx->tcp.remote.sa_family, SOCK_STREAM, - IPPROTO_TCP, &ctx->tcp.ctx); - if (ret) { - NET_DBG("Get context error (%d)", ret); - return ret; - } - - net_context_setup_pools(ctx->tcp.ctx, ctx->tx_slab, ctx->data_pool); - - ret = net_context_bind(ctx->tcp.ctx, &ctx->tcp.local, - addrlen); - if (ret) { - NET_DBG("Bind error (%d)", ret); - goto out; - } - - ret = net_context_connect(ctx->tcp.ctx, - &ctx->tcp.remote, addrlen, - NULL, ctx->tcp.timeout, NULL); - if (ret) { - NET_DBG("Connect error (%d)", ret); - goto out; - } - - return net_context_recv(ctx->tcp.ctx, ctx->tcp.recv_cb, K_NO_WAIT, ctx); - -out: - net_context_put(ctx->tcp.ctx); - ctx->tcp.ctx = NULL; - - return ret; -} - -#if defined(CONFIG_NET_DEBUG_HTTP) -static void sprint_addr(char *buf, int len, - sa_family_t family, - struct sockaddr *addr) -{ - if (family == AF_INET6) { - net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf, len); - } else if (family == AF_INET) { - net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf, len); - } else { - NET_DBG("Invalid protocol family"); - } -} -#endif - -static inline void print_info(struct http_client_ctx *ctx, - enum http_method method) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) - char local[NET_IPV6_ADDR_LEN]; - char remote[NET_IPV6_ADDR_LEN]; - - sprint_addr(local, NET_IPV6_ADDR_LEN, ctx->tcp.local.sa_family, - &ctx->tcp.local); - - sprint_addr(remote, NET_IPV6_ADDR_LEN, ctx->tcp.remote.sa_family, - &ctx->tcp.remote); - - NET_DBG("HTTP %s (%s) %s -> %s port %d", - http_method_str(method), ctx->req.host, local, remote, - ntohs(net_sin(&ctx->tcp.remote)->sin_port)); -#endif -} - -#if defined(CONFIG_HTTPS) -#if defined(MBEDTLS_ERROR_C) -#define print_error(fmt, ret) \ - do { \ - char error[64]; \ - \ - mbedtls_strerror(ret, error, sizeof(error)); \ - \ - NET_ERR(fmt " (%s)", -ret, error); \ - } while (0) -#else -#define print_error(fmt, ret) NET_ERR(fmt, -ret) -#endif /* MBEDTLS_ERROR_C */ - -static void ssl_sent(struct net_context *context, - int status, void *token, void *user_data) -{ - struct http_client_ctx *http_ctx = user_data; - - k_sem_give(&http_ctx->https.mbedtls.ssl_ctx.tx_sem); -} - -/* Send encrypted data */ -static int ssl_tx(void *context, const unsigned char *buf, size_t size) -{ - struct http_client_ctx *ctx = context; - struct net_pkt *send_pkt; - int ret, len; - - send_pkt = net_pkt_get_tx(ctx->tcp.ctx, BUF_ALLOC_TIMEOUT); - if (!send_pkt) { - return MBEDTLS_ERR_SSL_ALLOC_FAILED; - } - - ret = net_pkt_append_all(send_pkt, size, (u8_t *)buf, - BUF_ALLOC_TIMEOUT); - if (!ret) { - /* Cannot append data */ - net_pkt_unref(send_pkt); - return MBEDTLS_ERR_SSL_ALLOC_FAILED; - } - - len = size; - - ret = net_context_send(send_pkt, ssl_sent, K_NO_WAIT, NULL, ctx); - if (ret < 0) { - net_pkt_unref(send_pkt); - return ret; - } - - k_sem_take(&ctx->https.mbedtls.ssl_ctx.tx_sem, K_FOREVER); - - return len; -} - -struct rx_fifo_block { - sys_snode_t snode; - struct k_mem_block block; - struct net_pkt *pkt; -}; - -struct tx_fifo_block { - struct k_mem_block block; - struct http_client_request *req; -}; - -/* Receive encrypted data from network. Put that data into fifo - * that will be read by https thread. - */ -static void ssl_received(struct net_context *context, - struct net_pkt *pkt, - int status, - void *user_data) -{ - struct http_client_ctx *http_ctx = user_data; - struct rx_fifo_block *rx_data = NULL; - struct k_mem_block block; - int ret; - - ARG_UNUSED(context); + ARG_UNUSED(app_ctx); ARG_UNUSED(status); - if (pkt && !net_pkt_appdatalen(pkt)) { - net_pkt_unref(pkt); - return; + NET_DBG("[%p] connection closed", ctx); + + ctx->is_connected = false; + + if (ctx->cb.close) { + ctx->cb.close(ctx, 0, ctx->user_data); } - - ret = k_mem_pool_alloc(http_ctx->https.pool, &block, - sizeof(struct rx_fifo_block), - BUF_ALLOC_TIMEOUT); - if (ret < 0) { - if (pkt) { - net_pkt_unref(pkt); - } - - return; - } - - rx_data = block.data; - rx_data->pkt = pkt; - - /* For freeing memory later */ - memcpy(&rx_data->block, &block, sizeof(struct k_mem_block)); - - k_fifo_put(&http_ctx->https.mbedtls.ssl_ctx.rx_fifo, (void *)rx_data); - - /* Let the ssl_rx() to run */ - k_yield(); } -int ssl_rx(void *context, unsigned char *buf, size_t size) -{ - struct http_client_ctx *ctx = context; - struct rx_fifo_block *rx_data; - u16_t read_bytes; - u8_t *ptr; - int pos; - int len; - int ret = 0; - - if (!ctx->https.mbedtls.ssl_ctx.frag) { - rx_data = k_fifo_get(&ctx->https.mbedtls.ssl_ctx.rx_fifo, - K_FOREVER); - if (!rx_data || !rx_data->pkt) { - NET_DBG("Closing %p connection", ctx); - - if (rx_data) { - k_mem_pool_free(&rx_data->block); - } - - return MBEDTLS_ERR_SSL_CONN_EOF; - } - - ctx->https.mbedtls.ssl_ctx.rx_pkt = rx_data->pkt; - - k_mem_pool_free(&rx_data->block); - - read_bytes = net_pkt_appdatalen( - ctx->https.mbedtls.ssl_ctx.rx_pkt); - - ctx->https.mbedtls.ssl_ctx.remaining = read_bytes; - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.rx_pkt->frags; - - ptr = net_pkt_appdata(ctx->https.mbedtls.ssl_ctx.rx_pkt); - len = ptr - ctx->https.mbedtls.ssl_ctx.frag->data; - - if (len > ctx->https.mbedtls.ssl_ctx.frag->size) { - NET_ERR("Buf overflow (%d > %u)", len, - ctx->https.mbedtls.ssl_ctx.frag->size); - return -EINVAL; - } - - /* This will get rid of IP header */ - net_buf_pull(ctx->https.mbedtls.ssl_ctx.frag, len); - } else { - read_bytes = ctx->https.mbedtls.ssl_ctx.remaining; - ptr = ctx->https.mbedtls.ssl_ctx.frag->data; - } - - len = ctx->https.mbedtls.ssl_ctx.frag->len; - pos = 0; - if (read_bytes > size) { - while (ctx->https.mbedtls.ssl_ctx.frag) { - read_bytes = len < (size - pos) ? len : (size - pos); - -#if RX_EXTRA_DEBUG == 1 - NET_DBG("Copying %d bytes", read_bytes); -#endif - - memcpy(buf + pos, ptr, read_bytes); - - pos += read_bytes; - if (pos < size) { - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.frag->frags; - ptr = ctx->https.mbedtls.ssl_ctx.frag->data; - len = ctx->https.mbedtls.ssl_ctx.frag->len; - } else { - if (read_bytes == len) { - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.frag->frags; - } else { - net_buf_pull( - ctx->https.mbedtls.ssl_ctx.frag, - read_bytes); - } - - ctx->https.mbedtls.ssl_ctx.remaining -= size; - return size; - } - } - } else { - while (ctx->https.mbedtls.ssl_ctx.frag) { -#if RX_EXTRA_DEBUG == 1 - NET_DBG("Copying all %d bytes", len); -#endif - - memcpy(buf + pos, ptr, len); - - pos += len; - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.frag->frags; - if (!ctx->https.mbedtls.ssl_ctx.frag) { - break; - } - - ptr = ctx->https.mbedtls.ssl_ctx.frag->data; - len = ctx->https.mbedtls.ssl_ctx.frag->len; - } - - net_pkt_unref(ctx->https.mbedtls.ssl_ctx.rx_pkt); - ctx->https.mbedtls.ssl_ctx.rx_pkt = NULL; - ctx->https.mbedtls.ssl_ctx.frag = NULL; - ctx->https.mbedtls.ssl_ctx.remaining = 0; - - if (read_bytes != pos) { - return -EIO; - } - - ret = read_bytes; - } - - return ret; -} - -#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_HTTP) -static void my_debug(void *ctx, int level, - const char *file, int line, const char *str) -{ - const char *p, *basename; - int len; - - ARG_UNUSED(ctx); - - /* Extract basename from file */ - for (p = basename = file; *p != '\0'; p++) { - if (*p == '/' || *p == '\\') { - basename = p + 1; - } - - } - - /* Avoid printing double newlines */ - len = strlen(str); - if (str[len - 1] == '\n') { - ((char *)str)[len - 1] = '\0'; - } - - NET_DBG("%s:%04d: |%d| %s", basename, line, level, str); -} -#endif /* MBEDTLS_DEBUG_C && CONFIG_NET_DEBUG_HTTP */ - -static int entropy_source(void *data, unsigned char *output, size_t len, - size_t *olen) -{ - u32_t seed; - - ARG_UNUSED(data); - - seed = sys_rand32_get(); - - if (len > sizeof(seed)) { - len = sizeof(seed); - } - - memcpy(output, &seed, len); - - *olen = len; - return 0; -} - -static int https_init(struct http_client_ctx *ctx) -{ - int ret = 0; - - k_sem_init(&ctx->https.mbedtls.ssl_ctx.tx_sem, 0, UINT_MAX); - k_fifo_init(&ctx->https.mbedtls.ssl_ctx.rx_fifo); - k_fifo_init(&ctx->https.mbedtls.ssl_ctx.tx_fifo); - - mbedtls_platform_set_printf(printk); - - /* Coverity tells in CID 170746 that the mbedtls_ssl_init() - * is overwriting the ssl struct. This looks like false positive as - * the struct is initialized with proper size. - */ - mbedtls_ssl_init(&ctx->https.mbedtls.ssl); - - /* Coverity tells in CID 170745 that the mbedtls_ssl_config_init() - * is overwriting the conf struct. This looks like false positive as - * the struct is initialized with proper size. - */ - mbedtls_ssl_config_init(&ctx->https.mbedtls.conf); - - mbedtls_entropy_init(&ctx->https.mbedtls.entropy); - mbedtls_ctr_drbg_init(&ctx->https.mbedtls.ctr_drbg); - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - mbedtls_x509_crt_init(&ctx->https.mbedtls.ca_cert); -#endif - -#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_HTTP) - mbedtls_debug_set_threshold(DEBUG_THRESHOLD); - mbedtls_ssl_conf_dbg(&ctx->https.mbedtls.conf, my_debug, NULL); -#endif - - /* Seed the RNG */ - mbedtls_entropy_add_source(&ctx->https.mbedtls.entropy, - ctx->https.mbedtls.entropy_src_cb, - NULL, - MBEDTLS_ENTROPY_MAX_GATHER, - MBEDTLS_ENTROPY_SOURCE_STRONG); - - ret = mbedtls_ctr_drbg_seed( - &ctx->https.mbedtls.ctr_drbg, - mbedtls_entropy_func, - &ctx->https.mbedtls.entropy, - (const unsigned char *)ctx->https.mbedtls.personalization_data, - ctx->https.mbedtls.personalization_data_len); - if (ret != 0) { - print_error("mbedtls_ctr_drbg_seed returned -0x%x", ret); - goto exit; - } - - /* Setup SSL defaults etc. */ - ret = mbedtls_ssl_config_defaults(&ctx->https.mbedtls.conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - if (ret != 0) { - print_error("mbedtls_ssl_config_defaults returned -0x%x", ret); - goto exit; - } - - mbedtls_ssl_conf_rng(&ctx->https.mbedtls.conf, - mbedtls_ctr_drbg_random, - &ctx->https.mbedtls.ctr_drbg); - - /* Load the certificates and other related stuff. This needs to be done - * by the user so we call a callback that user must have provided. - */ - ret = ctx->https.mbedtls.cert_cb(ctx, &ctx->https.mbedtls.ca_cert); - if (ret != 0) { - goto exit; - } - - ret = mbedtls_ssl_setup(&ctx->https.mbedtls.ssl, - &ctx->https.mbedtls.conf); - if (ret != 0) { - NET_ERR("mbedtls_ssl_setup returned -0x%x", ret); - goto exit; - } - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - if (ctx->https.cert_host) { - ret = mbedtls_ssl_set_hostname(&ctx->https.mbedtls.ssl, - ctx->https.cert_host); - if (ret != 0) { - print_error("mbedtls_ssl_set_hostname returned -0x%x", - ret); - goto exit; - } - } -#endif - - NET_DBG("SSL setup done"); - - /* The HTTPS thread is started do initiate HTTPS handshake etc when - * the first HTTP request is being done. - */ - -exit: - return ret; -} - -static void https_handler(struct http_client_ctx *ctx, - struct k_sem *startup_sync) -{ - struct tx_fifo_block *tx_data; - struct http_client_request req; - size_t len; - int ret; - - /* First mbedtls specific initialization */ - ret = https_init(ctx); - - k_sem_give(startup_sync); - - if (ret < 0) { - return; - } - -reset: - http_parser_init(&ctx->parser, HTTP_RESPONSE); - ctx->rsp.data_len = 0; - - /* Wait that the sender sends the data, and the peer to respond to. - */ - tx_data = k_fifo_get(&ctx->https.mbedtls.ssl_ctx.tx_fifo, K_FOREVER); - if (tx_data) { - /* Because the req pointer might disappear as it is controlled - * by application, copy the data here. - */ - memcpy(&req, tx_data->req, sizeof(req)); - } else { - NET_ASSERT(tx_data); - goto reset; - } - - print_info(ctx, ctx->req.method); - - /* If the connection is not active, then re-connect */ - ret = tcp_connect(ctx); - if (ret < 0 && ret != -EALREADY) { - k_sem_give(&ctx->req.wait); - goto reset; - } - - mbedtls_ssl_session_reset(&ctx->https.mbedtls.ssl); - mbedtls_ssl_set_bio(&ctx->https.mbedtls.ssl, ctx, ssl_tx, - ssl_rx, NULL); - - /* SSL handshake. The ssl_rx() function will be called next by - * mbedtls library. The ssl_rx() will block and wait that data is - * received by ssl_received() and passed to it via fifo. After - * receiving the data, this function will then proceed with secure - * connection establishment. - */ - /* Waiting SSL handshake */ - do { - ret = mbedtls_ssl_handshake(&ctx->https.mbedtls.ssl); - if (ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - if (ret == MBEDTLS_ERR_SSL_CONN_EOF) { - goto close; - } - - if (ret < 0) { - print_error("mbedtls_ssl_handshake returned " - "-0x%x", ret); - goto close; - } - } - } while (ret != 0); - - ret = http_request(ctx, &req, BUF_ALLOC_TIMEOUT); - - k_mem_pool_free(&tx_data->block); - - if (ret < 0) { - NET_DBG("Send error (%d)", ret); - goto close; - } - - NET_DBG("Read HTTPS response"); - - do { - len = ctx->rsp.response_buf_len - 1; - memset(ctx->rsp.response_buf, 0, ctx->rsp.response_buf_len); - - ret = mbedtls_ssl_read(&ctx->https.mbedtls.ssl, - ctx->rsp.response_buf, len); - if (ret == 0) { - goto close; - } - - if (ret == MBEDTLS_ERR_SSL_WANT_READ || - ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - continue; - } - - if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { - NET_DBG("Connection was closed gracefully"); - goto close; - } - - if (ret == MBEDTLS_ERR_NET_CONN_RESET) { - NET_DBG("Connection was reset by peer"); - goto close; - } - - if (ret == -EIO) { - NET_DBG("Response received, waiting another ctx %p", - ctx); - goto next; - } - - if (ret < 0) { - print_error("mbedtls_ssl_read returned -0x%x", ret); - goto close; - } - - /* The data_len will count how many bytes we have read, - * this value is passed to user supplied response callback - * by on_body() and on_message_complete() functions. - */ - ctx->rsp.data_len += ret; - - ret = http_parser_execute(&ctx->parser, - &ctx->settings, - ctx->rsp.response_buf, - ret); - if (!ret) { - goto close; - } - - ctx->rsp.data_len = 0; - - if (ret > 0) { - /* Get more data */ - ret = MBEDTLS_ERR_SSL_WANT_READ; - } - } while (ret < 0); - -close: - /* If there is any pending data that have not been processed yet, - * we need to free it here. - */ - if (ctx->https.mbedtls.ssl_ctx.rx_pkt) { - net_pkt_unref(ctx->https.mbedtls.ssl_ctx.rx_pkt); - ctx->https.mbedtls.ssl_ctx.rx_pkt = NULL; - ctx->https.mbedtls.ssl_ctx.frag = NULL; - } - - NET_DBG("Resetting HTTPS connection %p", ctx); - - tcp_disconnect(ctx); - -next: - mbedtls_ssl_close_notify(&ctx->https.mbedtls.ssl); - - goto reset; -} - -static void https_shutdown(struct http_client_ctx *ctx) -{ - if (!ctx->https.tid) { - return; - } - - /* Empty the fifo just in case there is any received packets - * still there. - */ - while (1) { - struct rx_fifo_block *rx_data; - - rx_data = k_fifo_get(&ctx->https.mbedtls.ssl_ctx.rx_fifo, - K_NO_WAIT); - if (!rx_data) { - break; - } - - net_pkt_unref(rx_data->pkt); - - k_mem_pool_free(&rx_data->block); - } - - k_fifo_cancel_wait(&ctx->https.mbedtls.ssl_ctx.rx_fifo); - - /* Let the ssl_rx() run if there is anything there waiting */ - k_yield(); - - mbedtls_ssl_close_notify(&ctx->https.mbedtls.ssl); - mbedtls_ssl_free(&ctx->https.mbedtls.ssl); - mbedtls_ssl_config_free(&ctx->https.mbedtls.conf); - mbedtls_ctr_drbg_free(&ctx->https.mbedtls.ctr_drbg); - mbedtls_entropy_free(&ctx->https.mbedtls.entropy); - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - mbedtls_x509_crt_free(&ctx->https.mbedtls.ca_cert); -#endif - - tcp_disconnect(ctx); - - NET_DBG("HTTPS thread %p stopped for %p", ctx->https.tid, ctx); - - k_thread_abort(ctx->https.tid); - ctx->https.tid = 0; -} - -static int start_https(struct http_client_ctx *ctx) -{ - struct k_sem startup_sync; - - /* Start the thread that handles HTTPS traffic. */ - if (ctx->https.tid) { - return -EALREADY; - } - - NET_DBG("Starting HTTPS thread for %p", ctx); - - k_sem_init(&startup_sync, 0, 1); - - ctx->https.tid = k_thread_create(&ctx->https.thread, - ctx->https.stack, - ctx->https.stack_size, - (k_thread_entry_t)https_handler, - ctx, &startup_sync, 0, - K_PRIO_COOP(7), 0, 0); - - /* Wait until we know that the HTTPS thread startup was ok */ - if (k_sem_take(&startup_sync, HTTPS_STARTUP_TIMEOUT) < 0) { - https_shutdown(ctx); - return -ECANCELED; - } - - NET_DBG("HTTPS thread %p started for %p", ctx->https.tid, ctx); - - return 0; -} -#else -#define start_https(...) 0 -#endif /* CONFIG_HTTPS */ - -int http_client_send_req(struct http_client_ctx *ctx, - struct http_client_request *req, - http_response_cb_t cb, - u8_t *response_buf, - size_t response_buf_len, - void *user_data, - s32_t timeout) +int http_client_init(struct http_ctx *ctx, + const char *server, + u16_t server_port, + struct sockaddr *server_addr, + s32_t timeout) { int ret; - if (!response_buf || response_buf_len == 0) { + memset(ctx, 0, sizeof(*ctx)); + + ret = net_app_init_tcp_client(&ctx->app_ctx, + NULL, /* use any local address */ + server_addr, + server, + server_port, + timeout, + ctx); + if (ret < 0) { + NET_DBG("Cannot init HTTP client (%d)", ret); + return ret; + } + + ret = net_app_set_cb(&ctx->app_ctx, http_connected, http_received, + http_data_sent, http_closed); + if (ret < 0) { + NET_ERR("Cannot set callbacks (%d)", ret); + return ret; + } + + ctx->http.parser_settings.on_body = on_body; + ctx->http.parser_settings.on_chunk_complete = on_chunk_complete; + ctx->http.parser_settings.on_chunk_header = on_chunk_header; + ctx->http.parser_settings.on_headers_complete = on_headers_complete; + ctx->http.parser_settings.on_header_field = on_header_field; + ctx->http.parser_settings.on_header_value = on_header_value; + ctx->http.parser_settings.on_message_begin = on_message_begin; + ctx->http.parser_settings.on_message_complete = on_message_complete; + ctx->http.parser_settings.on_status = on_status; + ctx->http.parser_settings.on_url = on_url; + + k_sem_init(&ctx->http.req.wait, 0, 1); + k_sem_init(&ctx->http.connect_wait, 0, 1); + + ctx->server = server; + ctx->is_init = true; + ctx->is_client = true; + + return 0; +} + +int http_request_cancel(struct http_ctx *ctx) +{ + if (!ctx->is_init) { return -EINVAL; } - ctx->rsp.response_buf = response_buf; - ctx->rsp.response_buf_len = response_buf_len; + if (!ctx->is_client) { + return -EINVAL; + } client_reset(ctx); - /* HTTPS connection is established in https_handler() */ - if (!ctx->is_https) { - ret = tcp_connect(ctx); - if (ret < 0 && ret != -EALREADY) { - NET_DBG("TCP connect error (%d)", ret); - return ret; - } - } - - if (!req->host) { - req->host = ctx->server; - } - - ctx->req.host = req->host; - ctx->req.method = req->method; - ctx->req.user_data = user_data; - - ctx->rsp.cb = cb; + return 0; +} #if defined(CONFIG_HTTPS) - if (ctx->is_https) { - struct tx_fifo_block *tx_data; - struct k_mem_block block; - - ret = start_https(ctx); - if (ret != 0 && ret != -EALREADY) { - NET_ERR("HTTPS init failed (%d)", ret); - goto out; - } - - ret = k_mem_pool_alloc(ctx->https.pool, &block, - sizeof(struct tx_fifo_block), - BUF_ALLOC_TIMEOUT); - if (ret < 0) { - goto out; - } - - tx_data = block.data; - tx_data->req = req; - - memcpy(&tx_data->block, &block, sizeof(struct k_mem_block)); - - /* We need to pass the HTTPS request to HTTPS thread because - * of the mbedtls API stack size requirements. - */ - k_fifo_put(&ctx->https.mbedtls.ssl_ctx.tx_fifo, - (void *)tx_data); - - /* Let the https_handler() to start to process the message. - * - * Note that if the timeout > 0 or is K_FOREVER, then this - * yield is not really necessary as the k_sem_take() will - * let the https handler thread to run. But if the timeout - * is K_NO_WAIT, then we need to let the https handler to - * run now. - */ - k_yield(); - } else -#endif /* CONFIG_HTTPS */ - { - print_info(ctx, ctx->req.method); - - ret = http_request(ctx, req, timeout); - if (ret < 0) { - NET_DBG("Send error (%d)", ret); - goto out; - } - } - - if (timeout != 0 && k_sem_take(&ctx->req.wait, timeout)) { - ret = -ETIMEDOUT; - goto out; - } - - if (timeout == 0) { - return -EINPROGRESS; - } - - return 0; - -out: - tcp_disconnect(ctx); - - return ret; -} - -#if defined(CONFIG_DNS_RESOLVER) -static void dns_cb(enum dns_resolve_status status, - struct dns_addrinfo *info, - void *user_data) +int http_client_set_tls(struct http_ctx *ctx, + u8_t *request_buf, + size_t request_buf_len, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_ca_cert_cb_t cert_cb, + const char *cert_host, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + k_thread_stack_t *https_stack, + size_t https_stack_size) { - struct waiter *waiter = user_data; - struct http_client_ctx *ctx = waiter->ctx; - - if (!(status == DNS_EAI_INPROGRESS && info)) { - return; - } - - if (info->ai_family == AF_INET) { -#if defined(CONFIG_NET_IPV4) - net_ipaddr_copy(&net_sin(&ctx->tcp.remote)->sin_addr, - &net_sin(&info->ai_addr)->sin_addr); -#else - goto out; -#endif - } else if (info->ai_family == AF_INET6) { -#if defined(CONFIG_NET_IPV6) - net_ipaddr_copy(&net_sin6(&ctx->tcp.remote)->sin6_addr, - &net_sin6(&info->ai_addr)->sin6_addr); -#else - goto out; -#endif - } else { - goto out; - } - - ctx->tcp.remote.sa_family = info->ai_family; - -out: - k_sem_give(&waiter->wait); -} - -#define DNS_WAIT K_SECONDS(2) -#define DNS_WAIT_SEM (DNS_WAIT + K_SECONDS(1)) - -static int resolve_name(struct http_client_ctx *ctx, - const char *server, - enum dns_query_type type) -{ - struct waiter dns_waiter; int ret; - dns_waiter.ctx = ctx; - k_sem_init(&dns_waiter.wait, 0, 1); - - ret = dns_get_addr_info(server, type, &ctx->dns_id, dns_cb, - &dns_waiter, DNS_WAIT); + ret = net_app_client_tls(&ctx->app_ctx, + request_buf, + request_buf_len, + personalization_data, + personalization_data_len, + cert_cb, + cert_host, + entropy_src_cb, + pool, + https_stack, + https_stack_size); if (ret < 0) { - NET_ERR("Cannot resolve %s (%d)", server, ret); - ctx->dns_id = 0; + NET_DBG("Cannot init TLS (%d)", ret); return ret; } - /* Wait a little longer for the DNS to finish so that - * the DNS will timeout before the semaphore. - */ - if (k_sem_take(&dns_waiter.wait, DNS_WAIT_SEM)) { - NET_ERR("Timeout while resolving %s", server); - ctx->dns_id = 0; - return -ETIMEDOUT; - } + ctx->is_tls = true; - ctx->dns_id = 0; - - if (ctx->tcp.remote.sa_family == AF_UNSPEC) { - return -EINVAL; - } - - return 0; -} -#endif /* CONFIG_DNS_RESOLVER */ - -static inline int set_remote_addr(struct http_client_ctx *ctx, - const char *server, u16_t server_port) -{ - int ret; - -#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4) - ret = net_addr_pton(AF_INET6, server, - &net_sin6(&ctx->tcp.remote)->sin6_addr); - if (ret < 0) { - /* Could be hostname, try DNS if configured. */ -#if !defined(CONFIG_DNS_RESOLVER) - NET_ERR("Invalid IPv6 address %s", server); - return -EINVAL; -#else - ret = resolve_name(ctx, server, DNS_QUERY_TYPE_AAAA); - if (ret < 0) { - NET_ERR("Cannot resolve %s (%d)", server, ret); - return ret; - } -#endif - } - - net_sin6(&ctx->tcp.remote)->sin6_port = htons(server_port); - net_sin6(&ctx->tcp.remote)->sin6_family = AF_INET6; -#endif /* IPV6 && !IPV4 */ - -#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) - ret = net_addr_pton(AF_INET, server, - &net_sin(&ctx->tcp.remote)->sin_addr); - if (ret < 0) { - /* Could be hostname, try DNS if configured. */ -#if !defined(CONFIG_DNS_RESOLVER) - NET_ERR("Invalid IPv4 address %s", server); - return -EINVAL; -#else - ret = resolve_name(ctx, server, DNS_QUERY_TYPE_A); - if (ret < 0) { - NET_ERR("Cannot resolve %s (%d)", server, ret); - return ret; - } -#endif - } - - net_sin(&ctx->tcp.remote)->sin_port = htons(server_port); - net_sin(&ctx->tcp.remote)->sin_family = AF_INET; -#endif /* IPV6 && !IPV4 */ - -#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) - ret = net_addr_pton(AF_INET, server, - &net_sin(&ctx->tcp.remote)->sin_addr); - if (ret < 0) { - ret = net_addr_pton(AF_INET6, server, - &net_sin6(&ctx->tcp.remote)->sin6_addr); - if (ret < 0) { - /* Could be hostname, try DNS if configured. */ -#if !defined(CONFIG_DNS_RESOLVER) - NET_ERR("Invalid IPv4 or IPv6 address %s", server); - return -EINVAL; -#else - ret = resolve_name(ctx, server, DNS_QUERY_TYPE_A); - if (ret < 0) { - ret = resolve_name(ctx, server, - DNS_QUERY_TYPE_AAAA); - if (ret < 0) { - NET_ERR("Cannot resolve %s (%d)", - server, ret); - return ret; - } - - goto ipv6; - } - - goto ipv4; -#endif /* !CONFIG_DNS_RESOLVER */ - } else { -#if defined(CONFIG_DNS_RESOLVER) - ipv6: -#endif - net_sin6(&ctx->tcp.remote)->sin6_port = - htons(server_port); - net_sin6(&ctx->tcp.remote)->sin6_family = AF_INET6; - } - } else { -#if defined(CONFIG_DNS_RESOLVER) - ipv4: -#endif - net_sin(&ctx->tcp.remote)->sin_port = htons(server_port); - net_sin(&ctx->tcp.remote)->sin_family = AF_INET; - } -#endif /* IPV4 && IPV6 */ - - /* If we have not yet figured out what is the protocol family, - * then we cannot continue. - */ - if (ctx->tcp.remote.sa_family == AF_UNSPEC) { - NET_ERR("Unknown protocol family."); - return -EPFNOSUPPORT; - } - - return 0; -} - -int http_client_init(struct http_client_ctx *ctx, - const char *server, u16_t server_port) -{ - int ret; - - /* Coverity tells in CID 170743 that the next memset() is - * is overwriting the ctx struct. This is false positive as - * the struct is initialized with proper size. - */ - memset(ctx, 0, sizeof(*ctx)); - - if (server) { - ret = set_remote_addr(ctx, server, server_port); - if (ret < 0) { - return ret; - } - - ctx->tcp.local.sa_family = ctx->tcp.remote.sa_family; - ctx->server = server; - } - - ctx->settings.on_body = on_body; - ctx->settings.on_chunk_complete = on_chunk_complete; - ctx->settings.on_chunk_header = on_chunk_header; - ctx->settings.on_headers_complete = on_headers_complete; - ctx->settings.on_header_field = on_header_field; - ctx->settings.on_header_value = on_header_value; - ctx->settings.on_message_begin = on_message_begin; - ctx->settings.on_message_complete = on_message_complete; - ctx->settings.on_status = on_status; - ctx->settings.on_url = on_url; - - ctx->tcp.receive_cb = http_receive_cb; - ctx->tcp.timeout = HTTP_NETWORK_TIMEOUT; - ctx->tcp.send_data = net_context_send; - ctx->tcp.recv_cb = recv_cb; - - k_sem_init(&ctx->req.wait, 0, 1); - - return 0; -} - -#if defined(CONFIG_HTTPS) -/* This gets plain data and it sends encrypted one to peer */ -static int https_send(struct net_pkt *pkt, - net_context_send_cb_t cb, - s32_t timeout, - void *token, - void *user_data) -{ - struct http_client_ctx *ctx = user_data; - int ret; - u16_t len; - - if (!ctx->rsp.response_buf || ctx->rsp.response_buf_len == 0) { - NET_DBG("Response buf not setup"); - return -EINVAL; - } - - len = net_pkt_appdatalen(pkt); - if (len == 0) { - NET_DBG("No application data to send"); - return -EINVAL; - } - - ret = net_frag_linearize(ctx->rsp.response_buf, - ctx->rsp.response_buf_len, - pkt, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_opt_len(pkt), - len); - if (ret < 0) { - NET_DBG("Cannot linearize send data (%d)", ret); - return ret; - } - - if (ret != len) { - NET_DBG("Linear copy error (%u vs %d)", len, ret); - return -EINVAL; - } - - do { - ret = mbedtls_ssl_write(&ctx->https.mbedtls.ssl, - ctx->rsp.response_buf, len); - if (ret == MBEDTLS_ERR_NET_CONN_RESET) { - NET_ERR("peer closed the connection -0x%x", ret); - goto out; - } - - if (ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - if (ret < 0) { - print_error("mbedtls_ssl_write returned -0x%x", - ret); - goto out; - } - } - } while (ret <= 0); - -out: - if (cb) { - cb(net_pkt_context(pkt), ret, token, user_data); - } - - return ret; -} - -int https_client_init(struct http_client_ctx *ctx, - const char *server, u16_t server_port, - u8_t *personalization_data, - size_t personalization_data_len, - https_ca_cert_cb_t cert_cb, - const char *cert_host, - https_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *https_stack, - size_t https_stack_size) -{ - int ret; - - if (!cert_cb) { - NET_ERR("Cert callback must be set"); - return -EINVAL; - } - - memset(ctx, 0, sizeof(*ctx)); - - if (server) { - ret = set_remote_addr(ctx, server, server_port); - if (ret < 0) { - return ret; - } - - ctx->tcp.local.sa_family = ctx->tcp.remote.sa_family; - ctx->server = server; - } - - k_sem_init(&ctx->req.wait, 0, 1); - - ctx->is_https = true; - - ctx->settings.on_body = on_body; - ctx->settings.on_chunk_complete = on_chunk_complete; - ctx->settings.on_chunk_header = on_chunk_header; - ctx->settings.on_headers_complete = on_headers_complete; - ctx->settings.on_header_field = on_header_field; - ctx->settings.on_header_value = on_header_value; - ctx->settings.on_message_begin = on_message_begin; - ctx->settings.on_message_complete = on_message_complete; - ctx->settings.on_status = on_status; - ctx->settings.on_url = on_url; - - ctx->tcp.timeout = HTTP_NETWORK_TIMEOUT; - ctx->tcp.send_data = https_send; - ctx->tcp.recv_cb = ssl_received; - - ctx->https.cert_host = cert_host; - ctx->https.stack = https_stack; - ctx->https.stack_size = https_stack_size; - ctx->https.mbedtls.cert_cb = cert_cb; - ctx->https.pool = pool; - ctx->https.mbedtls.personalization_data = personalization_data; - ctx->https.mbedtls.personalization_data_len = personalization_data_len; - - if (entropy_src_cb) { - ctx->https.mbedtls.entropy_src_cb = entropy_src_cb; - } else { - ctx->https.mbedtls.entropy_src_cb = entropy_source; - } - - /* The mbedtls is initialized in HTTPS thread because of mbedtls stack - * requirements. - */ return 0; } #endif /* CONFIG_HTTPS */ - -void http_client_release(struct http_client_ctx *ctx) -{ - if (!ctx) { - return; - } - -#if defined(CONFIG_HTTPS) - if (ctx->is_https) { - https_shutdown(ctx); - } -#endif /* CONFIG_HTTPS */ - - /* https_shutdown() might have released the context already */ - if (ctx->tcp.ctx) { - net_context_put(ctx->tcp.ctx); - ctx->tcp.ctx = NULL; - } - - ctx->tcp.receive_cb = NULL; - ctx->rsp.cb = NULL; - k_sem_give(&ctx->req.wait); - -#if defined(CONFIG_DNS_RESOLVER) - if (ctx->dns_id) { - dns_cancel_addr_info(ctx->dns_id); - } -#endif - - /* Let all the pending waiters run */ - k_yield(); - - /* Coverity tells in CID 170742 that the next memset() is - * is overwriting the ctx struct. This is false positive as - * the struct is initialized with proper size. - */ - memset(ctx, 0, sizeof(*ctx)); -} - -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) -int http_client_set_net_pkt_pool(struct http_client_ctx *ctx, - net_pkt_get_slab_func_t tx_slab, - net_pkt_get_pool_func_t data_pool) -{ - ctx->tx_slab = tx_slab; - ctx->data_pool = data_pool; - - return 0; -} -#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ diff --git a/subsys/net/lib/http/http_server.c b/subsys/net/lib/http/http_server.c index 7d7277e215d..7ddd23c4385 100644 --- a/subsys/net/lib/http/http_server.c +++ b/subsys/net/lib/http/http_server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Intel Corporation + * Copyright (c) 2017 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,64 +10,39 @@ #else #define SYS_LOG_DOMAIN "http/server" #endif +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG #define NET_LOG_ENABLED 1 #endif -#define RX_EXTRA_DEBUG 0 - -#include +#include +#include +#include +#include +#include +#include #include -#include -#include - +#include #include -#if defined(CONFIG_HTTPS) -static void https_enable(struct http_server_ctx *ctx); -static void https_disable(struct http_server_ctx *ctx); - -#if defined(MBEDTLS_DEBUG_C) -#include -/* - Debug levels (from ext/lib/crypto/mbedtls/include/mbedtls/debug.h) - * - 0 No debug - * - 1 Error - * - 2 State change - * - 3 Informational - * - 4 Verbose - */ -#define DEBUG_THRESHOLD 0 -#endif - -#endif /* CONFIG_HTTPS */ +#define BUF_ALLOC_TIMEOUT 100 #define HTTP_DEFAULT_PORT 80 #define HTTPS_DEFAULT_PORT 443 #define RC_STR(rc) (rc == 0 ? "OK" : "ERROR") -#define HTTP_STATUS_200_OK "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/html\r\n" \ - "Transfer-Encoding: chunked\r\n" \ - "\r\n" - #define HTTP_STATUS_400_BR "HTTP/1.1 400 Bad Request\r\n" \ "\r\n" -#define HTTP_STATUS_403_FBD "HTTP/1.1 403 Forbidden\r\n" \ - "\r\n" - -#define HTTP_STATUS_404_NF "HTTP/1.1 404 Not Found\r\n" \ - "\r\n" - #if defined(CONFIG_NET_DEBUG_HTTP_CONN) -/** List of HTTP connections */ +/** List of http connections */ static sys_slist_t http_conn; static http_server_cb_t ctx_mon; static void *mon_user_data; -static void http_server_conn_add(struct http_server_ctx *ctx) +void http_server_conn_add(struct http_ctx *ctx) { sys_slist_prepend(&http_conn, &ctx->node); @@ -76,14 +51,14 @@ static void http_server_conn_add(struct http_server_ctx *ctx) } } -static void http_server_conn_del(struct http_server_ctx *ctx) +void http_server_conn_del(struct http_ctx *ctx) { sys_slist_find_and_remove(&http_conn, &ctx->node); } void http_server_conn_foreach(http_server_cb_t cb, void *user_data) { - struct http_server_ctx *ctx; + struct http_ctx *ctx; SYS_SLIST_FOR_EACH_CONTAINER(&http_conn, ctx, node) { cb(ctx, user_data); @@ -95,283 +70,265 @@ void http_server_conn_monitor(http_server_cb_t cb, void *user_data) ctx_mon = cb; mon_user_data = user_data; } -#else -#define http_server_conn_add(...) -#define http_server_conn_del(...) #endif /* CONFIG_NET_DEBUG_HTTP_CONN */ -static inline u16_t http_strlen(const char *str) +const char * const http_state_str(enum http_state state) { - if (str) { - return strlen(str); +#if defined(CONFIG_NET_DEBUG_HTTP) + switch (state) { + case HTTP_STATE_CLOSED: + return "CLOSED"; + case HTTP_STATE_WAITING_HEADER: + return "WAITING_HEADER"; + case HTTP_STATE_RECEIVING_HEADER: + return "RECEIVING HEADER"; + case HTTP_STATE_HEADER_RECEIVED: + return "HEADER_RECEIVED"; + case HTTP_STATE_OPEN: + return "OPEN"; } +#else /* CONFIG_NET_DEBUG_HTTP */ + ARG_UNUSED(state); +#endif /* CONFIG_NET_DEBUG_HTTP */ - return 0; + return ""; } -static int http_add_header(struct net_pkt *pkt, s32_t timeout, const char *str) +#if defined(CONFIG_NET_DEBUG_HTTP) +static void validate_state_transition(struct http_ctx *ctx, + enum http_state current, + enum http_state new) { - if (net_pkt_append_all(pkt, strlen(str), (u8_t *)str, timeout)) { - return 0; + static const u16_t valid_transitions[] = { + [HTTP_STATE_CLOSED] = 1 << HTTP_STATE_WAITING_HEADER, + [HTTP_STATE_WAITING_HEADER] = + 1 << HTTP_STATE_RECEIVING_HEADER | + 1 << HTTP_STATE_CLOSED, + [HTTP_STATE_RECEIVING_HEADER] = + 1 << HTTP_STATE_HEADER_RECEIVED | + 1 << HTTP_STATE_CLOSED | + 1 << HTTP_STATE_OPEN, + [HTTP_STATE_HEADER_RECEIVED] = + 1 << HTTP_STATE_OPEN | + 1 << HTTP_STATE_CLOSED, + [HTTP_STATE_OPEN] = 1 << HTTP_STATE_CLOSED, + }; + + if (!(valid_transitions[current] & 1 << new)) { + NET_DBG("[%p] Invalid state transition: %s (%d) => %s (%d)", + ctx, http_state_str(current), current, + http_state_str(new), new); + } +} +#endif /* CONFIG_NET_DEBUG_HTTP */ + +void _http_change_state(struct http_ctx *ctx, + enum http_state new_state, + const char *func, int line) +{ + if (ctx->state == new_state) { + return; } - return -ENOMEM; + NET_ASSERT(new_state >= HTTP_STATE_CLOSED && + new_state <= HTTP_STATE_OPEN); + + NET_DBG("[%p] state %s (%d) => %s (%d) [%s():%d]", + ctx, http_state_str(ctx->state), ctx->state, + http_state_str(new_state), new_state, + func, line); + +#if defined(CONFIG_NET_DEBUG_HTTP) + validate_state_transition(ctx, ctx->state, new_state); +#endif /* CONFIG_NET_DEBUG_HTTP */ + + ctx->state = new_state; } -static int http_add_chunk(struct net_pkt *pkt, s32_t timeout, const char *str) +static void http_data_sent(struct net_app_ctx *app_ctx, + int status, + void *user_data_send, + void *user_data) { - char chunk_header[16]; - u16_t str_len; - bool ret; + struct http_ctx *ctx = user_data; - str_len = http_strlen(str); - - snprintk(chunk_header, sizeof(chunk_header), "%x\r\n", str_len); - - ret = net_pkt_append_all(pkt, strlen(chunk_header), chunk_header, - timeout); - if (!ret) { - return -ENOMEM; + if (!user_data_send) { + /* This is the token field in the net_context_send(). + * If this is not set, then it is TCP ACK messages + * that are generated by the stack. We just ignore those. + */ + return; } - if (str_len > 0) { - ret = net_pkt_append_all(pkt, str_len, (u8_t *)str, timeout); - if (!ret) { - return -ENOMEM; - } + if (ctx->state == HTTP_STATE_OPEN && ctx->cb.send) { + ctx->cb.send(ctx, status, user_data_send, ctx->user_data); } - - ret = net_pkt_append_all(pkt, sizeof(HTTP_CRLF) - 1, HTTP_CRLF, - timeout); - if (!ret) { - return -ENOMEM; - } - - return 0; } -static void req_timer_cancel(struct http_server_ctx *ctx) +int http_send_error(struct http_ctx *ctx, int code, + u8_t *html_payload, size_t html_len) { - k_delayed_work_cancel(&ctx->req.timer); + const char *msg; + int ret; - NET_DBG("Context %p request timer cancelled", ctx); -} - -static void req_timeout(struct k_work *work) -{ - struct http_server_ctx *ctx = CONTAINER_OF(work, - struct http_server_ctx, - req.timer); - - NET_DBG("Context %p request timeout", ctx); - - net_context_put(ctx->req.net_ctx); - ctx->req.net_ctx = NULL; - - http_server_conn_del(ctx); -} - -static void pkt_sent(struct net_context *context, - int status, - void *token, - void *user_data) -{ - s32_t timeout = POINTER_TO_INT(token); - struct http_server_ctx *ctx = user_data; - - req_timer_cancel(ctx); - - if (timeout == K_NO_WAIT) { - /* We can just close the context after the packet is sent. */ - net_context_put(context); - http_server_conn_del(ctx); - } else if (timeout > 0) { - NET_DBG("Context %p starting timer", ctx); - - k_delayed_work_submit(&ctx->req.timer, timeout); + if (ctx->pending) { + net_pkt_unref(ctx->pending); + ctx->pending = NULL; } - /* Note that if the timeout is K_FOREVER, we do not close - * the connection. - */ -} - -int http_response_wait(struct http_server_ctx *ctx, const char *http_header, - const char *html_payload, s32_t timeout) -{ - struct net_pkt *pkt; - int ret = -EINVAL; - - pkt = net_pkt_get_tx(ctx->req.net_ctx, ctx->timeout); - if (!pkt) { - return ret; + switch (code) { + case 400: + msg = HTTP_STATUS_400_BR; + break; } - ret = http_add_header(pkt, ctx->timeout, http_header); - if (ret != 0) { - goto exit_routine; + ret = http_add_header(ctx, msg, NULL); + if (ret < 0) { + goto quit; } if (html_payload) { - ret = http_add_chunk(pkt, ctx->timeout, html_payload); - if (ret != 0) { - goto exit_routine; - } - - /* like EOF */ - ret = http_add_chunk(pkt, ctx->timeout, NULL); - if (ret != 0) { - goto exit_routine; + ret = http_prepare_and_send(ctx, html_payload, html_len, NULL); + if (ret < 0) { + goto quit; } } - net_pkt_set_appdatalen(pkt, net_buf_frags_len(pkt->frags)); + ret = http_send_flush(ctx, NULL); - ret = ctx->send_data(pkt, pkt_sent, 0, INT_TO_POINTER(timeout), ctx); - if (ret != 0) { - goto exit_routine; - } - - pkt = NULL; - -exit_routine: - if (pkt) { - net_pkt_unref(pkt); +quit: + if (ret < 0) { + net_pkt_unref(ctx->pending); + ctx->pending = NULL; } return ret; } -int http_response(struct http_server_ctx *ctx, const char *http_header, - const char *html_payload) +#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) +static char *sprint_ipaddr(char *buf, int buflen, const struct sockaddr *addr) { - return http_response_wait(ctx, http_header, html_payload, K_NO_WAIT); -} - -int http_response_400(struct http_server_ctx *ctx, const char *html_payload) -{ - return http_response(ctx, HTTP_STATUS_400_BR, html_payload); -} - -int http_response_403(struct http_server_ctx *ctx, const char *html_payload) -{ - return http_response(ctx, HTTP_STATUS_403_FBD, html_payload); -} - -int http_response_404(struct http_server_ctx *ctx, const char *html_payload) -{ - return http_response(ctx, HTTP_STATUS_404_NF, html_payload); -} - -int http_response_send_data(struct http_server_ctx *ctx, - const char *http_header, - const char *html_payload, - s32_t timeout) -{ - struct net_pkt *pkt; - int ret = -EINVAL; - - pkt = net_pkt_get_tx(ctx->req.net_ctx, ctx->timeout); - if (!pkt) { - return ret; - } - - if (http_header) { - ret = http_add_header(pkt, ctx->timeout, http_header); - if (ret != 0) { - goto exit_routine; - } - } - - ret = http_add_chunk(pkt, ctx->timeout, html_payload); - if (ret != 0) { - goto exit_routine; - } - - net_pkt_set_appdatalen(pkt, net_buf_frags_len(pkt->frags)); - - ret = ctx->send_data(pkt, pkt_sent, 0, INT_TO_POINTER(timeout), ctx); - if (ret != 0) { - goto exit_routine; - } - - pkt = NULL; - - /* We must let the system to send the packet, otherwise TCP might - * timeout before the packet is actually sent. This is easily seen - * if the application calls this functions many times in a row. - */ - k_yield(); - -exit_routine: - if (pkt) { - net_pkt_unref(pkt); - } - - return ret; -} - -int http_server_set_local_addr(struct sockaddr *addr, const char *myaddr, - u16_t port) -{ - if (!addr) { - return -EINVAL; - } - - if (myaddr) { - void *inaddr; - - if (addr->sa_family == AF_INET) { -#if defined(CONFIG_NET_IPV4) - inaddr = &net_sin(addr)->sin_addr; - net_sin(addr)->sin_port = htons(port); -#else - return -EPFNOSUPPORT; -#endif - } else if (addr->sa_family == AF_INET6) { -#if defined(CONFIG_NET_IPV6) - inaddr = &net_sin6(addr)->sin6_addr; - net_sin6(addr)->sin6_port = htons(port); -#else - return -EPFNOSUPPORT; -#endif - } else { - return -EAFNOSUPPORT; - } - - return net_addr_pton(addr->sa_family, myaddr, inaddr); - } - - /* If the caller did not supply the address where to bind, then - * try to figure it out ourselves. - */ if (addr->sa_family == AF_INET6) { #if defined(CONFIG_NET_IPV6) - net_ipaddr_copy(&net_sin6(addr)->sin6_addr, - net_if_ipv6_select_src_addr(NULL, - (struct in6_addr *) - net_ipv6_unspecified_address())); -#else - return -EPFNOSUPPORT; + char ipaddr[NET_IPV6_ADDR_LEN]; + + net_addr_ntop(addr->sa_family, + &net_sin6(addr)->sin6_addr, + ipaddr, sizeof(ipaddr)); + snprintk(buf, buflen, "[%s]:%u", ipaddr, + ntohs(net_sin6(addr)->sin6_port)); #endif } else if (addr->sa_family == AF_INET) { #if defined(CONFIG_NET_IPV4) - struct net_if *iface = net_if_get_default(); + char ipaddr[NET_IPV4_ADDR_LEN]; - /* For IPv4 we take the first address in the interface */ - net_ipaddr_copy(&net_sin(addr)->sin_addr, - &iface->ipv4.unicast[0].address.in_addr); -#else - return -EPFNOSUPPORT; + net_addr_ntop(addr->sa_family, + &net_sin(addr)->sin_addr, + ipaddr, sizeof(ipaddr)); + snprintk(buf, buflen, "%s:%u", ipaddr, + ntohs(net_sin(addr)->sin_port)); #endif + } else { + snprintk(buf, buflen, "", addr->sa_family); } - return 0; + return buf; +} + +static struct net_context *get_server_ctx(struct net_app_ctx *ctx, + const struct sockaddr *dst) +{ + int i; + + if (!dst) { + return NULL; + } + + for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) { + struct net_context *tmp; + u16_t port, rport; + + if (!ctx->server.net_ctxs[i]) { + continue; + } + + tmp = ctx->server.net_ctxs[i]; + + if (IS_ENABLED(CONFIG_NET_IPV4) && + tmp->remote.sa_family == AF_INET && + dst->sa_family == AF_INET) { + struct in_addr *addr4 = &net_sin(dst)->sin_addr; + struct in_addr *remote4; + + remote4 = &net_sin(&tmp->remote)->sin_addr; + rport = net_sin(&tmp->remote)->sin_port; + port = net_sin(dst)->sin_port; + + if (net_ipv4_addr_cmp(addr4, remote4) && + port == rport) { + return tmp; + } + + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + tmp->remote.sa_family == AF_INET6 && + dst->sa_family == AF_INET6) { + struct in6_addr *addr6 = &net_sin6(dst)->sin6_addr; + struct in6_addr *remote6; + + remote6 = &net_sin6(&tmp->remote)->sin6_addr; + rport = net_sin6(&tmp->remote)->sin6_port; + port = net_sin6(dst)->sin6_port; + + if (net_ipv6_addr_cmp(addr6, remote6) && + port == rport) { + return tmp; + } + } + } + + return NULL; +} +#endif /* CONFIG_NET_DEBUG_HTTP */ + +static inline void new_client(struct http_ctx *ctx, + enum http_connection_type type, + struct net_app_ctx *app_ctx) +{ +#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) +#if defined(CONFIG_NET_IPV6) +#define PORT_LEN sizeof("[]:xxxxx") +#define ADDR_LEN NET_IPV6_ADDR_LEN +#elif defined(CONFIG_NET_IPV4) +#define PORT_LEN sizeof(":xxxxx") +#define ADDR_LEN NET_IPV4_ADDR_LEN +#endif + char buf[ADDR_LEN + PORT_LEN]; + struct net_context *net_ctx; + const char *type_str = "HTTP"; + + net_ctx = get_server_ctx(app_ctx, ctx->addr); + if (net_ctx) { + NET_INFO("[%p] %s connection from %s (%p)", ctx, type_str, + sprint_ipaddr(buf, sizeof(buf), &net_ctx->remote), + net_ctx); + } else { + NET_INFO("[%p] %s connection", ctx, type_str); + } +#endif /* CONFIG_NET_DEBUG_HTTP */ +} + +static void url_connected(struct http_ctx *ctx, + enum http_connection_type type) +{ + new_client(ctx, type, &ctx->app_ctx); + + if (ctx->cb.connect) { + ctx->cb.connect(ctx, type, ctx->user_data); + } } struct http_root_url *http_server_add_url(struct http_server_urls *my, - const char *url, u8_t flags, - http_url_cb_t write_cb) + const char *url, u8_t flags) { int i; @@ -386,7 +343,11 @@ struct http_root_url *http_server_add_url(struct http_server_urls *my, /* This will speed-up some future operations */ my->urls[i].root_len = strlen(url); my->urls[i].flags = flags; - my->urls[i].write_cb = write_cb; + + NET_DBG("[%d] %s URL %s", i, + flags == HTTP_URL_STANDARD ? "HTTP" : + (flags == HTTP_URL_WEBSOCKET ? "WS" : ""), + url); return &my->urls[i]; } @@ -417,7 +378,7 @@ int http_server_del_url(struct http_server_urls *my, const char *url) } struct http_root_url *http_server_add_default(struct http_server_urls *my, - http_url_cb_t write_cb) + http_url_cb_t cb) { if (my->default_url.is_used) { return NULL; @@ -427,7 +388,7 @@ struct http_root_url *http_server_add_default(struct http_server_urls *my, my->default_url.root = NULL; my->default_url.root_len = 0; my->default_url.flags = 0; - my->default_url.write_cb = write_cb; + my->default_cb = cb; return &my->default_url; } @@ -443,136 +404,6 @@ int http_server_del_default(struct http_server_urls *my) return 0; } -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) -static char *sprint_ipaddr(char *buf, int buflen, const struct sockaddr *addr) -{ - if (addr->sa_family == AF_INET6) { -#if defined(CONFIG_NET_IPV6) - char ipaddr[NET_IPV6_ADDR_LEN]; - - net_addr_ntop(addr->sa_family, - &net_sin6(addr)->sin6_addr, - ipaddr, sizeof(ipaddr)); - snprintk(buf, buflen, "[%s]:%u", ipaddr, - ntohs(net_sin6(addr)->sin6_port)); -#endif - } else if (addr->sa_family == AF_INET) { -#if defined(CONFIG_NET_IPV4) - char ipaddr[NET_IPV4_ADDR_LEN]; - - net_addr_ntop(addr->sa_family, - &net_sin(addr)->sin_addr, - ipaddr, sizeof(ipaddr)); - snprintk(buf, buflen, "%s:%u", ipaddr, - ntohs(net_sin(addr)->sin_port)); -#endif - } - - return buf; -} -#endif /* CONFIG_NET_DEBUG_HTTP */ - -static inline void new_client(struct http_server_ctx *http_ctx, - struct net_context *net_ctx, - const struct sockaddr *addr) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) -#if defined(CONFIG_NET_IPV6) -#define PORT_STR sizeof("[]:xxxxx") - char buf[NET_IPV6_ADDR_LEN + PORT_STR]; -#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) -#define PORT_STR sizeof(":xxxxx") - char buf[NET_IPV4_ADDR_LEN + PORT_STR]; -#endif - - NET_INFO("%s connection from %s (%p)", - http_ctx->is_https ? "HTTPS" : "HTTP", - sprint_ipaddr(buf, sizeof(buf), addr), - net_ctx); -#endif /* CONFIG_NET_DEBUG_HTTP */ -} - -static inline void new_server(struct http_server_ctx *ctx, - const char *server_banner, - const struct sockaddr *addr) -{ -#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) -#if defined(CONFIG_NET_IPV6) -#define PORT_STR sizeof("[]:xxxxx") - char buf[NET_IPV6_ADDR_LEN + PORT_STR]; -#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) -#define PORT_STR sizeof(":xxxxx") - char buf[NET_IPV4_ADDR_LEN + PORT_STR]; -#endif - - if (addr) { - NET_INFO("%s %s (%p)", server_banner, - sprint_ipaddr(buf, sizeof(buf), addr), ctx); - } else { - NET_INFO("%s (%p)", server_banner, ctx); - } -#endif /* CONFIG_NET_DEBUG_HTTP */ -} - -static int on_header_field(struct http_parser *parser, - const char *at, size_t length) -{ - struct http_server_ctx *ctx = parser->data; - - if (ctx->req.field_values_ctr >= CONFIG_HTTP_HEADER_FIELD_ITEMS) { - return 0; - } - - ctx->req.field_values[ctx->req.field_values_ctr].key = at; - ctx->req.field_values[ctx->req.field_values_ctr].key_len = length; - - return 0; -} - -static int on_header_value(struct http_parser *parser, - const char *at, size_t length) -{ - struct http_server_ctx *ctx = parser->data; - - if (ctx->req.field_values_ctr >= CONFIG_HTTP_HEADER_FIELD_ITEMS) { - return 0; - } - - ctx->req.field_values[ctx->req.field_values_ctr].value = at; - ctx->req.field_values[ctx->req.field_values_ctr].value_len = length; - - ctx->req.field_values_ctr++; - - return 0; -} - -static int on_url(struct http_parser *parser, const char *at, size_t length) -{ - struct http_server_ctx *ctx = parser->data; - - ctx->req.url = at; - ctx->req.url_len = length; - - http_server_conn_add(ctx); - - return 0; -} - -static int parser_init(struct http_server_ctx *ctx) -{ - memset(ctx->req.field_values, 0, sizeof(ctx->req.field_values)); - - ctx->req.settings.on_header_field = on_header_field; - ctx->req.settings.on_header_value = on_header_value; - ctx->req.settings.on_url = on_url; - - http_parser_init(&ctx->req.parser, HTTP_REQUEST); - - ctx->req.parser.data = ctx; - - return 0; -} - static int http_url_cmp(const char *url, u16_t url_len, const char *root_url, u16_t root_url_len) { @@ -591,7 +422,9 @@ static int http_url_cmp(const char *url, u16_t url_len, * root_url = /images/, url = /images_and_docs -> ERROR */ if (url_len > root_url_len) { - if (root_url[root_url_len - 1] == '/') { + /* Do not match root_url = / and url = /foobar */ + if (root_url_len > 1 && + root_url[root_url_len - 1] == '/') { return 0; } @@ -604,23 +437,28 @@ static int http_url_cmp(const char *url, u16_t url_len, return -EINVAL; } -static struct http_root_url *http_url_find(struct http_server_ctx *http_ctx) +struct http_root_url *http_url_find(struct http_ctx *ctx, + enum http_url_flags flags) { - u16_t url_len = http_ctx->req.url_len; - const char *url = http_ctx->req.url; + u16_t url_len = ctx->http.url_len; + const char *url = ctx->http.url; struct http_root_url *root_url; u8_t i; int ret; for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) { - root_url = &http_ctx->urls->urls[i]; - if (!root_url->is_used) { + if (!ctx->http.urls) { + continue; + } + + root_url = &ctx->http.urls->urls[i]; + if (!root_url || !root_url->is_used) { continue; } ret = http_url_cmp(url, url_len, root_url->root, root_url->root_len); - if (!ret) { + if (!ret && flags == root_url->flags) { return root_url; } } @@ -628,53 +466,77 @@ static struct http_root_url *http_url_find(struct http_server_ctx *http_ctx) return NULL; } -static int http_process_recv(struct http_server_ctx *http_ctx) +static int http_process_recv(struct http_ctx *ctx) { struct http_root_url *root_url; int ret; - root_url = http_url_find(http_ctx); + root_url = http_url_find(ctx, HTTP_URL_STANDARD); if (!root_url) { - root_url = &http_ctx->urls->default_url; - if (!root_url->is_used) { - NET_DBG("No default handler found (%p)", http_ctx); + if (!ctx->http.urls) { + NET_DBG("[%p] No URL handlers found", ctx); ret = -ENOENT; goto out; } + + root_url = &ctx->http.urls->default_url; + if (!root_url || !root_url->is_used) { + NET_DBG("[%p] No default handler found", ctx); + ret = -ENOENT; + goto out; + } + + if (ctx->http.urls->default_cb) { + ret = ctx->http.urls->default_cb(ctx, + HTTP_CONNECTION); + if (ret != HTTP_VERDICT_ACCEPT) { + ret = -ECONNREFUSED; + goto out; + } + } } - if (root_url->write_cb) { - NET_DBG("Calling handler %p context %p", - root_url->write_cb, http_ctx); - ret = root_url->write_cb(http_ctx); - } else { - NET_ERR("No handler for %s", http_ctx->req.url); - ret = -ENOENT; - } + http_change_state(ctx, HTTP_STATE_OPEN); + url_connected(ctx, HTTP_CONNECTION); + + ret = 0; out: return ret; } -static void http_recv(struct net_context *net_ctx, - struct net_pkt *pkt, int status, - void *user_data) +static void http_closed(struct net_app_ctx *app_ctx, + int status, + void *user_data) { - struct http_server_ctx *http_ctx = user_data; + struct http_ctx *ctx = user_data; + + ARG_UNUSED(app_ctx); + ARG_UNUSED(status); + + http_change_state(ctx, HTTP_STATE_CLOSED); + + NET_DBG("[%p] http closed", ctx); + + http_server_conn_del(ctx); + + if (ctx->cb.close) { + ctx->cb.close(ctx, 0, ctx->user_data); + } +} + +static void http_received(struct net_app_ctx *app_ctx, + struct net_pkt *pkt, + int status, + void *user_data) +{ + struct http_ctx *ctx = user_data; + size_t start = ctx->http.data_len; + u16_t len = 0; struct net_buf *frag; - size_t start = http_ctx->req.data_len; int parsed_len; - int header_len; - u16_t len = 0, recv_len; - - if (!pkt) { - NET_DBG("Connection closed by peer"); - return; - } - - if (!http_ctx->enabled) { - goto quit; - } + size_t recv_len; + size_t pkt_len; recv_len = net_pkt_appdatalen(pkt); if (recv_len == 0) { @@ -683,953 +545,300 @@ static void http_recv(struct net_context *net_ctx, } if (status) { - NET_DBG("Status %d <%s>", status, RC_STR(status)); + NET_DBG("[%p] Status %d <%s>", ctx, status, RC_STR(status)); goto out; } /* Get rid of possible IP headers in the first fragment. */ frag = pkt->frags; - header_len = net_pkt_appdata(pkt) - frag->data; + pkt_len = net_pkt_get_len(pkt); - NET_DBG("Received %d bytes data", net_pkt_appdatalen(pkt)); + if (recv_len < pkt_len) { + net_buf_pull(frag, pkt_len - recv_len); + net_pkt_set_appdata(pkt, frag->data); + } - /* After this pull, the frag->data points directly to application data. - */ - net_buf_pull(frag, header_len); + NET_DBG("[%p] Received %zd bytes http data", ctx, recv_len); + + if (ctx->state == HTTP_STATE_OPEN) { + /* We have active websocket session and there is no longer + * any HTTP traffic in the connection. Give the data to + * application to send. + */ + goto http_only; + } while (frag) { /* If this fragment cannot be copied to result buf, * then parse what we have which will cause the callback to be * called in function on_body(), and continue copying. */ - if (http_ctx->req.data_len + frag->len > - http_ctx->req.request_buf_len) { + if ((ctx->http.data_len + frag->len) > + ctx->http.request_buf_len) { + + if (ctx->state == HTTP_STATE_HEADER_RECEIVED) { + goto http_ready; + } /* If the caller has not supplied a callback, then * we cannot really continue if the request buffer * overflows. Set the data_len to mark how many bytes * should be needed in the response_buf. */ - if (http_process_recv(http_ctx) < 0) { - http_ctx->req.data_len = net_pkt_get_len(pkt); + if (http_process_recv(ctx) < 0) { + ctx->http.data_len = recv_len; goto out; } parsed_len = - http_parser_execute(&http_ctx->req.parser, - &http_ctx->req.settings, - http_ctx->req.request_buf + + http_parser_execute(&ctx->http.parser, + &ctx->http.parser_settings, + ctx->http.request_buf + start, len); if (parsed_len <= 0) { goto fail; } - http_ctx->req.data_len = 0; + ctx->http.data_len = 0; len = 0; start = 0; } - memcpy(http_ctx->req.request_buf + http_ctx->req.data_len, + memcpy(ctx->http.request_buf + ctx->http.data_len, frag->data, frag->len); - http_ctx->req.data_len += frag->len; + ctx->http.data_len += frag->len; len += frag->len; frag = frag->frags; } out: - parsed_len = http_parser_execute(&http_ctx->req.parser, - &http_ctx->req.settings, - http_ctx->req.request_buf + start, + parsed_len = http_parser_execute(&ctx->http.parser, + &ctx->http.parser_settings, + ctx->http.request_buf + start, len); if (parsed_len < 0) { fail: - NET_DBG("Received %u bytes, only parsed %d bytes (%s %s)", - recv_len, parsed_len, - http_errno_name(http_ctx->req.parser.http_errno), + NET_DBG("[%p] Received %zd bytes, only parsed %d " + "bytes (%s %s)", + ctx, recv_len, parsed_len, + http_errno_name(ctx->http.parser.http_errno), http_errno_description( - http_ctx->req.parser.http_errno)); + ctx->http.parser.http_errno)); } - if (http_ctx->req.parser.http_errno != HPE_OK) { - http_response_400(http_ctx, NULL); + if (ctx->http.parser.http_errno != HPE_OK) { + http_send_error(ctx, 400, NULL, 0); } else { - http_process_recv(http_ctx); + if (ctx->state == HTTP_STATE_HEADER_RECEIVED) { + goto http_ready; + } + + http_process_recv(ctx); } quit: - http_parser_init(&http_ctx->req.parser, HTTP_REQUEST); - http_ctx->req.field_values_ctr = 0; - http_ctx->req.data_len = 0; + http_parser_init(&ctx->http.parser, HTTP_REQUEST); + ctx->http.data_len = 0; + ctx->http.field_values_ctr = 0; + net_pkt_unref(pkt); + + return; + +http_only: + if (ctx->cb.recv) { + ctx->cb.recv(ctx, pkt, 0, 0, ctx->user_data); + } + + return; + +http_ready: + http_change_state(ctx, HTTP_STATE_OPEN); + url_connected(ctx, HTTP_CONNECT); net_pkt_unref(pkt); } -static void accept_cb(struct net_context *net_ctx, - struct sockaddr *addr, socklen_t addrlen, - int status, void *data) -{ - struct http_server_ctx *http_ctx = data; - int ret; - - ARG_UNUSED(addr); - ARG_UNUSED(addrlen); - - if (status != 0) { - net_context_put(net_ctx); - return; - } - - /* If we receive a HTTP request and if the earlier context is still - * active and it is not the same as the new one, then close the earlier - * one. Otherwise it is possible that the context will be left into - * TCP ESTABLISHED state and would never be released. Example of this - * is that we had IPv4 connection active and then IPv6 connection is - * established, in this case we disconnect the IPv4 here. - */ - if (http_ctx->req.net_ctx && http_ctx->req.net_ctx != net_ctx && - net_context_get_state(http_ctx->req.net_ctx) == - NET_CONTEXT_CONNECTED) { - net_context_put(http_ctx->req.net_ctx); - } - - http_ctx->req.net_ctx = net_ctx; - - new_client(http_ctx, net_ctx, addr); - - ret = net_context_recv(net_ctx, http_ctx->recv_cb, K_NO_WAIT, - http_ctx); - if (ret < 0) { - NET_DBG("Cannot set recv_cb (%d)", ret); - } -} - -static int set_net_ctx(struct http_server_ctx *http_ctx, - struct net_context *ctx, - struct sockaddr *addr, - socklen_t socklen) +#if defined(CONFIG_HTTPS) +int http_server_set_tls(struct http_ctx *ctx, + const char *server_banner, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_cert_cb_t cert_cb, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + k_thread_stack_t *stack, + size_t stack_len) { int ret; - ret = net_context_bind(ctx, addr, socklen); - if (ret < 0) { - NET_ERR("Cannot bind context (%d)", ret); - goto out; - } - - ret = net_context_listen(ctx, 0); - if (ret < 0) { - NET_ERR("Cannot listen context (%d)", ret); - goto out; - } - - ret = net_context_accept(ctx, accept_cb, K_NO_WAIT, http_ctx); - if (ret < 0) { - NET_ERR("Cannot accept context (%d)", ret); - goto out; - } - -out: - return ret; -} - -#if defined(CONFIG_NET_IPV4) -static int setup_ipv4_ctx(struct http_server_ctx *http_ctx, - struct sockaddr *addr) -{ - socklen_t socklen; - int ret; - - socklen = sizeof(struct sockaddr_in); - - ret = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, - &http_ctx->net_ipv4_ctx); - if (ret < 0) { - NET_ERR("Cannot get network context (%d)", ret); - http_ctx->net_ipv4_ctx = NULL; - return ret; - } - - net_context_setup_pools(http_ctx->net_ipv4_ctx, http_ctx->tx_slab, - http_ctx->data_pool); - - if (addr->sa_family == AF_UNSPEC) { - addr->sa_family = AF_INET; - - http_server_set_local_addr(addr, NULL, - net_sin(addr)->sin_port); - } - - ret = set_net_ctx(http_ctx, http_ctx->net_ipv4_ctx, - addr, socklen); - if (ret < 0) { - net_context_put(http_ctx->net_ipv4_ctx); - http_ctx->net_ipv4_ctx = NULL; - } - - return ret; -} -#endif + if (!ctx->is_tls) { + /* Change the default port if user did not set it */ + if (!ctx->server_addr) { + net_sin(&ctx->local)->sin_port = + htons(HTTPS_DEFAULT_PORT); #if defined(CONFIG_NET_IPV6) -int setup_ipv6_ctx(struct http_server_ctx *http_ctx, struct sockaddr *addr) -{ - socklen_t socklen; - int ret; + net_sin6(&ctx->app_ctx.ipv6.local)->sin6_port = + htons(HTTPS_DEFAULT_PORT); +#endif +#if defined(CONFIG_NET_IPV4) + net_sin(&ctx->app_ctx.ipv4.local)->sin_port = + htons(HTTPS_DEFAULT_PORT); +#endif + } - socklen = sizeof(struct sockaddr_in6); + ret = net_app_server_tls(&ctx->app_ctx, + ctx->http.request_buf, + ctx->http.request_buf_len, + server_banner, + personalization_data, + personalization_data_len, + cert_cb, + entropy_src_cb, + pool, + stack, + stack_len); + if (ret < 0) { + NET_ERR("Cannot init TLS (%d)", ret); + goto quit; + } - ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, - &http_ctx->net_ipv6_ctx); - if (ret < 0) { - NET_ERR("Cannot get network context (%d)", ret); - http_ctx->net_ipv6_ctx = NULL; - return ret; + ctx->is_tls = true; + return 0; } - net_context_setup_pools(http_ctx->net_ipv6_ctx, http_ctx->tx_slab, - http_ctx->data_pool); - - if (addr->sa_family == AF_UNSPEC) { - addr->sa_family = AF_INET6; - - http_server_set_local_addr(addr, NULL, - net_sin6(addr)->sin6_port); - } - - ret = set_net_ctx(http_ctx, http_ctx->net_ipv6_ctx, - addr, socklen); - if (ret < 0) { - net_context_put(http_ctx->net_ipv6_ctx); - http_ctx->net_ipv6_ctx = NULL; - } + return -EALREADY; +quit: + net_app_release(&ctx->app_ctx); return ret; } #endif -static int init_net(struct http_server_ctx *ctx, - struct sockaddr *server_addr, - u16_t port) +static int on_header_field(struct http_parser *parser, + const char *at, size_t length) { - struct sockaddr addr; - int ret; + struct http_ctx *ctx = parser->data; - memset(&addr, 0, sizeof(addr)); + if (ctx->http.field_values_ctr >= CONFIG_HTTP_HEADERS) { + return 0; + } + + http_change_state(ctx, HTTP_STATE_RECEIVING_HEADER); + + ctx->http.field_values[ctx->http.field_values_ctr].key = at; + ctx->http.field_values[ctx->http.field_values_ctr].key_len = length; + + return 0; +} + +static int on_header_value(struct http_parser *parser, + const char *at, size_t length) +{ + struct http_ctx *ctx = parser->data; + + if (ctx->http.field_values_ctr >= CONFIG_HTTP_HEADERS) { + return 0; + } + + ctx->http.field_values[ctx->http.field_values_ctr].value = at; + ctx->http.field_values[ctx->http.field_values_ctr].value_len = length; + + ctx->http.field_values_ctr++; + + return 0; +} + +static int on_url(struct http_parser *parser, const char *at, size_t length) +{ + struct http_ctx *ctx = parser->data; + + ctx->http.url = at; + ctx->http.url_len = length; + + http_change_state(ctx, HTTP_STATE_WAITING_HEADER); + + http_server_conn_add(ctx); + + return 0; +} + +static int on_headers_complete(struct http_parser *parser) +{ + ARG_UNUSED(parser); + + return 0; +} + +static int init_http_parser(struct http_ctx *ctx) +{ + memset(ctx->http.field_values, 0, sizeof(ctx->http.field_values)); + + ctx->http.parser_settings.on_header_field = on_header_field; + ctx->http.parser_settings.on_header_value = on_header_value; + ctx->http.parser_settings.on_url = on_url; + ctx->http.parser_settings.on_headers_complete = on_headers_complete; + + http_parser_init(&ctx->http.parser, HTTP_REQUEST); + + ctx->http.parser.data = ctx; + + return 0; +} + +static inline void new_server(struct http_ctx *ctx, + const char *server_banner, + const struct sockaddr *addr) +{ +#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) +#if defined(CONFIG_NET_IPV6) +#define PORT_STR sizeof("[]:xxxxx") + char buf[NET_IPV6_ADDR_LEN + PORT_STR]; +#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) +#define PORT_STR sizeof(":xxxxx") + char buf[NET_IPV4_ADDR_LEN + PORT_STR]; +#endif + + if (addr) { + NET_INFO("%s %s (%p)", server_banner, + sprint_ipaddr(buf, sizeof(buf), addr), ctx); + } else { + NET_INFO("%s (%p)", server_banner, ctx); + } +#endif /* CONFIG_NET_DEBUG_HTTP */ +} + +static void init_net(struct http_ctx *ctx, + struct sockaddr *server_addr, + u16_t port) +{ + memset(&ctx->local, 0, sizeof(ctx->local)); if (server_addr) { - memcpy(&addr, server_addr, sizeof(addr)); + memcpy(&ctx->local, server_addr, sizeof(ctx->local)); } else { - addr.sa_family = AF_UNSPEC; - net_sin(&addr)->sin_port = htons(port); + ctx->local.sa_family = AF_UNSPEC; + net_sin(&ctx->local)->sin_port = htons(port); } - - if (addr.sa_family == AF_INET6) { -#if defined(CONFIG_NET_IPV6) - ret = setup_ipv6_ctx(ctx, &addr); -#else - return -EPFNOSUPPORT; -#endif - } else if (addr.sa_family == AF_INET) { -#if defined(CONFIG_NET_IPV4) - ret = setup_ipv4_ctx(ctx, &addr); -#else - return -EPFNOSUPPORT; -#endif - } else if (addr.sa_family == AF_UNSPEC) { -#if defined(CONFIG_NET_IPV4) - ret = setup_ipv4_ctx(ctx, &addr); -#endif - /* We ignore the IPv4 error if IPv6 is enabled */ -#if defined(CONFIG_NET_IPV6) - memset(&addr, 0, sizeof(addr)); - addr.sa_family = AF_UNSPEC; - net_sin6(&addr)->sin6_port = htons(port); - - ret = setup_ipv6_ctx(ctx, &addr); -#endif - } else { - return -EINVAL; - } - - return ret; } -bool http_server_enable(struct http_server_ctx *http_ctx) -{ - bool old; - - NET_ASSERT(http_ctx); - - old = http_ctx->enabled; - - http_ctx->enabled = true; - -#if defined(CONFIG_HTTPS) - if (http_ctx->is_https) { - https_enable(http_ctx); - } -#endif - return old; -} - -bool http_server_disable(struct http_server_ctx *http_ctx) -{ - bool old; - - NET_ASSERT(http_ctx); - - req_timer_cancel(http_ctx); - - old = http_ctx->enabled; - - http_ctx->enabled = false; - -#if defined(CONFIG_HTTPS) - if (http_ctx->is_https) { - https_disable(http_ctx); - } -#endif - return old; -} - -int http_server_init(struct http_server_ctx *http_ctx, +int http_server_init(struct http_ctx *ctx, struct http_server_urls *urls, struct sockaddr *server_addr, u8_t *request_buf, size_t request_buf_len, - const char *server_banner) + const char *server_banner, + void *user_data) { - int ret; - - if (http_ctx->urls) { - NET_ERR("Server context %p already initialized", http_ctx); - return -EINVAL; - } - - if (!request_buf || request_buf_len == 0) { - NET_ERR("Request buf must be set"); - return -EINVAL; - } - - ret = init_net(http_ctx, server_addr, HTTP_DEFAULT_PORT); - if (ret < 0) { - return ret; - } - - if (server_banner) { - new_server(http_ctx, server_banner, server_addr); - } - - http_ctx->req.request_buf = request_buf; - http_ctx->req.request_buf_len = request_buf_len; - http_ctx->req.data_len = 0; - http_ctx->urls = urls; - http_ctx->recv_cb = http_recv; - http_ctx->send_data = net_context_send; - - k_delayed_work_init(&http_ctx->req.timer, req_timeout); - - parser_init(http_ctx); - - return 0; -} - -void http_server_release(struct http_server_ctx *http_ctx) -{ - if (!http_ctx->urls) { - return; - } - - http_server_disable(http_ctx); - -#if defined(CONFIG_NET_IPV4) - if (http_ctx->net_ipv4_ctx) { - net_context_put(http_ctx->net_ipv4_ctx); - http_ctx->net_ipv4_ctx = NULL; - } -#endif -#if defined(CONFIG_NET_IPV6) - if (http_ctx->net_ipv6_ctx) { - net_context_put(http_ctx->net_ipv6_ctx); - http_ctx->net_ipv6_ctx = NULL; - } -#endif - - http_ctx->req.net_ctx = NULL; - http_ctx->urls = NULL; -} - -#if defined(CONFIG_HTTPS) -struct rx_fifo_block { - sys_snode_t snode; - struct k_mem_block block; - struct net_pkt *pkt; -}; - -#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_HTTP) -static void my_debug(void *ctx, int level, - const char *file, int line, const char *str) -{ - const char *p, *basename; - int len; - - ARG_UNUSED(ctx); - - /* Extract basename from file */ - for (p = basename = file; *p != '\0'; p++) { - if (*p == '/' || *p == '\\') { - basename = p + 1; - } - - } - - /* Avoid printing double newlines */ - len = strlen(str); - if (str[len - 1] == '\n') { - ((char *)str)[len - 1] = '\0'; - } - - NET_DBG("%s:%04d: |%d| %s", basename, line, level, str); -} -#endif /* MBEDTLS_DEBUG_C && CONFIG_NET_DEBUG_HTTP */ - -#if defined(MBEDTLS_ERROR_C) -#define print_error(fmt, ret) \ - do { \ - char error[64]; \ - \ - mbedtls_strerror(ret, error, sizeof(error)); \ - \ - NET_ERR(fmt " (%s)", -ret, error); \ - } while (0) -#else -#define print_error(fmt, ret) \ - do { \ - NET_ERR(fmt, -ret); \ - } while (0) -#endif - -#define BUF_ALLOC_TIMEOUT 100 - -/* Receive encrypted data from network. Put that data into fifo - * that will be read by https thread. - */ -static void ssl_received(struct net_context *context, - struct net_pkt *pkt, - int status, - void *user_data) -{ - struct http_server_ctx *http_ctx = user_data; - struct rx_fifo_block *rx_data = NULL; - struct k_mem_block block; - int ret; - - ARG_UNUSED(context); - ARG_UNUSED(status); - - if (pkt && !net_pkt_appdatalen(pkt)) { - net_pkt_unref(pkt); - return; - } - - ret = k_mem_pool_alloc(http_ctx->https.pool, &block, - sizeof(struct rx_fifo_block), - BUF_ALLOC_TIMEOUT); - if (ret < 0) { - net_pkt_unref(pkt); - return; - } - - rx_data = block.data; - rx_data->pkt = pkt; - - /* For freeing memory later */ - memcpy(&rx_data->block, &block, sizeof(struct k_mem_block)); - - k_fifo_put(&http_ctx->https.mbedtls.ssl_ctx.rx_fifo, (void *)rx_data); -} - -/* This will copy data from received net_pkt buf into mbedtls internal buffers. - */ -static int ssl_rx(void *context, unsigned char *buf, size_t size) -{ - struct http_server_ctx *ctx = context; - struct rx_fifo_block *rx_data; - u16_t read_bytes; - u8_t *ptr; - int pos; - int len; int ret = 0; - if (!ctx->https.mbedtls.ssl_ctx.frag) { - rx_data = k_fifo_get(&ctx->https.mbedtls.ssl_ctx.rx_fifo, - K_FOREVER); - if (!rx_data->pkt) { - NET_DBG("Closing %p connection", ctx); - k_mem_pool_free(&rx_data->block); - return -EIO; - } - - ctx->https.mbedtls.ssl_ctx.rx_pkt = rx_data->pkt; - - k_mem_pool_free(&rx_data->block); - - read_bytes = net_pkt_appdatalen( - ctx->https.mbedtls.ssl_ctx.rx_pkt); - - ctx->https.mbedtls.ssl_ctx.remaining = read_bytes; - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.rx_pkt->frags; - - ptr = net_pkt_appdata(ctx->https.mbedtls.ssl_ctx.rx_pkt); - len = ptr - ctx->https.mbedtls.ssl_ctx.frag->data; - - if (len > ctx->https.mbedtls.ssl_ctx.frag->size) { - NET_ERR("Buf overflow (%d > %u)", len, - ctx->https.mbedtls.ssl_ctx.frag->size); - return -EINVAL; - } else { - /* This will get rid of IP header */ - net_buf_pull(ctx->https.mbedtls.ssl_ctx.frag, len); - } - } else { - read_bytes = ctx->https.mbedtls.ssl_ctx.remaining; - ptr = ctx->https.mbedtls.ssl_ctx.frag->data; - } - - len = ctx->https.mbedtls.ssl_ctx.frag->len; - pos = 0; - if (read_bytes > size) { - while (ctx->https.mbedtls.ssl_ctx.frag) { - read_bytes = len < (size - pos) ? len : (size - pos); - -#if RX_EXTRA_DEBUG == 1 - NET_DBG("Copying %d bytes", read_bytes); -#endif - - memcpy(buf + pos, ptr, read_bytes); - - pos += read_bytes; - if (pos < size) { - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.frag->frags; - ptr = ctx->https.mbedtls.ssl_ctx.frag->data; - len = ctx->https.mbedtls.ssl_ctx.frag->len; - } else { - if (read_bytes == len) { - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.frag->frags; - } else { - net_buf_pull( - ctx->https.mbedtls.ssl_ctx.frag, - read_bytes); - } - - ctx->https.mbedtls.ssl_ctx.remaining -= size; - return size; - } - } - } else { - while (ctx->https.mbedtls.ssl_ctx.frag) { -#if RX_EXTRA_DEBUG == 1 - NET_DBG("Copying all %d bytes", len); -#endif - - memcpy(buf + pos, ptr, len); - - pos += len; - ctx->https.mbedtls.ssl_ctx.frag = - ctx->https.mbedtls.ssl_ctx.frag->frags; - if (!ctx->https.mbedtls.ssl_ctx.frag) { - break; - } - - ptr = ctx->https.mbedtls.ssl_ctx.frag->data; - len = ctx->https.mbedtls.ssl_ctx.frag->len; - } - - net_pkt_unref(ctx->https.mbedtls.ssl_ctx.rx_pkt); - ctx->https.mbedtls.ssl_ctx.rx_pkt = NULL; - ctx->https.mbedtls.ssl_ctx.frag = NULL; - ctx->https.mbedtls.ssl_ctx.remaining = 0; - - if (read_bytes != pos) { - return -EIO; - } - - ret = read_bytes; - } - - return ret; -} - -static void ssl_sent(struct net_context *context, - int status, void *token, void *user_data) -{ - struct http_server_ctx *http_ctx = user_data; - - k_sem_give(&http_ctx->https.mbedtls.ssl_ctx.tx_sem); -} - -/* Send encrypted data */ -static int ssl_tx(void *context, const unsigned char *buf, size_t size) -{ - struct http_server_ctx *ctx = context; - struct net_pkt *send_buf; - int ret, len; - - send_buf = net_pkt_get_tx(ctx->req.net_ctx, BUF_ALLOC_TIMEOUT); - if (!send_buf) { - return MBEDTLS_ERR_SSL_ALLOC_FAILED; - } - - ret = net_pkt_append_all(send_buf, size, (u8_t *)buf, - BUF_ALLOC_TIMEOUT); - if (!ret) { - /* Cannot append data */ - net_pkt_unref(send_buf); - return 0; - } - - len = size; - - ret = net_context_send(send_buf, ssl_sent, K_NO_WAIT, NULL, ctx); - if (ret < 0) { - net_pkt_unref(send_buf); - return MBEDTLS_ERR_SSL_INTERNAL_ERROR; - } - - k_sem_take(&ctx->https.mbedtls.ssl_ctx.tx_sem, K_FOREVER); - - return len; -} - -static int entropy_source(void *data, unsigned char *output, size_t len, - size_t *olen) -{ - u32_t seed; - - ARG_UNUSED(data); - - seed = sys_rand32_get(); - - if (len > sizeof(seed)) { - len = sizeof(seed); - } - - memcpy(output, &seed, len); - - *olen = len; - return 0; -} - -/* This gets plain data and it sends encrypted one to peer */ -static int https_send(struct net_pkt *pkt, - net_context_send_cb_t cb, - s32_t timeout, - void *token, - void *user_data) -{ - struct http_server_ctx *ctx = user_data; - int ret; - u16_t len; - - len = net_pkt_appdatalen(pkt); - - ret = net_frag_linearize(ctx->req.request_buf, - ctx->req.request_buf_len, - pkt, net_pkt_ip_hdr_len(pkt), - len); - if (ret < 0) { - NET_DBG("Cannot linearize send data (%d)", ret); - return ret; - } - - if (ret != len) { - NET_DBG("Linear copy error (%u vs %d)", len, ret); + if (!ctx) { return -EINVAL; } - do { - ret = mbedtls_ssl_write(&ctx->https.mbedtls.ssl, - ctx->req.request_buf, len); - if (ret == MBEDTLS_ERR_NET_CONN_RESET) { - print_error("peer closed the connection -0x%x", ret); - goto out; - } - - if (ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - if (ret < 0) { - print_error("mbedtls_ssl_write returned " - "-0x%x", ret); - goto out; - } - } - } while (ret <= 0); - -out: - if (cb) { - cb(net_pkt_context(pkt), ret, token, user_data); - } - - return ret; -} - -static void https_handler(struct http_server_ctx *ctx) -{ - size_t len; - int ret; - - NET_DBG("HTTPS handler starting"); - - mbedtls_platform_set_printf(printk); - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - mbedtls_x509_crt_init(&ctx->https.mbedtls.srvcert); -#endif - - mbedtls_pk_init(&ctx->https.mbedtls.pkey); - mbedtls_ssl_init(&ctx->https.mbedtls.ssl); - mbedtls_ssl_config_init(&ctx->https.mbedtls.conf); - mbedtls_entropy_init(&ctx->https.mbedtls.entropy); - mbedtls_ctr_drbg_init(&ctx->https.mbedtls.ctr_drbg); - -#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_HTTP) - mbedtls_debug_set_threshold(DEBUG_THRESHOLD); - mbedtls_ssl_conf_dbg(&ctx->https.mbedtls.conf, my_debug, NULL); -#endif - - /* Load the certificates and private RSA key. This needs to be done - * by the user so we call a callback that user must have provided. - */ - ret = ctx->https.mbedtls.cert_cb(ctx, &ctx->https.mbedtls.srvcert, - &ctx->https.mbedtls.pkey); - if (ret != 0) { - goto exit; - } - - /* Seed the RNG */ - mbedtls_entropy_add_source(&ctx->https.mbedtls.entropy, - ctx->https.mbedtls.entropy_src_cb, - NULL, - MBEDTLS_ENTROPY_MAX_GATHER, - MBEDTLS_ENTROPY_SOURCE_STRONG); - - ret = mbedtls_ctr_drbg_seed( - &ctx->https.mbedtls.ctr_drbg, - mbedtls_entropy_func, - &ctx->https.mbedtls.entropy, - (const unsigned char *)ctx->https.mbedtls.personalization_data, - ctx->https.mbedtls.personalization_data_len); - if (ret != 0) { - print_error("mbedtls_ctr_drbg_seed returned -0x%x", ret); - goto exit; - } - - /* Setup SSL defaults etc. */ - ret = mbedtls_ssl_config_defaults(&ctx->https.mbedtls.conf, - MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - if (ret != 0) { - print_error("mbedtls_ssl_config_defaults returned -0x%x", ret); - goto exit; - } - - mbedtls_ssl_conf_rng(&ctx->https.mbedtls.conf, - mbedtls_ctr_drbg_random, - &ctx->https.mbedtls.ctr_drbg); - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - mbedtls_ssl_conf_ca_chain(&ctx->https.mbedtls.conf, - ctx->https.mbedtls.srvcert.next, - NULL); - - ret = mbedtls_ssl_conf_own_cert(&ctx->https.mbedtls.conf, - &ctx->https.mbedtls.srvcert, - &ctx->https.mbedtls.pkey); - if (ret != 0) { - print_error("mbedtls_ssl_conf_own_cert returned -0x%x", ret); - goto exit; - } -#endif /* MBEDTLS_X509_CRT_PARSE_C */ - - ret = mbedtls_ssl_setup(&ctx->https.mbedtls.ssl, - &ctx->https.mbedtls.conf); - if (ret != 0) { - print_error("mbedtls_ssl_setup returned -0x%x", ret); - goto exit; - } - -reset: - mbedtls_ssl_session_reset(&ctx->https.mbedtls.ssl); - mbedtls_ssl_set_bio(&ctx->https.mbedtls.ssl, ctx, ssl_tx, - ssl_rx, NULL); - - /* SSL handshake. The ssl_rx() function will be called next by - * mbedtls library. The ssl_rx() will block and wait that data is - * received by ssl_received() and passed to it via fifo. After - * receiving the data, this function will then proceed with secure - * connection establishment. - */ - /* Waiting SSL handshake */ - do { - ret = mbedtls_ssl_handshake(&ctx->https.mbedtls.ssl); - if (ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - if (ret < 0) { - goto reset; - } - } - } while (ret != 0); - - /* Read the HTTPS Request */ - NET_DBG("Read HTTPS request"); - do { - len = ctx->req.request_buf_len - 1; - memset(ctx->req.request_buf, 0, ctx->req.request_buf_len); - - ret = mbedtls_ssl_read(&ctx->https.mbedtls.ssl, - ctx->req.request_buf, len); - if (ret == MBEDTLS_ERR_SSL_WANT_READ || - ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - continue; - } - - if (ret <= 0) { - switch (ret) { - case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: - NET_DBG("Connection was closed gracefully"); - goto close; - - case MBEDTLS_ERR_NET_CONN_RESET: - NET_DBG("Connection was reset by peer"); - break; - - default: - print_error("mbedtls_ssl_read returned " - "-0x%x", ret); - break; - } - - goto close; - } - - ret = http_parser_execute(&ctx->req.parser, - &ctx->req.settings, - ctx->req.request_buf, - ret); - } while (ret < 0); - - /* Write the Response */ - NET_DBG("Write HTTPS response"); - - if (ctx->req.parser.http_errno != HPE_OK) { - http_response_400(ctx, NULL); - } else { - http_process_recv(ctx); - } - -close: - http_parser_init(&ctx->req.parser, HTTP_REQUEST); - ctx->req.field_values_ctr = 0; - - mbedtls_ssl_close_notify(&ctx->https.mbedtls.ssl); - - goto reset; - -exit: - return; -} - -static void https_enable(struct http_server_ctx *ctx) -{ - /* Start the thread that handles HTTPS traffic. */ - if (ctx->https.tid) { - return; - } - - ctx->https.tid = k_thread_create(&ctx->https.thread, - ctx->https.stack, - ctx->https.stack_size, - (k_thread_entry_t)https_handler, - ctx, NULL, NULL, - K_PRIO_COOP(7), 0, 0); -} - -static void https_disable(struct http_server_ctx *ctx) -{ - if (!ctx->https.tid) { - return; - } - - mbedtls_ssl_free(&ctx->https.mbedtls.ssl); - mbedtls_ssl_config_free(&ctx->https.mbedtls.conf); - mbedtls_ctr_drbg_free(&ctx->https.mbedtls.ctr_drbg); - mbedtls_entropy_free(&ctx->https.mbedtls.entropy); - - /* Empty the fifo just in case there is any received packets - * still there. - */ - while (1) { - struct rx_fifo_block *rx_data; - - rx_data = k_fifo_get(&ctx->https.mbedtls.ssl_ctx.rx_fifo, - K_NO_WAIT); - if (!rx_data) { - break; - } - - net_pkt_unref(rx_data->pkt); - - k_mem_pool_free(&rx_data->block); - } - - NET_DBG("HTTPS thread %p stopped", ctx->https.tid); - - k_thread_abort(ctx->https.tid); - ctx->https.tid = 0; -} - -static int https_init(struct http_server_ctx *ctx) -{ - k_sem_init(&ctx->https.mbedtls.ssl_ctx.tx_sem, 0, UINT_MAX); - k_fifo_init(&ctx->https.mbedtls.ssl_ctx.rx_fifo); - - /* Next we return to application which must then enable the HTTPS - * service. The enable function will then start the https thread and - * do what ever further configuration needed. - * - * We do the mbedtls initialization in its own thread because it uses - * of of stack and the main stack runs out of memory very easily. - * - * See https_handler() how the things proceed from now on. - */ - - return 0; -} - -int https_server_init(struct http_server_ctx *ctx, - struct http_server_urls *urls, - struct sockaddr *server_addr, - u8_t *request_buf, - size_t request_buf_len, - const char *server_banner, - u8_t *personalization_data, - size_t personalization_data_len, - https_server_cert_cb_t cert_cb, - https_entropy_src_cb_t entropy_src_cb, - struct k_mem_pool *pool, - k_thread_stack_t *https_stack, - size_t https_stack_size) -{ - int ret; - - if (ctx->urls) { - NET_ERR("Server context %p already initialized", ctx); + if (ctx->is_init) { return -EALREADY; } @@ -1638,58 +847,72 @@ int https_server_init(struct http_server_ctx *ctx, return -EINVAL; } - if (!cert_cb) { - NET_ERR("Cert callback must be set"); - return -EINVAL; - } + memset(ctx, 0, sizeof(*ctx)); - ret = init_net(ctx, server_addr, HTTPS_DEFAULT_PORT); - if (ret < 0) { - return ret; - } + init_net(ctx, server_addr, HTTP_DEFAULT_PORT); if (server_banner) { new_server(ctx, server_banner, server_addr); } - ctx->req.request_buf = request_buf; - ctx->req.request_buf_len = request_buf_len; - ctx->req.data_len = 0; - ctx->urls = urls; - ctx->is_https = true; - ctx->https.stack = https_stack; - ctx->https.stack_size = https_stack_size; - ctx->https.mbedtls.cert_cb = cert_cb; - ctx->https.pool = pool; + /* Timeout for network buffer allocations */ + ctx->timeout = BUF_ALLOC_TIMEOUT; - if (entropy_src_cb) { - ctx->https.mbedtls.entropy_src_cb = entropy_src_cb; - } else { - ctx->https.mbedtls.entropy_src_cb = entropy_source; + ctx->http.request_buf = request_buf; + ctx->http.request_buf_len = request_buf_len; + ctx->http.urls = urls; + ctx->http.data_len = 0; + ctx->user_data = user_data; + ctx->server_addr = server_addr; + + ret = net_app_init_tcp_server(&ctx->app_ctx, + &ctx->local, + HTTP_DEFAULT_PORT, + ctx); + if (ret < 0) { + NET_ERR("Cannot create http server (%d)", ret); + return ret; } - ctx->https.mbedtls.personalization_data = personalization_data; - ctx->https.mbedtls.personalization_data_len = personalization_data_len; - ctx->send_data = https_send; - ctx->recv_cb = ssl_received; + ret = net_app_set_cb(&ctx->app_ctx, NULL, http_received, + http_data_sent, http_closed); + if (ret < 0) { + NET_ERR("Cannot set callbacks (%d)", ret); + goto quit; + } - k_delayed_work_init(&ctx->req.timer, req_timeout); + init_http_parser(ctx); - parser_init(ctx); + ctx->is_init = true; + return 0; - /* Then mbedtls specific initialization */ - return https_init(ctx); +quit: + net_app_release(&ctx->app_ctx); + return ret; } -#endif /* CONFIG_HTTPS */ -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) -int http_server_set_net_pkt_pool(struct http_server_ctx *ctx, - net_pkt_get_slab_func_t tx_slab, - net_pkt_get_pool_func_t data_pool) +int http_server_enable(struct http_ctx *ctx) { - ctx->tx_slab = tx_slab; - ctx->data_pool = data_pool; + int ret; + + NET_ASSERT(ctx); + + net_app_server_enable(&ctx->app_ctx); + + ret = net_app_listen(&ctx->app_ctx); + if (ret < 0) { + NET_ERR("Cannot wait connection (%d)", ret); + return false; + } + + return 0; +} + +int http_server_disable(struct http_ctx *ctx) +{ + NET_ASSERT(ctx); + + net_app_server_disable(&ctx->app_ctx); return 0; } -#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */