From 59e8b872a1264fcd3c42a70f97ba0de921ee576a Mon Sep 17 00:00:00 2001
From: Jukka Rissanen
Date: Tue, 25 Apr 2017 17:58:18 +0300
Subject: [PATCH 1/2] http: client: Create a HTTP library
Instead of separate sample application that does everything
related to HTTP client connectivity, create a HTTP client library
that hides nasty details that are related to sending HTTP methods.
After this the sample HTTP client application is very simple and
only shows how to use the client HTTP API.
Signed-off-by: Jukka Rissanen
---
include/net/http.h | 383 +++++++-
include/net/http_parser.h | 6 +-
samples/net/http_client/Makefile | 2 +-
samples/net/http_client/README.rst | 197 +---
samples/net/http_client/prj_frdm_k64f.conf | 19 +-
samples/net/http_client/prj_qemu_x86.conf | 35 +-
samples/net/http_client/src/Makefile | 4 -
samples/net/http_client/src/config.h | 73 +-
samples/net/http_client/src/http_client.c | 44 -
samples/net/http_client/src/http_client.h | 17 -
samples/net/http_client/src/http_client_cb.c | 133 ---
samples/net/http_client/src/http_client_cb.h | 38 -
samples/net/http_client/src/http_client_rcv.c | 101 --
samples/net/http_client/src/http_client_rcv.h | 15 -
.../net/http_client/src/http_client_types.h | 37 -
samples/net/http_client/src/main.c | 361 +++++--
samples/net/http_client/src/tcp_client.c | 162 ----
samples/net/http_client/src/tcp_client.h | 31 -
subsys/net/lib/http/Kconfig | 26 +-
subsys/net/lib/http/http_client.c | 884 ++++++++++++++++--
subsys/net/lib/http/http_parser.c | 6 +-
21 files changed, 1553 insertions(+), 1021 deletions(-)
delete mode 100644 samples/net/http_client/src/http_client.c
delete mode 100644 samples/net/http_client/src/http_client.h
delete mode 100644 samples/net/http_client/src/http_client_cb.c
delete mode 100644 samples/net/http_client/src/http_client_cb.h
delete mode 100644 samples/net/http_client/src/http_client_rcv.c
delete mode 100644 samples/net/http_client/src/http_client_rcv.h
delete mode 100644 samples/net/http_client/src/http_client_types.h
delete mode 100644 samples/net/http_client/src/tcp_client.c
delete mode 100644 samples/net/http_client/src/tcp_client.h
diff --git a/include/net/http.h b/include/net/http.h
index 9e3c38fedefd7..8de5694c5badc 100644
--- a/include/net/http.h
+++ b/include/net/http.h
@@ -9,45 +9,392 @@
#if defined(CONFIG_HTTP_CLIENT)
+#include
#include
+#define HTTP_CRLF "\r\n"
+
+/* 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 overriden if needed. */
+#ifndef HTTP_STATUS_STR_SIZE
+#define HTTP_STATUS_STR_SIZE 32
+#endif
+
+/* Default network activity timeout in seconds */
+#define HTTP_NETWORK_TIMEOUT K_SECONDS(20)
+
+/* 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.
+ */
+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
+ */
+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);
+
+/**
+ * HTTP client context information. This contains all the data that is
+ * needed when doing HTTP requests.
+ */
+struct http_client_ctx {
+ struct http_parser parser;
+ struct http_parser_settings settings;
+
+ /** Server name */
+ const char *server;
+
+#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.
+ */
+ http_receive_cb_t receive_cb;
+ } 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;
+ } rsp;
+};
+
+/**
+ * HTTP client request. This contains all the data that is needed when doing
+ * a HTTP request.
+ */
struct http_client_request {
- /** The HTTP method: GET, HEAD, OPTIONS, POST */
- char *method;
+ /** The HTTP method: GET, HEAD, OPTIONS, POST, ... */
+ enum http_method method;
+
/** The URL for this request, for example: /index.html */
- char *url;
+ const char *url;
+
/** The HTTP protocol: HTTP/1.1 */
- char *protocol;
+ 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.
*/
- char *header_fields;
+ const char *header_fields;
+
/** The value of the Content-Type header field, may be NULL */
- char *content_type_value;
+ const char *content_type_value;
+
+ /** Hostname to be used in the request */
+ const char *host;
+
/** Payload, may be NULL */
- char *payload;
+ const char *payload;
+
/** Payload size, may be 0 */
u16_t payload_size;
};
-int http_request(struct net_context *net_ctx, s32_t timeout,
- struct http_client_request *req);
+/**
+ * @brief Generic function to send a HTTP request to the network. Normally
+ * applications would not need to use this function.
+ *
+ * @param net_ctx Network context.
+ * @param req HTTP request to perform.
+ * @param timeout Timeout when doing net_buf allocations.
+ *
+ * @return Return 0 if ok, and <0 if error.
+ */
+int http_request(struct net_context *net_ctx, struct http_client_request *req,
+ s32_t timeout);
-int http_request_get(struct net_context *net_ctx, s32_t timeout, char *url,
- char *header_fields);
+/**
+ * @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 suppied 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_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);
-int http_request_head(struct net_context *net_ctx, s32_t timeout, char *url,
- char *header_fields);
+/**
+ * @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 suppied 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_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,
+ };
-int http_request_options(struct net_context *net_ctx, s32_t timeout,
- char *url, char *header_fields);
+ return http_client_send_req(http_ctx, &req, cb, response_buf,
+ response_buf_len, user_data, timeout);
+}
-int http_request_post(struct net_context *net_ctx, s32_t timeout, char *url,
- char *header_fields, char *content_type_value,
- char *payload);
+/**
+ * @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 suppied 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_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 it 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.
+ */
+int http_client_init(struct http_client_ctx *http_ctx,
+ const char *server, u16_t server_port);
+
+/**
+ * @brief Release all the resources allocated for HTTP context.
+ *
+ * @param http_ctx HTTP context.
+ */
+void http_client_release(struct http_client_ctx *http_ctx);
#endif
#if defined(CONFIG_HTTP_SERVER)
diff --git a/include/net/http_parser.h b/include/net/http_parser.h
index 28e0fa59ccf33..ea31fff128cba 100644
--- a/include/net/http_parser.h
+++ b/include/net/http_parser.h
@@ -285,9 +285,9 @@ void http_parser_settings_init(struct http_parser_settings *settings);
* `parser->http_errno` on error.
*/
-int http_parser_execute(struct http_parser *parser,
- const struct http_parser_settings *settings,
- const char *data, size_t len);
+size_t http_parser_execute(struct http_parser *parser,
+ const struct http_parser_settings *settings,
+ const char *data, size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
diff --git a/samples/net/http_client/Makefile b/samples/net/http_client/Makefile
index ab97d7df90014..049778d9bc0bf 100644
--- a/samples/net/http_client/Makefile
+++ b/samples/net/http_client/Makefile
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: Apache-2.0
#
-BOARD ?= frdm_k64f
+BOARD ?= qemu_x86
CONF_FILE ?= prj_$(BOARD).conf
include $(ZEPHYR_BASE)/Makefile.inc
diff --git a/samples/net/http_client/README.rst b/samples/net/http_client/README.rst
index 02b0d0df682ac..9574cad1fdc97 100644
--- a/samples/net/http_client/README.rst
+++ b/samples/net/http_client/README.rst
@@ -16,17 +16,17 @@ The source code for this sample application can be found at:
Requirements
************
-- Freedom Board (FRDM-K64F)
-- LAN for testing purposes (Ethernet)
+- QEMU
- Terminal emulator software
- HTTP Server
+- DNS server (optional)
Building and Running
********************
Open the project configuration file for your platform, for example:
-:file:`prj_frdm_k64f.conf` is the configuration file for the
-:ref:`frdm_k64f` board. For IPv4 networks, set the following variables:
+:file:`prj_qemu_x86.conf` is the configuration file for QEMU.
+For IPv4 networks, set the following variables:
.. code-block:: console
@@ -36,8 +36,8 @@ Open the project configuration file for your platform, for example:
IPv6 is the preferred routing technology for this sample application,
if CONFIG_NET_IPV6=y is set, the value of CONFIG_NET_IPV4=y is ignored.
-In this sample application, only static IP addresses are supported,
-those addresses are specified in the project configuration file,
+In this sample application, both static IP addresses and DHCPv4 are supported.
+Static IP addresses are specified in the project configuration file,
for example:
.. code-block:: console
@@ -46,10 +46,14 @@ for example:
CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2"
are the IPv6 addresses for the HTTP client running Zephyr and the
-HTTP server, respectively.
+HTTP server, respectively. The application also supports DNS resolving so the
+peer address is resolved automatically if host name is given, so you
+can also write the HTTP server name like this:
-Alternatively, the IP addresses may be specified in the
-:file:`src/config.h` file.
+.. code-block:: console
+
+ CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
+ CONFIG_NET_APP_PEER_IPV6_ADDR="6.zephyr.test"
Open the :file:`src/config.h` file and set the server port
to match the HTTP server setup, for example:
@@ -59,173 +63,68 @@ to match the HTTP server setup, for example:
#define SERVER_PORT 80
assumes that the HTTP server is listening at the TCP port 80.
+If the default example HTTP server is used, then the default
+port is 8000.
HTTP Server
===========
-Setting up an HTTP server on your host computer is beyond the scope
-of this document.
-(We used `Apache 2 `_
-for testing this sample application.
-
-However, this application assumes that there is a server's
-resource that can process an HTTP 1.1 POST request.
-
-For example, assuming that the Apache 2 server with PHP support
-is used, and that the client sends a POST request with
-"Content-Type = application/x-www-form-urlencoded" the following
-PHP script will echo the POST payload back to the client:
-
-.. code-block:: html
-
-
-
- HTTP Server POST test
-
-
- POST key/values:
';
- foreach ($_POST as $key => $value) {
- echo " {$key} : {$value}
";
- }
- ?>
-
-
-
-In the machine hosting the HTTP server, this php script is at
-:file:`/var/www/html/post_test.php`. However, for your test machine
-this path can be different, but should be at your server's root folder.
-
-HTTP Responses
-==============
-
-Server's responses are processed by the http_receive_cb routine
-defined inside the :file:`src/http_client_rcv.c` file.
-
-This sample application only prints the HTTP header fields via
-the HTTP Parser Library, see :file:`include/net/http_parser.h`.
-To process the HTTP response's body, use the HTTP Parser's callbacks
-to determine where the body begins. Depending on the payload's size,
-it may be necessary to traverse the network buffer's fragment chain.
-See the :file:`src/http_client_rcv.c` file at line 70 for sample code
-that shows how to walk the fragment chain.
-
-FRDM K64F
-=========
+A very simple HTTP server is provided in net-tool project.
+
+The net-tools can be downloaded from
+
+ https://github.com/zephyrproject-rtos/net-tools
+
Open a terminal window and type:
.. code-block:: console
- $ make BOARD=frdm_k64f
+ $ cd net-tools
+ $ ./http-server.sh
-The FRDM K64F board is detected as a USB storage device. The board
-must be mounted (i.e. to /mnt) to 'flash' the binary:
-.. code-block:: console
+DNS setup
+=========
- $ cp outdir/frdm_k64f/zephyr.bin /mnt
+The net-tool project provides a simple DNS resolver. You can activate
+it like this if you want to test the DNS resolving with HTTP client.
-On Linux, use the 'dmesg' program to find the right USB device for the
-FRDM serial console. Assuming that this device is ttyACM0, open a
-terminal window and type:
+Open a terminal window and type:
.. code-block:: console
- $ screen /dev/ttyACM0 115200
-
-Once the binary is loaded into the FRDM board, press the RESET button.
+ $ cd net-tools
+ $ ./dnsmasq.sh
-Refer to the board documentation in Zephyr, :ref:`frdm_k64f`,
-for more information about this board and how to access the FRDM
-serial console under other operating systems.
Sample Output
=============
-This sample application loops a specified number of times doing four
-HTTP 1.1 requests and displays the header fields that were extracted
-from the server's response. The four requests are:
+This sample application loops a specified number of times doing several
+HTTP 1.1 requests and printing some output. The requests are:
- GET "/index.html"
- HEAD "/"
-- OPTIONS "/"
+- OPTIONS "/index.html"
- POST "/post_test.php"
+- GET "/big-file.html"
-The terminal window where screen is running will show something similar
+The terminal window where QEMU is running will show something similar
to the following:
.. code-block:: console
- *******************************************
- HTTP Client: 2001:db8::1
- Connecting to: 2001:db8::2 port 80
- Hostname: 2001:db8::2
- HTTP Request: GET
-
- --------- HTTP response (headers) ---------
- Date: Thu, 02 Feb 2017 00:51:31 GMT
- Server: Apache/2.4.10 (Debian)
- Last-Modified: Sat, 28 Jan 2017 02:55:09 GMT
- ETag: "3c-5471eb5db3c73"
- Accept-Ranges: bytes
- Content-Length: 60
- Connection: close
- Content-Type: text/html
-
- HTTP server response status: OK
- HTTP parser status: success
- HTTP body: 60 bytes, expected: 60 bytes
-
- *******************************************
- HTTP Client: 2001:db8::1
- Connecting to: 2001:db8::2 port 80
- Hostname: 2001:db8::2
- HTTP Request: HEAD
-
- --------- HTTP response (headers) ---------
- Date: Thu, 02 Feb 2017 00:51:37 GMT
- Server: Apache/2.4.10 (Debian)
- Last-Modified: Sat, 28 Jan 2017 02:55:09 GMT
- ETag: "3c-5471eb5db3c73"
- Accept-Ranges: bytes
- Content-Length: 60
- Connection: close
- Content-Type: text/html
-
- HTTP server response status: OK
- HTTP parser status: success
-
- *******************************************
- HTTP Client: 2001:db8::1
- Connecting to: 2001:db8::2 port 80
- Hostname: 2001:db8::2
- HTTP Request: OPTIONS
-
- --------- HTTP response (headers) ---------
- Date: Thu, 02 Feb 2017 00:51:43 GMT
- Server: Apache/2.4.10 (Debian)
- Allow: GET,HEAD,POST,OPTIONS
- Content-Length: 0
- Connection: close
- Content-Type: text/html
-
- HTTP server response status: OK
- HTTP parser status: success
-
- *******************************************
- HTTP Client: 2001:db8::1
- Connecting to: 2001:db8::2 port 80
- Hostname: 2001:db8::2
- HTTP Request: POST
-
- --------- HTTP response (headers) ---------
- Date: Thu, 02 Feb 2017 00:51:49 GMT
- Server: Apache/2.4.10 (Debian)
- Vary: Accept-Encoding
- Content-Length: 231
- Connection: close
- Content-Type: text/html; charset=UTF-8
-
- HTTP server response status: OK
- HTTP parser status: success
+ [http-client] [INF] response: Received 364 bytes piece of data
+ [http-client] [INF] response: HTTP server response status: OK
+ [http-client] [INF] response: HTTP body: 178 bytes, expected: 178 bytes
+ [http-client] [INF] response: HTTP server response status: OK
+ [http-client] [INF] response: HTTP server response status: Unsupported method ('OPTIONS')
+ [http-client] [INF] response: Received 163 bytes piece of data
+ [http-client] [INF] response: HTTP server response status: OK
+ [http-client] [INF] response: HTTP body: 24 bytes, expected: 24 bytes
+ [http-client] [INF] response: Received 657 bytes piece of data
+ [http-client] [INF] response: Received 640 bytes piece of data
+ [http-client] [INF] response: Received 446 bytes piece of data
+ [http-client] [INF] response: HTTP server response status: OK
+ [http-client] [INF] response: HTTP body: 1556 bytes, expected: 1556 bytes
diff --git a/samples/net/http_client/prj_frdm_k64f.conf b/samples/net/http_client/prj_frdm_k64f.conf
index 7042142f4d218..36f39d35b3fb0 100644
--- a/samples/net/http_client/prj_frdm_k64f.conf
+++ b/samples/net/http_client/prj_frdm_k64f.conf
@@ -4,6 +4,8 @@ CONFIG_NETWORKING=y
CONFIG_NET_TCP=y
CONFIG_NET_ARP=y
CONFIG_NET_L2_ETHERNET=y
+CONFIG_NET_LOG=y
+CONFIG_NET_SHELL=y
CONFIG_NET_IPV6_RA_RDNSS=y
CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3
@@ -13,11 +15,11 @@ CONFIG_NET_PKT_TX_COUNT=64
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
-CONFIG_NET_IPV4=n
-CONFIG_NET_IPV6=y
+CONFIG_NET_IPV4=y
+CONFIG_NET_DHCPV4=y
+CONFIG_NET_IPV6=n
CONFIG_HTTP_CLIENT=y
-CONFIG_HTTP_PARSER=y
CONFIG_STDOUT_CONSOLE=y
@@ -27,11 +29,8 @@ CONFIG_STDOUT_CONSOLE=y
CONFIG_NET_APP_SETTINGS=y
CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2"
-CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101"
-CONFIG_NET_APP_PEER_IPV4_ADDR="192.168.1.10"
+CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
+CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2"
-#CONFIG_MAIN_STACK_SIZE=8192
-
-# See the config.h file and the LINEARIZE_BUFFER define
-#
-#CONFIG_NET_BUF_DATA_SIZE=512
+CONFIG_NET_MGMT=y
+CONFIG_NET_MGMT_EVENT=y
diff --git a/samples/net/http_client/prj_qemu_x86.conf b/samples/net/http_client/prj_qemu_x86.conf
index 33b6ab3c4f9d6..3601c80d36801 100644
--- a/samples/net/http_client/prj_qemu_x86.conf
+++ b/samples/net/http_client/prj_qemu_x86.conf
@@ -12,17 +12,20 @@ CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_IPV6_RA_RDNSS=y
-CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
-CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2
+CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
+CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
-# Enable IPv6 support
-CONFIG_NET_IPV6=n
-# Enable IPv4 support
-CONFIG_NET_IPV4=y
+CONFIG_INIT_STACKS=y
+
+CONFIG_SYS_LOG_SHOW_COLOR=y
+CONFIG_SYS_LOG_NET_LEVEL=4
+#CONFIG_NET_DEBUG_HTTP=y
CONFIG_HTTP_CLIENT=y
-CONFIG_HTTP_PARSER=y
-CONFIG_STDOUT_CONSOLE=y
+
+CONFIG_NET_IPV6=y
+CONFIG_NET_IPV4=y
+CONFIG_NET_DHCPV4=y
CONFIG_NET_APP_SETTINGS=y
CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
@@ -30,3 +33,19 @@ CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2"
CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2"
+
+CONFIG_NET_SHELL=y
+
+CONFIG_DNS_RESOLVER=y
+CONFIG_DNS_RESOLVER_ADDITIONAL_BUF_CTR=2
+CONFIG_DNS_RESOLVER_ADDITIONAL_QUERIES=2
+CONFIG_DNS_RESOLVER_MAX_SERVERS=2
+CONFIG_DNS_SERVER_IP_ADDRESSES=y
+CONFIG_DNS_NUM_CONCUR_QUERIES=2
+
+CONFIG_NET_MGMT=y
+CONFIG_NET_MGMT_EVENT=y
+
+# Example DNS servers running in linux using dnsmasq
+CONFIG_DNS_SERVER1="192.0.2.2:5353"
+CONFIG_DNS_SERVER2="[2001:db8::2]:5353"
diff --git a/samples/net/http_client/src/Makefile b/samples/net/http_client/src/Makefile
index 32a9e074c365c..587479094c8dd 100644
--- a/samples/net/http_client/src/Makefile
+++ b/samples/net/http_client/src/Makefile
@@ -5,7 +5,3 @@
#
obj-y += main.o
-obj-y += tcp_client.o
-obj-y += http_client.o
-obj-y += http_client_rcv.o
-obj-y += http_client_cb.o
diff --git a/samples/net/http_client/src/config.h b/samples/net/http_client/src/config.h
index 93a5f986fc39c..b005216b7df51 100644
--- a/samples/net/http_client/src/config.h
+++ b/samples/net/http_client/src/config.h
@@ -4,71 +4,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
-#include
+#define APP_REQ_TIMEOUT K_SECONDS(5)
-#define APP_NAP_TIME 3000
+/* The startup time needs to be longish if DHCP is enabled as setting
+ * DHCP up takes some time.
+ */
+#define APP_STARTUP_TIME K_SECONDS(20)
-#define HTTP_POOL_BUF_CTR 4
-#define HTTP_POOL_BUF_SIZE 1024
-#define HTTP_STATUS_STR_SIZE 32
+#define POST_CONTENT_TYPE "application/x-www-form-urlencoded"
+#define POST_PAYLOAD "os=ZephyrRTOS&arch=" CONFIG_ARCH
-/* server port */
-#define SERVER_PORT 80
-/* rx tx timeout */
-#define HTTP_NETWORK_TIMEOUT 300
+#define SERVER_PORT 8000
-#ifdef CONFIG_NET_APP_SETTINGS
-#ifdef CONFIG_NET_IPV6
-#define LOCAL_ADDR CONFIG_NET_APP_MY_IPV6_ADDR
-#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV6_ADDR
-#else
-#define LOCAL_ADDR CONFIG_NET_APP_MY_IPV4_ADDR
-#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV4_ADDR
-#endif
-#else
-#ifdef CONFIG_NET_IPV6
-#define LOCAL_ADDR "2001:db8::1"
-#define SERVER_ADDR "2001:db8::2"
+#if defined(CONFIG_NET_IPV6)
+#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV6_ADDR
#else
-#define LOCAL_ADDR "192.168.1.101"
-#define SERVER_ADDR "192.168.1.10"
-#endif
-#endif /* CONFIG */
-
-/* It seems enough to hold 'Content-Length' and its value */
-#define CON_LEN_SIZE 48
-
-/* Default HTTP Header Field values for HTTP Requests */
-#define ACCEPT "text/plain"
-#define ACCEPT_ENC "identity"
-#define ACCEPT_LANG "en-US"
-#define CONNECTION "Close"
-#define USER_AGENT "ZephyrHTTPClient/1.7"
-#define HOST_NAME SERVER_ADDR /* or example.com, www.example.com */
-
-#define HEADER_FIELDS "Accept: "ACCEPT"\r\n" \
- "Accept-Encoding: "ACCEPT_ENC"\r\n" \
- "Accept-Language: "ACCEPT_LANG"\r\n" \
- "User-Agent: "USER_AGENT"\r\n" \
- "Host: "HOST_NAME"\r\n" \
- "Connection: "CONNECTION"\r\n"
-
-/* Parsing and token tracking becomes a bit complicated if the
- * RX buffer is fragmented. for example: an HTTP response with
- * header fields that lie in two fragments. So, here we have
- * two options:
- *
- * - Use the fragmented buffer, but increasing the fragment size
- * - Linearize the buffer, it works better but consumes more memory
- *
- * Comment the following define to test the first case, set the
- * CONFIG_NET_BUF_DATA_SIZE variable to 384 or 512. See the
- * prj_frdm_k64f.conf file.
- */
-#define LINEARIZE_BUFFER
-
-#ifndef LINEARIZE_BUFFER
-#if CONFIG_NET_BUF_DATA_SIZE <= 256
-#error set CONFIG_NET_BUF_DATA_SIZE to 384 or 512
-#endif
+#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV4_ADDR
#endif
diff --git a/samples/net/http_client/src/http_client.c b/samples/net/http_client/src/http_client.c
deleted file mode 100644
index fe6cdd8aaad93..0000000000000
--- a/samples/net/http_client/src/http_client.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include "http_client.h"
-#include "http_client_rcv.h"
-#include "http_client_cb.h"
-#include "config.h"
-
-#include
-
-int http_init(struct http_client_ctx *http_ctx)
-{
- memset(http_ctx, 0, sizeof(struct http_client_ctx));
-
- http_ctx->settings.on_body = on_body;
- http_ctx->settings.on_chunk_complete = on_chunk_complete;
- http_ctx->settings.on_chunk_header = on_chunk_header;
- http_ctx->settings.on_headers_complete = on_headers_complete;
- http_ctx->settings.on_header_field = on_header_field;
- http_ctx->settings.on_header_value = on_header_value;
- http_ctx->settings.on_message_begin = on_message_begin;
- http_ctx->settings.on_message_complete = on_message_complete;
- http_ctx->settings.on_status = on_status;
- http_ctx->settings.on_url = on_url;
-
- return 0;
-}
-
-int http_reset_ctx(struct http_client_ctx *http_ctx)
-{
- http_parser_init(&http_ctx->parser, HTTP_RESPONSE);
-
- memset(http_ctx->http_status, 0, sizeof(http_ctx->http_status));
-
- http_ctx->cl_present = 0;
- http_ctx->content_length = 0;
- http_ctx->processed = 0;
- http_ctx->body_found = 0;
-
- return 0;
-}
diff --git a/samples/net/http_client/src/http_client.h b/samples/net/http_client/src/http_client.h
deleted file mode 100644
index 58eaf58ddc8df..0000000000000
--- a/samples/net/http_client/src/http_client.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef _HTTP_CLIENT_H_
-#define _HTTP_CLIENT_H_
-
-#include "http_client_types.h"
-#include "http_client_rcv.h"
-
-int http_init(struct http_client_ctx *http_ctx);
-
-int http_reset_ctx(struct http_client_ctx *http_ctx);
-
-#endif
diff --git a/samples/net/http_client/src/http_client_cb.c b/samples/net/http_client/src/http_client_cb.c
deleted file mode 100644
index 767512ca8080b..0000000000000
--- a/samples/net/http_client/src/http_client_cb.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include "http_client_cb.h"
-#include "http_client_types.h"
-
-#include
-#include
-
-#define MAX_NUM_DIGITS 16
-
-int on_url(struct http_parser *parser, const char *at, size_t length)
-{
- ARG_UNUSED(parser);
-
- printf("URL: %.*s\n", (int)length, at);
-
- return 0;
-}
-
-int on_status(struct http_parser *parser, const char *at, size_t length)
-{
- struct http_client_ctx *ctx;
- u16_t len;
-
- ARG_UNUSED(parser);
-
- ctx = CONTAINER_OF(parser, struct http_client_ctx, parser);
- len = min(length, sizeof(ctx->http_status) - 1);
- memcpy(ctx->http_status, at, len);
- ctx->http_status[len] = 0;
-
- return 0;
-}
-
-int on_header_field(struct http_parser *parser, const char *at, size_t length)
-{
- char *content_len = "Content-Length";
- struct http_client_ctx *ctx;
- 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->cl_present = 1;
- }
-
- printf("%.*s: ", (int)length, at);
-
- return 0;
-}
-
-int on_header_value(struct http_parser *parser, const char *at, size_t length)
-{
- struct http_client_ctx *ctx;
- char str[MAX_NUM_DIGITS];
-
- ctx = CONTAINER_OF(parser, struct http_client_ctx, parser);
-
- if (ctx->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->content_length = num;
- }
-
- ctx->cl_present = 0;
- }
-
- printf("%.*s\n", (int)length, at);
-
- return 0;
-}
-
-int on_body(struct http_parser *parser, const char *at, size_t length)
-{
- struct http_client_ctx *ctx;
-
- ctx = CONTAINER_OF(parser, struct http_client_ctx, parser);
-
- ctx->body_found = 1;
- ctx->processed += length;
-
- return 0;
-}
-
-int on_headers_complete(struct http_parser *parser)
-{
- ARG_UNUSED(parser);
-
- return 0;
-}
-
-int on_message_begin(struct http_parser *parser)
-{
- ARG_UNUSED(parser);
-
- printf("\n--------- HTTP response (headers) ---------\n");
-
- return 0;
-}
-
-int on_message_complete(struct http_parser *parser)
-{
- ARG_UNUSED(parser);
-
- return 0;
-}
-
-int on_chunk_header(struct http_parser *parser)
-{
- ARG_UNUSED(parser);
-
- return 0;
-}
-
-int on_chunk_complete(struct http_parser *parser)
-{
- ARG_UNUSED(parser);
-
- return 0;
-}
diff --git a/samples/net/http_client/src/http_client_cb.h b/samples/net/http_client/src/http_client_cb.h
deleted file mode 100644
index 457ee15e8cdb4..0000000000000
--- a/samples/net/http_client/src/http_client_cb.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef _HTTP_CLIENT_CB_H_
-#define _HTTP_CLIENT_CB_H_
-
-#include
-
-/*
- * This are the callbacks executed by the parser. Some of them
- * are only useful when parsing requests (or responses).
- * Unused callbacks may be removed.
- */
-
-int on_url(struct http_parser *parser, const char *at, size_t length);
-
-int on_status(struct http_parser *parser, const char *at, size_t length);
-
-int on_header_field(struct http_parser *parser, const char *at, size_t length);
-
-int on_header_value(struct http_parser *parser, const char *at, size_t length);
-
-int on_body(struct http_parser *parser, const char *at, size_t length);
-
-int on_headers_complete(struct http_parser *parser);
-
-int on_message_begin(struct http_parser *parser);
-
-int on_message_complete(struct http_parser *parser);
-
-int on_chunk_header(struct http_parser *parser);
-
-int on_chunk_complete(struct http_parser *parser);
-
-#endif
diff --git a/samples/net/http_client/src/http_client_rcv.c b/samples/net/http_client/src/http_client_rcv.c
deleted file mode 100644
index 6d5e1af372cd7..0000000000000
--- a/samples/net/http_client/src/http_client_rcv.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include "http_client_rcv.h"
-#include "http_client_types.h"
-#include "config.h"
-
-#include
-
-#ifdef LINEARIZE_BUFFER
-
-NET_BUF_POOL_DEFINE(http_pool, HTTP_POOL_BUF_CTR, HTTP_POOL_BUF_SIZE, 0, NULL);
-
-void http_receive_cb(struct tcp_client_ctx *tcp_ctx, struct net_pkt *rx)
-{
- struct http_client_ctx *http_ctx;
- struct net_buf *data_buf = NULL;
- u16_t data_len;
- u16_t offset;
- int rc;
-
- if (!rx) {
- return;
- }
-
- data_buf = net_buf_alloc(&http_pool, tcp_ctx->timeout);
- if (data_buf == NULL) {
- goto lb_exit;
- }
-
- data_len = min(net_pkt_appdatalen(rx), HTTP_POOL_BUF_SIZE);
- offset = net_pkt_get_len(rx) - data_len;
-
- rc = net_frag_linear_copy(data_buf, rx->frags, offset, data_len);
- if (rc != 0) {
- rc = -ENOMEM;
- goto lb_exit;
- }
-
- http_ctx = CONTAINER_OF(tcp_ctx, struct http_client_ctx, tcp_ctx);
-
- /* The parser's error can be catched outside, reading the
- * http_errno struct member
- */
- http_parser_execute(&http_ctx->parser, &http_ctx->settings,
- data_buf->data, data_buf->len);
-
-lb_exit:
- net_buf_unref(data_buf);
- net_pkt_unref(rx);
-}
-
-#else
-
-void http_receive_cb(struct tcp_client_ctx *tcp_ctx, struct net_pkt *rx)
-{
- struct http_client_ctx *http_ctx;
- struct net_buf *frag = rx->frags;
- u16_t offset;
-
- if (!rx) {
- return;
- }
-
- http_ctx = CONTAINER_OF(tcp_ctx, struct http_client_ctx, tcp_ctx);
-
- offset = net_pkt_get_len(rx) - net_pkt_appdatalen(rx);
-
- /* find the fragment */
- while (frag && offset >= frag->len) {
- offset -= frag->len;
- frag = frag->frags;
- }
-
- while (frag) {
- (void)http_parser_execute(&http_ctx->parser,
- &http_ctx->settings,
- frag->data + offset,
- frag->len - offset);
-
- /* after the first iteration, we set offset to 0 */
- offset = 0;
-
- /* The parser's error can be catched outside, reading the
- * http_errno struct member
- */
- if (http_ctx->parser.http_errno != HPE_OK) {
- goto lb_exit;
- }
-
- frag = frag->frags;
- }
-
-lb_exit:
- net_pkt_unref(rx);
-}
-
-#endif
diff --git a/samples/net/http_client/src/http_client_rcv.h b/samples/net/http_client/src/http_client_rcv.h
deleted file mode 100644
index 5bdca8ddd2893..0000000000000
--- a/samples/net/http_client/src/http_client_rcv.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef _HTTP_CLIENT_RCV_H_
-#define _HTTP_CLIENT_RCV_H_
-
-#include "tcp_client.h"
-
-/* HTTP reception callback */
-void http_receive_cb(struct tcp_client_ctx *tcp_ctx, struct net_pkt *rx);
-
-#endif
diff --git a/samples/net/http_client/src/http_client_types.h b/samples/net/http_client/src/http_client_types.h
deleted file mode 100644
index fe3915c291523..0000000000000
--- a/samples/net/http_client/src/http_client_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef _HTTP_CLIENT_TYPES_H_
-#define _HTTP_CLIENT_TYPES_H_
-
-#include
-#include "tcp_client.h"
-#include "config.h"
-
-struct http_client_ctx {
- struct http_parser parser;
- struct http_parser_settings settings;
-
- struct tcp_client_ctx tcp_ctx;
-
- u32_t content_length;
- u32_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;
-};
-
-#endif
diff --git a/samples/net/http_client/src/main.c b/samples/net/http_client/src/main.c
index 3cda74196312c..b354d1f398c4a 100644
--- a/samples/net/http_client/src/main.c
+++ b/samples/net/http_client/src/main.c
@@ -4,131 +4,324 @@
* SPDX-License-Identifier: Apache-2.0
*/
+#if 1
+#define SYS_LOG_DOMAIN "http-client"
+#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
+#define NET_LOG_ENABLED 1
+#endif
+
#include
#include
-#include
+
+#include
+#include
#include
-#include "http_client.h"
-#include "http_client_types.h"
#include "config.h"
-#define POST_CONTENT_TYPE "application/x-www-form-urlencoded"
-#define POST_PAYLOAD "os=ZephyrRTOS&arch="CONFIG_ARCH
+#define MAX_ITERATIONS 20
+#define WAIT_TIME (APP_REQ_TIMEOUT * 2)
-#define MAX_ITERATIONS 100
+#define RESULT_BUF_SIZE 1024
+static u8_t result[RESULT_BUF_SIZE];
+/*
+ * Note that the http_client_ctx is quite large so be careful if that is
+ * allocated from stack.
+ */
static struct http_client_ctx http_ctx;
-static void send_http_method(enum http_method method, char *url,
- char *content_type, char *payload);
+struct waiter {
+ struct http_client_ctx *ctx;
+ struct k_sem wait;
+ size_t total_len;
+ size_t header_len;
+};
-void main(void)
+void panic(const char *msg)
+{
+ if (msg) {
+ NET_ERR("%s", msg);
+ }
+
+ for (;;) {
+ k_sleep(K_FOREVER);
+ }
+}
+
+static int do_sync_http_req(struct http_client_ctx *ctx,
+ enum http_method method,
+ const char *url,
+ const char *content_type,
+ const char *payload)
{
- int i = MAX_ITERATIONS;
- int rc;
+ struct http_client_request req = {};
+ int ret;
- http_init(&http_ctx);
- http_ctx.tcp_ctx.receive_cb = http_receive_cb;
- http_ctx.tcp_ctx.timeout = HTTP_NETWORK_TIMEOUT;
+ req.method = method;
+ req.url = url;
+ req.protocol = " " HTTP_PROTOCOL HTTP_CRLF;
- rc = tcp_set_local_addr(&http_ctx.tcp_ctx, LOCAL_ADDR);
- if (rc) {
- printk("tcp_set_local_addr error\n");
- goto lb_exit;
+ ret = http_client_send_req(ctx, &req, NULL, result, sizeof(result),
+ NULL, APP_REQ_TIMEOUT);
+ if (ret < 0) {
+ NET_ERR("Cannot send %s request (%d)", http_method_str(method),
+ ret);
+ goto out;
}
- while (i-- > 0) {
- send_http_method(HTTP_GET, "/index.html", NULL, NULL);
- k_sleep(APP_NAP_TIME);
+ if (ctx->rsp.data_len > sizeof(result)) {
+ NET_ERR("Result buffer overflow by %zd bytes",
+ ctx->rsp.data_len - sizeof(result));
- send_http_method(HTTP_HEAD, "/", NULL, NULL);
- k_sleep(APP_NAP_TIME);
+ ret = -E2BIG;
+ } else {
+ NET_INFO("HTTP server response status: %s",
+ ctx->rsp.http_status);
- send_http_method(HTTP_OPTIONS, "/index.html", NULL, NULL);
- k_sleep(APP_NAP_TIME);
+ if (ctx->parser.http_errno) {
+ if (method == HTTP_OPTIONS) {
+ /* Ignore error if OPTIONS is not found */
+ goto out;
+ }
- send_http_method(HTTP_POST, "/post_test.php",
- POST_CONTENT_TYPE, POST_PAYLOAD);
- k_sleep(APP_NAP_TIME);
+ NET_INFO("HTTP parser status: %s",
+ http_errno_description(ctx->parser.http_errno));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (method != HTTP_HEAD) {
+ if (ctx->rsp.body_found) {
+ NET_INFO("HTTP body: %zd bytes, "
+ "expected: %zd bytes",
+ ctx->rsp.processed,
+ ctx->rsp.content_length);
+ } else {
+ NET_ERR("Error detected during HTTP msg "
+ "processing");
+ }
+ }
}
-lb_exit:
- printk("\nBye!\n");
+out:
+ return ret;
}
-void print_banner(enum http_method method)
+void response(struct http_client_ctx *ctx,
+ u8_t *data, size_t buflen,
+ size_t datalen,
+ enum http_final_call data_end,
+ void *user_data)
{
- printk("\n*******************************************\n"
- "HTTP Client: %s\nConnecting to: %s port %d\n"
- "Hostname: %s\nHTTP Request: %s\n",
- LOCAL_ADDR, SERVER_ADDR, SERVER_PORT,
- HOST_NAME, http_method_str(method));
+ struct waiter *waiter = user_data;
+ int ret;
+
+ if (data_end == HTTP_DATA_MORE) {
+ NET_INFO("Received %zd bytes piece of data", datalen);
+
+ /* Do something with the data here. For this example
+ * we just ignore the received data.
+ */
+ waiter->total_len += datalen;
+
+ if (ctx->rsp.body_start) {
+ /* This fragment contains the start of the body
+ * Note that the header length is not proper if
+ * the header is spanning over multiple recv
+ * fragments.
+ */
+ waiter->header_len = ctx->rsp.body_start -
+ ctx->rsp.response_buf;
+ }
+
+ return;
+ }
+
+ waiter->total_len += datalen;
+
+ NET_INFO("HTTP server response status: %s", ctx->rsp.http_status);
+
+ if (ctx->parser.http_errno) {
+ if (ctx->req.method == HTTP_OPTIONS) {
+ /* Ignore error if OPTIONS is not found */
+ goto out;
+ }
+
+ NET_INFO("HTTP parser status: %s",
+ http_errno_description(ctx->parser.http_errno));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ctx->req.method != HTTP_HEAD && ctx->req.method != HTTP_OPTIONS) {
+ if (ctx->rsp.body_found) {
+ NET_INFO("HTTP body: %zd bytes, expected: %zd bytes",
+ ctx->rsp.processed, ctx->rsp.content_length);
+ } else {
+ NET_ERR("Error detected during HTTP msg processing");
+ }
+
+ if (waiter->total_len !=
+ waiter->header_len + ctx->rsp.content_length) {
+ NET_ERR("Error while receiving data, "
+ "received %zd expected %zd bytes",
+ waiter->total_len, waiter->header_len +
+ ctx->rsp.content_length);
+ }
+ }
+
+out:
+ k_sem_give(&waiter->wait);
}
-static void send_http_method(enum http_method method, char *url,
- char *content_type, char *payload)
+static int do_async_http_req(struct http_client_ctx *ctx,
+ enum http_method method,
+ const char *url,
+ const char *content_type,
+ const char *payload)
{
- struct net_context *net_ctx;
- s32_t timeout;
- int rc;
+ struct http_client_request req = {};
+ struct waiter waiter;
+ int ret;
- print_banner(method);
+ req.method = method;
+ req.url = url;
+ req.protocol = " " HTTP_PROTOCOL HTTP_CRLF;
- http_reset_ctx(&http_ctx);
+ k_sem_init(&waiter.wait, 0, 1);
- rc = tcp_connect(&http_ctx.tcp_ctx, SERVER_ADDR, SERVER_PORT);
- if (rc) {
- printk("tcp_connect error\n");
- return;
+ waiter.total_len = 0;
+
+ ret = http_client_send_req(ctx, &req, response, result, sizeof(result),
+ &waiter, APP_REQ_TIMEOUT);
+ if (ret < 0 && ret != -EINPROGRESS) {
+ NET_ERR("Cannot send %s request (%d)", http_method_str(method),
+ ret);
+ goto out;
}
- net_ctx = http_ctx.tcp_ctx.net_ctx;
- timeout = http_ctx.tcp_ctx.timeout;
-
- switch (method) {
- case HTTP_GET:
- rc = http_request_get(net_ctx, timeout, url, HEADER_FIELDS);
- break;
- case HTTP_POST:
- rc = http_request_post(net_ctx, timeout, url, HEADER_FIELDS,
- content_type, payload);
- break;
- case HTTP_HEAD:
- rc = http_request_head(net_ctx, timeout, url, HEADER_FIELDS);
- break;
- case HTTP_OPTIONS:
- rc = http_request_options(net_ctx, timeout, url, HEADER_FIELDS);
- break;
- default:
- printk("Not yet implemented\n");
- goto lb_exit;
+ if (k_sem_take(&waiter.wait, WAIT_TIME)) {
+ NET_ERR("Timeout while waiting HTTP response");
+ http_client_release(ctx);
+ ret = -ETIMEDOUT;
+ goto out;
}
- if (rc) {
- printk("Send error\n");
- goto lb_exit;
+ ret = 0;
+
+out:
+ return ret;
+}
+
+static inline int do_sync_reqs(struct http_client_ctx *ctx, int count)
+{
+ int ret;
+
+ /* These examples use the HTTP client API synchronously so they
+ * do not set the callback parameter.
+ */
+ while (count--) {
+ ret = do_sync_http_req(&http_ctx, HTTP_GET, "/index.html",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = do_sync_http_req(&http_ctx, HTTP_HEAD, "/",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = do_sync_http_req(&http_ctx, HTTP_OPTIONS, "/index.html",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = do_sync_http_req(&http_ctx, HTTP_POST, "/post_test.php",
+ POST_CONTENT_TYPE, POST_PAYLOAD);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* Note that we cannot receive data bigger than RESULT_BUF_SIZE
+ * if we wait the buffer synchronously. If you want to receive
+ * bigger data, then you need to set the callback when sending
+ * the HTTP request using http_client_send_req()
+ */
}
- /* this is async, so we wait until the reception callback
- * processes the server's response (if any)
+out:
+ return ret;
+}
+
+static inline int do_async_reqs(struct http_client_ctx *ctx, int count)
+{
+ int ret;
+
+ /* These examples use the HTTP client API asynchronously so they
+ * do set the callback parameter.
*/
- k_sleep(APP_NAP_TIME);
+ while (count--) {
+ ret = do_async_http_req(&http_ctx, HTTP_GET, "/index.html",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
- printk("\nHTTP server response status: %s\n", http_ctx.http_status);
+ ret = do_async_http_req(&http_ctx, HTTP_HEAD, "/",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
- printk("HTTP parser status: %s\n",
- http_errno_description(http_ctx.parser.http_errno));
+ ret = do_async_http_req(&http_ctx, HTTP_OPTIONS, "/index.html",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
- if (method == HTTP_GET) {
- if (http_ctx.body_found) {
- printk("HTTP body: %u bytes, expected: %u bytes\n",
- http_ctx.processed, http_ctx.content_length);
- } else {
- printk("Error detected during HTTP msg processing\n");
+ ret = do_async_http_req(&http_ctx, HTTP_POST, "/post_test.php",
+ POST_CONTENT_TYPE, POST_PAYLOAD);
+ if (ret < 0) {
+ goto out;
}
+
+ ret = do_async_http_req(&http_ctx, HTTP_GET, "/big-file.html",
+ NULL, NULL);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+void main(void)
+{
+ int ret;
+
+ ret = http_client_init(&http_ctx, SERVER_ADDR, SERVER_PORT);
+ if (ret < 0) {
+ NET_ERR("HTTP init failed (%d)", ret);
+ panic(NULL);
}
-lb_exit:
- tcp_disconnect(&http_ctx.tcp_ctx);
+ ret = do_sync_reqs(&http_ctx, MAX_ITERATIONS);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = do_async_reqs(&http_ctx, MAX_ITERATIONS);
+ if (ret < 0) {
+ goto out;
+ }
+
+out:
+ http_client_release(&http_ctx);
+
+ NET_INFO("Done!");
}
diff --git a/samples/net/http_client/src/tcp_client.c b/samples/net/http_client/src/tcp_client.c
deleted file mode 100644
index 850c9e5665b4e..0000000000000
--- a/samples/net/http_client/src/tcp_client.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include "tcp_client.h"
-#include "config.h"
-
-#include
-#include
-#include
-
-#include
-
-static
-int set_addr(struct sockaddr *sock_addr, const char *addr, u16_t server_port)
-{
- void *ptr = NULL;
- int rc;
-
-#ifdef CONFIG_NET_IPV6
- net_sin6(sock_addr)->sin6_port = htons(server_port);
- sock_addr->family = AF_INET6;
- ptr = &(net_sin6(sock_addr)->sin6_addr);
- rc = net_addr_pton(AF_INET6, addr, ptr);
-#else
- net_sin(sock_addr)->sin_port = htons(server_port);
- sock_addr->family = AF_INET;
- ptr = &(net_sin(sock_addr)->sin_addr);
- rc = net_addr_pton(AF_INET, addr, ptr);
-#endif
- if (rc) {
- printk("Invalid IP address: %s\n", addr);
- }
-
- return rc;
-}
-
-static
-int if_addr_add(struct sockaddr *local_sock)
-{
- void *p = NULL;
-
-#ifdef CONFIG_NET_IPV6
- p = net_if_ipv6_addr_add(net_if_get_default(),
- &net_sin6(local_sock)->sin6_addr,
- NET_ADDR_MANUAL, 0);
-#else
- p = net_if_ipv4_addr_add(net_if_get_default(),
- &net_sin(local_sock)->sin_addr,
- NET_ADDR_MANUAL, 0);
-#endif
- if (p) {
- return 0;
- }
-
- return -EINVAL;
-}
-
-int tcp_set_local_addr(struct tcp_client_ctx *ctx, const char *local_addr)
-{
- int rc;
-
- rc = set_addr(&ctx->local_sock, local_addr, 0);
- if (rc) {
- printk("set_addr (local) error\n");
- goto lb_exit;
- }
-
- rc = if_addr_add(&ctx->local_sock);
- if (rc) {
- printk("if_addr_add error\n");
- }
-
-lb_exit:
- return rc;
-}
-
-static
-void recv_cb(struct net_context *net_ctx, struct net_pkt *rx, int status,
- void *data)
-{
- struct tcp_client_ctx *ctx = (struct tcp_client_ctx *)data;
-
- ARG_UNUSED(net_ctx);
-
- if (status) {
- return;
- }
-
- if (rx == NULL || net_pkt_appdatalen(rx) == 0) {
- goto lb_exit;
- }
-
- /* receive_cb must take ownership of the rx packet */
- if (ctx->receive_cb) {
- ctx->receive_cb(ctx, rx);
- return;
- }
-
-lb_exit:
- net_pkt_unref(rx);
-}
-
-int tcp_connect(struct tcp_client_ctx *ctx, const char *server_addr,
- u16_t server_port)
-{
-#if CONFIG_NET_IPV6
- socklen_t addr_len = sizeof(struct sockaddr_in6);
- sa_family_t family = AF_INET6;
-#else
- socklen_t addr_len = sizeof(struct sockaddr_in);
- sa_family_t family = AF_INET;
-#endif
- struct sockaddr server_sock;
- int rc;
-
- rc = net_context_get(family, SOCK_STREAM, IPPROTO_TCP, &ctx->net_ctx);
- if (rc) {
- printk("net_context_get error\n");
- return rc;
- }
-
- rc = net_context_bind(ctx->net_ctx, &ctx->local_sock, addr_len);
- if (rc) {
- printk("net_context_bind error\n");
- goto lb_exit;
- }
-
- rc = set_addr(&server_sock, server_addr, server_port);
- if (rc) {
- printk("set_addr (server) error\n");
- goto lb_exit;
- }
-
- rc = net_context_connect(ctx->net_ctx, &server_sock, addr_len, NULL,
- ctx->timeout, NULL);
- if (rc) {
- printk("net_context_connect error\n");
- goto lb_exit;
- }
-
- (void)net_context_recv(ctx->net_ctx, recv_cb, K_NO_WAIT, ctx);
-
- return 0;
-
-lb_exit:
- net_context_put(ctx->net_ctx);
-
- return rc;
-}
-
-int tcp_disconnect(struct tcp_client_ctx *ctx)
-{
- if (ctx->net_ctx) {
- net_context_put(ctx->net_ctx);
- ctx->net_ctx = NULL;
- }
-
- return 0;
-}
diff --git a/samples/net/http_client/src/tcp_client.h b/samples/net/http_client/src/tcp_client.h
deleted file mode 100644
index f974d03fe4964..0000000000000
--- a/samples/net/http_client/src/tcp_client.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef _TCP_CLIENT_H_
-#define _TCP_CLIENT_H_
-
-#include
-#include
-
-struct tcp_client_ctx {
- /* IP stack network context */
- struct net_context *net_ctx;
- /* Local sock address */
- struct sockaddr local_sock;
- /* Network timeout */
- s32_t timeout;
- /* User defined call back*/
- void (*receive_cb)(struct tcp_client_ctx *ctx, struct net_pkt *rx);
-};
-
-int tcp_set_local_addr(struct tcp_client_ctx *ctx, const char *local_addr);
-
-int tcp_connect(struct tcp_client_ctx *ctx, const char *server_addr,
- u16_t server_port);
-
-int tcp_disconnect(struct tcp_client_ctx *ctx);
-
-#endif
diff --git a/subsys/net/lib/http/Kconfig b/subsys/net/lib/http/Kconfig
index ba0ebf22cd14e..f5fa39d5b5839 100644
--- a/subsys/net/lib/http/Kconfig
+++ b/subsys/net/lib/http/Kconfig
@@ -4,23 +4,20 @@
#
config HTTP
- bool
- prompt "HTTP support"
+ bool "HTTP support"
default n
help
This option enables the HTTP library
config HTTP_SERVER
- bool
- prompt "HTTP server support"
+ bool "HTTP server support"
default n
select HTTP
help
Enables HTTP server routines
config HTTP_HEADER_FIELD_ITEMS
- int
- prompt "HTTP header field max number of items"
+ int "HTTP header field max number of items"
depends on HTTP_SERVER
default 8
help
@@ -28,16 +25,15 @@ config HTTP_HEADER_FIELD_ITEMS
application will handle
config HTTP_CLIENT
- bool
- prompt "HTTP client support"
+ bool "HTTP client support"
default n
+ select HTTP_PARSER
select HTTP
help
Enables HTTP client routines
config HTTP_PARSER
- bool
- prompt "HTTP Parser support"
+ bool "HTTP Parser support"
default n
select HTTP
help
@@ -46,9 +42,15 @@ config HTTP_PARSER
provided by a libc implementation.
config HTTP_PARSER_STRICT
- bool
- prompt "HTTP strict parsing"
+ bool "HTTP strict parsing"
default n
depends on HTTP_PARSER
help
This option enables the strict parsing option
+
+config NET_DEBUG_HTTP
+ bool "Debug HTTP"
+ default n
+ depends on HTTP && NET_LOG
+ help
+ Enables HTTP output debug messages
diff --git a/subsys/net/lib/http/http_client.c b/subsys/net/lib/http/http_client.c
index 7cb0db1d5d415..0ce4c9923acc7 100644
--- a/subsys/net/lib/http/http_client.c
+++ b/subsys/net/lib/http/http_client.c
@@ -4,151 +4,857 @@
* SPDX-License-Identifier: Apache-2.0
*/
-#include
+#if defined(CONFIG_NET_DEBUG_HTTP)
+#define SYS_LOG_DOMAIN "http/client"
+#define NET_LOG_ENABLED 1
+#endif
-#include
+#include
#include
+#include
+#include
+#include
+
+#include
+
+#define BUF_ALLOC_TIMEOUT K_SECONDS(1)
+
/* HTTP client defines */
-#define HTTP_PROTOCOL "HTTP/1.1"
#define HTTP_EOF "\r\n\r\n"
#define HTTP_CONTENT_TYPE "Content-Type: "
#define HTTP_CONT_LEN_SIZE 64
-int http_request(struct net_context *net_ctx, s32_t timeout,
- struct http_client_request *req)
+struct waiter {
+ struct http_client_ctx *ctx;
+ struct k_sem wait;
+};
+
+int http_request(struct net_context *net_ctx, struct http_client_request *req,
+ s32_t timeout)
{
- struct net_pkt *tx;
- int rc = -ENOMEM;
+ const char *method = http_method_str(req->method);
+ struct net_pkt *pkt;
+ int ret = -ENOMEM;
- tx = net_pkt_get_tx(net_ctx, timeout);
- if (!tx) {
+ pkt = net_pkt_get_tx(net_ctx, timeout);
+ if (!pkt) {
return -ENOMEM;
}
- if (!net_pkt_append_all(tx, strlen(req->method), (u8_t *)req->method,
- timeout)) {
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, strlen(method), (u8_t *)method,
+ timeout)) {
+ goto out;
}
- if (!net_pkt_append_all(tx, strlen(req->url), (u8_t *)req->url,
- timeout)) {
- goto lb_exit;
+ /* Space after method string. */
+ if (!net_pkt_append_all(pkt, 1, (u8_t *)" ", timeout)) {
+ goto out;
}
- if (!net_pkt_append_all(tx, strlen(req->protocol),
- (u8_t *)req->protocol, timeout)) {
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, strlen(req->url), (u8_t *)req->url,
+ timeout)) {
+ goto out;
}
- if (!net_pkt_append_all(tx, strlen(req->header_fields),
- (u8_t *)req->header_fields,
- timeout)) {
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, strlen(req->protocol),
+ (u8_t *)req->protocol, timeout)) {
+ goto out;
+ }
+
+ if (req->host) {
+ if (!net_pkt_append_all(pkt, strlen(req->host),
+ (u8_t *)req->host, timeout)) {
+ goto out;
+ }
+
+ if (!net_pkt_append_all(pkt, strlen(HTTP_CRLF),
+ (u8_t *)HTTP_CRLF, timeout)) {
+ goto out;
+ }
+ }
+
+ if (req->header_fields) {
+ if (!net_pkt_append_all(pkt, strlen(req->header_fields),
+ (u8_t *)req->header_fields,
+ timeout)) {
+ goto out;
+ }
}
if (req->content_type_value) {
- if (!net_pkt_append_all(tx, strlen(HTTP_CONTENT_TYPE),
- (u8_t *)HTTP_CONTENT_TYPE,
- timeout)) {
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, strlen(HTTP_CONTENT_TYPE),
+ (u8_t *)HTTP_CONTENT_TYPE,
+ timeout)) {
+ goto out;
}
- if (!net_pkt_append_all(tx, strlen(req->content_type_value),
- (u8_t *)req->content_type_value,
- timeout)) {
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, strlen(req->content_type_value),
+ (u8_t *)req->content_type_value,
+ timeout)) {
+ goto out;
}
}
if (req->payload && req->payload_size) {
char content_len_str[HTTP_CONT_LEN_SIZE];
- rc = snprintk(content_len_str, HTTP_CONT_LEN_SIZE,
- "\r\nContent-Length: %u\r\n\r\n",
- req->payload_size);
- if (rc <= 0 || rc >= HTTP_CONT_LEN_SIZE) {
- rc = -ENOMEM;
- goto lb_exit;
+ ret = snprintk(content_len_str, HTTP_CONT_LEN_SIZE,
+ HTTP_CRLF "Content-Length: %u"
+ HTTP_CRLF HTTP_CRLF,
+ req->payload_size);
+ if (ret <= 0 || ret >= HTTP_CONT_LEN_SIZE) {
+ ret = -ENOMEM;
+ goto out;
}
- if (!net_pkt_append_all(tx, rc, (u8_t *)content_len_str,
- timeout)) {
- rc = -ENOMEM;
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, ret, (u8_t *)content_len_str,
+ timeout)) {
+ ret = -ENOMEM;
+ goto out;
}
- if (!net_pkt_append_all(tx, req->payload_size,
- (u8_t *)req->payload,
- timeout)) {
- rc = -ENOMEM;
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, req->payload_size,
+ (u8_t *)req->payload,
+ timeout)) {
+ ret = -ENOMEM;
+ goto out;
}
-
} else {
- if (!net_pkt_append_all(tx, strlen(HTTP_EOF),
- (u8_t *)HTTP_EOF,
- timeout)) {
- goto lb_exit;
+ if (!net_pkt_append_all(pkt, strlen(HTTP_EOF),
+ (u8_t *)HTTP_EOF,
+ timeout)) {
+ goto out;
}
}
- return net_context_send(tx, NULL, timeout, NULL, NULL);
+ return net_context_send(pkt, NULL, timeout, NULL, NULL);
+
+out:
+ net_pkt_unref(pkt);
+
+ 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)
+{
+ struct http_client_ctx *ctx;
+ u16_t len;
+
+ 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;
+
+ NET_DBG("HTTP response status %s", ctx->rsp.http_status);
+
+ return 0;
+}
+
+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;
+ 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;
+ }
+
+ 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)
+{
+ struct http_client_ctx *ctx;
+ char str[MAX_NUM_DIGITS];
+
+ ctx = CONTAINER_OF(parser, struct http_client_ctx, parser);
+
+ if (ctx->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->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_client_ctx *ctx = CONTAINER_OF(parser,
+ struct http_client_ctx,
+ parser);
+
+ ctx->rsp.body_found = 1;
+ ctx->rsp.processed += length;
+
+ NET_DBG("Processed %zd length %zd", ctx->rsp.processed, length);
+
+ if (!ctx->rsp.body_start) {
+ ctx->rsp.body_start = (u8_t *)at;
+ }
+
+ if (ctx->rsp.cb) {
+ NET_DBG("Calling callback for partitioned %zd len data",
+ ctx->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);
-lb_exit:
- net_pkt_unref(tx);
+ /* Re-use the result buffer and start to fill it again */
+ ctx->rsp.data_len = 0;
+ }
- return rc;
+ return 0;
}
-int http_request_get(struct net_context *net_ctx, s32_t timeout, char *url,
- char *header_fields)
+static int on_headers_complete(struct http_parser *parser)
{
- struct http_client_request req = {
- .method = "GET ",
- .url = url,
- .protocol = " "HTTP_PROTOCOL"\r\n",
- .header_fields = header_fields };
+ struct http_client_ctx *ctx = CONTAINER_OF(parser,
+ struct http_client_ctx,
+ parser);
+
+ if (parser->status_code >= 500 && parser->status_code < 600) {
+ NET_DBG("Status %d, skipping body", parser->status_code);
- return http_request(net_ctx, timeout, &req);
+ return 1;
+ }
+
+ if ((ctx->req.method == HTTP_HEAD || ctx->req.method == HTTP_OPTIONS)
+ && ctx->rsp.content_length > 0) {
+ NET_DBG("No body expected");
+ return 1;
+ }
+
+ NET_DBG("Headers complete");
+
+ return 0;
}
-int http_request_head(struct net_context *net_ctx, s32_t timeout, char *url,
- char *header_fields)
+static int on_message_begin(struct http_parser *parser)
{
- struct http_client_request req = {
- .method = "HEAD ",
- .url = url,
- .protocol = " "HTTP_PROTOCOL"\r\n",
- .header_fields = header_fields };
+#if defined(CONFIG_NET_DEBUG_HTTP)
+ struct http_client_ctx *ctx = CONTAINER_OF(parser,
+ struct http_client_ctx,
+ parser);
- return http_request(net_ctx, timeout, &req);
+ NET_DBG("-- HTTP %s response (headers) --",
+ http_method_str(ctx->req.method));
+#else
+ ARG_UNUSED(parser);
+#endif
+ return 0;
}
-int http_request_options(struct net_context *net_ctx, s32_t timeout,
- char *url, char *header_fields)
+static int on_message_complete(struct http_parser *parser)
{
- struct http_client_request req = {
- .method = "OPTIONS ",
- .url = url,
- .protocol = " "HTTP_PROTOCOL"\r\n",
- .header_fields = header_fields };
+ struct http_client_ctx *ctx = CONTAINER_OF(parser,
+ struct http_client_ctx,
+ parser);
+
+ NET_DBG("-- HTTP %s response (complete) --",
+ http_method_str(ctx->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);
+ }
- return http_request(net_ctx, timeout, &req);
+ k_sem_give(&ctx->req.wait);
+
+ return 0;
+}
+
+static int on_chunk_header(struct http_parser *parser)
+{
+ ARG_UNUSED(parser);
+
+ return 0;
}
-int http_request_post(struct net_context *net_ctx, s32_t timeout, char *url,
- char *header_fields, char *content_type_value,
- char *payload)
+static int on_chunk_complete(struct http_parser *parser)
{
- struct http_client_request req = {
- .method = "POST ",
- .url = url,
- .protocol = " "HTTP_PROTOCOL"\r\n",
- .header_fields = header_fields,
- .content_type_value = content_type_value,
- .payload = payload };
+ ARG_UNUSED(parser);
+
+ return 0;
+}
+
+static void http_receive_cb(struct http_client_ctx *ctx,
+ struct net_pkt *pkt)
+{
+ size_t start = ctx->rsp.data_len;
+ size_t len = 0;
+ struct net_buf *frag;
+ int header_len;
+
+ if (!pkt) {
+ return;
+ }
+
+ /* Get rid of possible IP headers in the first fragment. */
+ frag = pkt->frags;
+
+ header_len = net_pkt_appdata(pkt) - frag->data;
+
+ NET_DBG("Received %d bytes data", net_pkt_appdatalen(pkt));
+
+ /* After this pull, the frag->data points directly to application data.
+ */
+ net_buf_pull(frag, header_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 the caller has not supplied a callback, then
+ * we cannot really continue if the response 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);
+ goto out;
+ }
+
+ http_parser_execute(&ctx->parser,
+ &ctx->settings,
+ ctx->rsp.response_buf + start,
+ len);
+
+ ctx->rsp.data_len = 0;
+ len = 0;
+ start = 0;
+ }
+
+ memcpy(ctx->rsp.response_buf + ctx->rsp.data_len,
+ frag->data, frag->len);
+
+ ctx->rsp.data_len += frag->len;
+ len += frag->len;
+ frag = frag->frags;
+ }
+
+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);
+
+ net_pkt_unref(pkt);
+}
+
+int client_reset(struct http_client_ctx *ctx)
+{
+ http_parser_init(&ctx->parser, HTTP_RESPONSE);
+
+ 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.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) {
+ return;
+ }
+
+ if (!pkt || net_pkt_appdatalen(pkt) == 0) {
+ goto out;
+ }
+
+ /* receive_cb must take ownership of the received packet */
+ if (ctx->tcp.receive_cb) {
+ ctx->tcp.receive_cb(ctx, pkt);
+ return;
+ }
+
+out:
+ net_pkt_unref(pkt);
+}
+
+static int get_local_addr(struct http_client_ctx *ctx)
+{
+ if (ctx->tcp.local.family == AF_INET6) {
+#if defined(CONFIG_NET_IPV6)
+ struct in6_addr *dst = &net_sin6(&ctx->tcp.remote)->sin6_addr;
+
+ 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.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.remote.family == AF_INET6) {
+ addrlen = sizeof(struct sockaddr_in6);
+ }
+
+ 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.family, SOCK_STREAM,
+ IPPROTO_TCP, &ctx->tcp.ctx);
+ if (ret) {
+ NET_DBG("Get context error (%d)", ret);
+ return ret;
+ }
+
+ 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, recv_cb, K_NO_WAIT, ctx);
+
+out:
+ net_context_put(ctx->tcp.ctx);
+
+ 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.family,
+ &ctx->tcp.local);
+
+ sprint_addr(remote, NET_IPV6_ADDR_LEN, ctx->tcp.remote.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
+}
+
+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 ret;
+
+ if (!response_buf || response_buf_len == 0) {
+ return -EINVAL;
+ }
+
+ client_reset(ctx);
+
+ ret = tcp_connect(ctx);
+ if (ret) {
+ 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;
+ ctx->rsp.response_buf = response_buf;
+ ctx->rsp.response_buf_len = response_buf_len;
+
+ print_info(ctx, ctx->req.method);
+
+ ret = http_request(ctx->tcp.ctx, req, BUF_ALLOC_TIMEOUT);
+ if (ret) {
+ 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)
+{
+ 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.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);
+ if (ret < 0) {
+ NET_ERR("Cannot resolve %s (%d)", server, ret);
+ ctx->dns_id = 0;
+ 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->dns_id = 0;
+
+ if (ctx->tcp.remote.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.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;
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ if (server) {
+ ret = set_remote_addr(ctx, server, server_port);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ctx->tcp.local.family = ctx->tcp.remote.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;
+
+ k_sem_init(&ctx->req.wait, 0, 1);
+
+ return 0;
+}
+
+void http_client_release(struct http_client_ctx *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ net_context_put(ctx->tcp.ctx);
+ 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();
- return http_request(net_ctx, timeout, &req);
+ memset(ctx, 0, sizeof(*ctx));
}
diff --git a/subsys/net/lib/http/http_parser.c b/subsys/net/lib/http/http_parser.c
index 36a33788f9d87..eab6ca065b292 100644
--- a/subsys/net/lib/http/http_parser.c
+++ b/subsys/net/lib/http/http_parser.c
@@ -2533,9 +2533,9 @@ int parser_execute(struct http_parser *parser,
return -HTTP_PARSER_ERRNO(parser);
}
-int http_parser_execute(struct http_parser *parser,
- const struct http_parser_settings *settings,
- const char *data, size_t len)
+size_t http_parser_execute(struct http_parser *parser,
+ const struct http_parser_settings *settings,
+ const char *data, size_t len)
{
size_t parsed;
From 5b7e8fc0cd95430bacef886adf331d14f666db7e Mon Sep 17 00:00:00 2001
From: Jukka Rissanen
Date: Fri, 28 Apr 2017 14:57:48 +0300
Subject: [PATCH 2/2] net: samples: Common application init API
This creates a common API for network sample applications for
setting up IP addresses etc. The HTTP client application is modified
to use this API.
Signed-off-by: Jukka Rissanen
---
samples/net/common/Makefile.common | 21 +++
samples/net/common/net_sample_app.h | 27 ++++
samples/net/common/sample_app_setup.c | 222 ++++++++++++++++++++++++++
samples/net/http_client/src/Makefile | 2 +
samples/net/http_client/src/main.c | 8 +
5 files changed, 280 insertions(+)
create mode 100644 samples/net/common/Makefile.common
create mode 100644 samples/net/common/net_sample_app.h
create mode 100644 samples/net/common/sample_app_setup.c
diff --git a/samples/net/common/Makefile.common b/samples/net/common/Makefile.common
new file mode 100644
index 0000000000000..6d50045b16fe8
--- /dev/null
+++ b/samples/net/common/Makefile.common
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 2017 Intel Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Common routines used in net samples
+
+ifeq ($(CONFIG_NET_L2_BLUETOOTH), y)
+CFLAGS +=-I${ZEPHYR_BASE}/samples/bluetooth/
+obj-y += ../bluetooth/gatt/ipss.o
+endif
+
+ifeq ($(CONFIG_NET_L2_IEEE802154), y)
+ifeq ($(CONFIG_NET_APP_SETTINGS), y)
+obj-y += ../../common/ieee802154_settings.o
+endif
+endif
+
+ccflags-y += -I${ZEPHYR_BASE}/samples/net/common/
+obj-y += ../../common/sample_app_setup.o
diff --git a/samples/net/common/net_sample_app.h b/samples/net/common/net_sample_app.h
new file mode 100644
index 0000000000000..0be387720211b
--- /dev/null
+++ b/samples/net/common/net_sample_app.h
@@ -0,0 +1,27 @@
+/** @file
+ * @brief Common routines needed in various network sample applications.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __NET_SAMPLE_APP_H
+#define __NET_SAMPLE_APP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* What kind of functionality is needed by the application. */
+#define NET_SAMPLE_NEED_ROUTER 0x00000001
+
+int net_sample_app_init(const char *app_info, u32_t flags, s32_t timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NET_SAMPLE_APP_H */
diff --git a/samples/net/common/sample_app_setup.c b/samples/net/common/sample_app_setup.c
new file mode 100644
index 0000000000000..53fb124ba85ff
--- /dev/null
+++ b/samples/net/common/sample_app_setup.c
@@ -0,0 +1,222 @@
+/* sample_app_setup.c */
+
+/*
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#if 1
+#define SYS_LOG_DOMAIN "sample/net"
+#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
+#define NET_LOG_ENABLED 1
+#endif
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "net_sample_app.h"
+
+#if defined(CONFIG_NET_L2_BLUETOOTH)
+#include
+#include
+#endif
+
+#if defined(CONFIG_NET_L2_IEEE802154)
+#include
+#endif
+
+struct k_sem waiter = K_SEM_INITIALIZER(waiter, 0, 1);
+
+#if defined(CONFIG_NET_DHCPV4)
+static struct net_mgmt_event_callback mgmt4_cb;
+
+static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb,
+ u32_t mgmt_event,
+ struct net_if *iface)
+{
+ char hr_addr[NET_IPV4_ADDR_LEN];
+ int i;
+
+ if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) {
+ return;
+ }
+
+ for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
+ struct net_if_addr *if_addr = &iface->ipv4.unicast[i];
+
+ if (if_addr->addr_type != NET_ADDR_DHCP || !if_addr->is_used) {
+ continue;
+ }
+
+ NET_INFO("IPv4 address: %s",
+ net_addr_ntop(AF_INET, &if_addr->address.in_addr,
+ hr_addr, NET_IPV4_ADDR_LEN));
+ NET_INFO("Lease time: %u seconds", iface->dhcpv4.lease_time);
+ NET_INFO("Subnet: %s",
+ net_addr_ntop(AF_INET, &iface->ipv4.netmask,
+ hr_addr, NET_IPV4_ADDR_LEN));
+ NET_INFO("Router: %s",
+ net_addr_ntop(AF_INET, &iface->ipv4.gw,
+ hr_addr, NET_IPV4_ADDR_LEN));
+ break;
+ }
+
+ k_sem_give(&waiter);
+}
+
+static void setup_dhcpv4(struct net_if *iface)
+{
+ NET_INFO("Running dhcpv4 client...");
+
+ net_mgmt_init_event_callback(&mgmt4_cb, ipv4_addr_add_handler,
+ NET_EVENT_IPV4_ADDR_ADD);
+ net_mgmt_add_event_callback(&mgmt4_cb);
+
+ net_dhcpv4_start(iface);
+}
+
+#else
+#define setup_dhcpv4(...)
+#endif /* CONFIG_NET_DHCPV4 */
+
+#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_DHCPV4)
+
+#if !defined(CONFIG_NET_APP_MY_IPV4_ADDR)
+#error "You need to define an IPv4 address or enable DHCPv4!"
+#endif
+
+static void setup_ipv4(struct net_if *iface)
+{
+ struct in_addr addr;
+ int ret;
+
+ if (net_addr_pton(AF_INET, CONFIG_NET_APP_MY_IPV4_ADDR, &addr)) {
+ NET_ERR("Invalid address: %s", CONFIG_NET_APP_MY_IPV4_ADDR);
+ return;
+ }
+
+ net_if_ipv4_addr_add(iface, &addr, NET_ADDR_MANUAL, 0);
+
+ NET_INFO("IPv4 address: %s",
+ net_addr_ntop(AF_INET, &addr, hr_addr, NET_IPV4_ADDR_LEN));
+
+ k_sem_give(&waiter);
+}
+
+#else
+#define setup_ipv4(...)
+#endif /* CONFIG_NET_IPV4 && !CONFIG_NET_DHCPV4 */
+
+#if defined(CONFIG_NET_IPV6)
+#if !defined(CONFIG_NET_APP_MY_IPV6_ADDR)
+#error "You need to define an IPv6 address!"
+#endif
+
+static struct net_mgmt_event_callback mgmt6_cb;
+static struct in6_addr laddr;
+
+/* DNS query will most probably fail if we do not have a default
+ * router configured because typically the DNS server is outside of local
+ * network. So wait for that before continuing.
+ */
+static void ipv6_event_handler(struct net_mgmt_event_callback *cb,
+ u32_t mgmt_event, struct net_if *iface)
+{
+ if (mgmt_event == NET_EVENT_IPV6_DAD_SUCCEED) {
+ char hr_addr[NET_IPV6_ADDR_LEN];
+ struct net_if_addr *ifaddr;
+
+ ifaddr = net_if_ipv6_addr_lookup(&laddr, &iface);
+ if (!ifaddr ||
+ !(net_ipv6_addr_cmp(&ifaddr->address.in6_addr, &laddr) &&
+ ifaddr->addr_state == NET_ADDR_PREFERRED)) {
+ /* Address is not yet properly setup */
+ return;
+ }
+
+ NET_INFO("IPv6 address: %s",
+ net_addr_ntop(AF_INET6, &laddr, hr_addr,
+ NET_IPV6_ADDR_LEN));
+
+ k_sem_give(&waiter);
+ }
+
+ if (mgmt_event == NET_EVENT_IPV6_ROUTER_ADD) {
+ k_sem_give(&waiter);
+ }
+}
+
+static void setup_ipv6(struct net_if *iface, u32_t flags)
+{
+ struct net_if_addr *ifaddr;
+ u32_t mask = NET_EVENT_IPV6_DAD_SUCCEED;
+
+ if (net_addr_pton(AF_INET6, CONFIG_NET_APP_MY_IPV6_ADDR, &laddr)) {
+ NET_ERR("Invalid address: %s", CONFIG_NET_APP_MY_IPV6_ADDR);
+ return;
+ }
+
+ if (flags & NET_SAMPLE_NEED_ROUTER) {
+ mask |= NET_EVENT_IPV6_ROUTER_ADD;
+ }
+
+ net_mgmt_init_event_callback(&mgmt6_cb, ipv6_event_handler, mask);
+ net_mgmt_add_event_callback(&mgmt6_cb);
+
+ ifaddr = net_if_ipv6_addr_add(iface, &laddr, NET_ADDR_MANUAL, 0);
+ if (!ifaddr) {
+ NET_ERR("Cannot add %s to interface",
+ CONFIG_NET_APP_MY_IPV6_ADDR);
+ return;
+ }
+}
+
+#else
+#define setup_ipv6(...)
+#endif /* CONFIG_NET_IPV6 */
+
+int net_sample_app_init(const char *app_info, u32_t flags, s32_t timeout)
+{
+ struct net_if *iface = net_if_get_default();
+
+ if (app_info) {
+ NET_INFO("%s", app_info);
+ }
+
+#if defined(CONFIG_NET_L2_BLUETOOTH)
+ if (bt_enable(NULL)) {
+ NET_ERR("Bluetooth init failed");
+ return -EFAULT;
+ } else {
+ ipss_init();
+ ipss_advertise();
+ }
+#endif
+
+#if defined(CONFIG_NET_L2_IEEE802154)
+ if (ieee802154_sample_setup()) {
+ NET_ERR("IEEE 802.15.4 setup failed");
+ return -EFAULT;
+ }
+#endif
+
+ setup_ipv4(iface);
+
+ setup_dhcpv4(iface);
+
+ setup_ipv6(iface, flags);
+
+ /* Wait until we are ready to continue */
+ if (k_sem_take(&waiter, timeout)) {
+ NET_ERR("Timeout while waiting setup");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
diff --git a/samples/net/http_client/src/Makefile b/samples/net/http_client/src/Makefile
index 587479094c8dd..6bede6e4ed980 100644
--- a/samples/net/http_client/src/Makefile
+++ b/samples/net/http_client/src/Makefile
@@ -4,4 +4,6 @@
# SPDX-License-Identifier: Apache-2.0
#
+include $(ZEPHYR_BASE)/samples/net/common/Makefile.common
+
obj-y += main.o
diff --git a/samples/net/http_client/src/main.c b/samples/net/http_client/src/main.c
index b354d1f398c4a..5fe308cc3251d 100644
--- a/samples/net/http_client/src/main.c
+++ b/samples/net/http_client/src/main.c
@@ -17,6 +17,9 @@
#include
#include
+
+#include
+
#include "config.h"
#define MAX_ITERATIONS 20
@@ -304,6 +307,11 @@ void main(void)
{
int ret;
+ ret = net_sample_app_init("Run HTTP client", 0, APP_STARTUP_TIME);
+ if (ret < 0) {
+ panic("Application init failed");
+ }
+
ret = http_client_init(&http_ctx, SERVER_ADDR, SERVER_PORT);
if (ret < 0) {
NET_ERR("HTTP init failed (%d)", ret);