net: http: Remove HTTP client and server APIs
The old legacy APIs use net-app library and as that is being removed, then the dependencies need to be removed also. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
1cba0161ed
commit
4043909d69
31 changed files with 0 additions and 5166 deletions
|
@ -210,12 +210,6 @@ DNS Resolve
|
||||||
.. doxygengroup:: dns_resolve
|
.. doxygengroup:: dns_resolve
|
||||||
:project: Zephyr
|
:project: Zephyr
|
||||||
|
|
||||||
HTTP
|
|
||||||
====
|
|
||||||
|
|
||||||
.. doxygengroup:: http
|
|
||||||
:project: Zephyr
|
|
||||||
|
|
||||||
TLS credentials
|
TLS credentials
|
||||||
***************
|
***************
|
||||||
|
|
||||||
|
|
|
@ -55,14 +55,6 @@ can be disabled if not needed.
|
||||||
configuration options for sockets API. Secure functions for the implementation
|
configuration options for sockets API. Secure functions for the implementation
|
||||||
are provided by mbedTLS library.
|
are provided by mbedTLS library.
|
||||||
|
|
||||||
* **HTTP** Hypertext Transfer Protocol (RFC 2116) is supported. A simple
|
|
||||||
library is provided that applications can use. Sample applications are
|
|
||||||
implemented for :ref:`http-client-sample` and :ref:`http-server-sample`.
|
|
||||||
Both :ref:`http-client-sample` and :ref:`http-server-sample` can use
|
|
||||||
TLS (Transport Layer Security) v1.2 (RFC 5246) or SSL (Secure Sockets
|
|
||||||
Layer) v3.0 (RFC 6101) functionality to encrypt the network traffic.
|
|
||||||
The secured connections are provided by mbed library.
|
|
||||||
|
|
||||||
* **MQTT** Message Queue Telemetry Transport (ISO/IEC PRF 20922) is supported.
|
* **MQTT** Message Queue Telemetry Transport (ISO/IEC PRF 20922) is supported.
|
||||||
A sample :ref:`mqtt-publisher-sample` client application for MQTT v3.1.1 is
|
A sample :ref:`mqtt-publisher-sample` client application for MQTT v3.1.1 is
|
||||||
implemented.
|
implemented.
|
||||||
|
|
1125
include/net/http.h
1125
include/net/http.h
File diff suppressed because it is too large
Load diff
|
@ -1,14 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.13.1)
|
|
||||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
|
||||||
project(http_client)
|
|
||||||
|
|
||||||
FILE(GLOB app_sources src/*.c)
|
|
||||||
target_sources(app PRIVATE ${app_sources})
|
|
||||||
|
|
||||||
include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake)
|
|
||||||
|
|
||||||
generate_inc_file_for_target(
|
|
||||||
app
|
|
||||||
src/echo-apps-cert.der
|
|
||||||
${ZEPHYR_BINARY_DIR}/include/generated/echo-apps-cert.der.inc
|
|
||||||
)
|
|
|
@ -1,148 +0,0 @@
|
||||||
.. _http-client-sample:
|
|
||||||
|
|
||||||
HTTP Client
|
|
||||||
###########
|
|
||||||
|
|
||||||
Overview
|
|
||||||
********
|
|
||||||
|
|
||||||
This sample application shows how to create HTTP 1.1 requests to
|
|
||||||
an HTTP server and how to parse the incoming responses.
|
|
||||||
Supported HTTP 1.1 methods are: GET, HEAD and POST.
|
|
||||||
|
|
||||||
The source code for this sample application can be found at:
|
|
||||||
:file:`samples/net/http_client`.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
************
|
|
||||||
|
|
||||||
- :ref:`networking_with_qemu`
|
|
||||||
- Terminal emulator software
|
|
||||||
- HTTP Server
|
|
||||||
- DNS server (optional)
|
|
||||||
|
|
||||||
|
|
||||||
Building and Running
|
|
||||||
********************
|
|
||||||
|
|
||||||
Open the project configuration file for your platform, for example:
|
|
||||||
:file:`prj_qemu_x86.conf` is the configuration file for QEMU.
|
|
||||||
|
|
||||||
To use QEMU for testing, follow the :ref:`networking_with_qemu` guide.
|
|
||||||
|
|
||||||
For IPv4 networks, set the following variables:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
CONFIG_NET_IPV4=y
|
|
||||||
CONFIG_NET_IPV6=n
|
|
||||||
|
|
||||||
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, both static IP addresses and DHCPv4 are supported.
|
|
||||||
Static IP addresses are specified in the project configuration file,
|
|
||||||
for example:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
|
||||||
|
|
||||||
are the IPv6 addresses for the HTTP client running Zephyr and the
|
|
||||||
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:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_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:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
#define SERVER_PORT 8000
|
|
||||||
|
|
||||||
assumes that the HTTP server is listening at the TCP port 8000.
|
|
||||||
|
|
||||||
HTTP Server
|
|
||||||
===========
|
|
||||||
|
|
||||||
Sample code for a very simple HTTP server can be downloaded from the
|
|
||||||
zephyrproject-rtos/net-tools project area:
|
|
||||||
https://github.com/zephyrproject-rtos/net-tools
|
|
||||||
|
|
||||||
Open a terminal window and type:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ cd net-tools
|
|
||||||
$ ./http-server.sh
|
|
||||||
|
|
||||||
|
|
||||||
DNS setup
|
|
||||||
=========
|
|
||||||
|
|
||||||
The net-tools project provides a simple DNS resolver. You can activate
|
|
||||||
it like this if you want to test the DNS resolving with HTTP client.
|
|
||||||
|
|
||||||
Open a terminal window and type:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ cd net-tools
|
|
||||||
$ ./dnsmasq.sh
|
|
||||||
|
|
||||||
|
|
||||||
Sample Output
|
|
||||||
=============
|
|
||||||
|
|
||||||
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 "/"
|
|
||||||
- POST "/post_test.php"
|
|
||||||
- GET "/big-file.html"
|
|
||||||
|
|
||||||
The terminal window where QEMU is running will show something similar
|
|
||||||
to the following:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
[http-client] [INF] do_sync_http_req: [19] Send /index.html
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP body: 170 bytes, expected: 170 bytes
|
|
||||||
[http-client] [INF] do_sync_http_req: [19] Send /
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_sync_http_req: [19] Send /post_test.php
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP body: 24 bytes, expected: 24 bytes
|
|
||||||
[http-client] [INF] do_sync_http_req: [20] Send /index.html
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP body: 170 bytes, expected: 170 bytes
|
|
||||||
[http-client] [INF] do_sync_http_req: [20] Send /
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_sync_http_req: [20] Send /post_test.php
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_sync_http_req: HTTP body: 24 bytes, expected: 24 bytes
|
|
||||||
[http-client] [INF] main: --------Sending 20 async request--------
|
|
||||||
[http-client] [INF] do_async_http_req: [1] Send /index.html
|
|
||||||
[http-client] [INF] response: Received 356 bytes piece of data
|
|
||||||
[http-client] [INF] response: HTTP server response status: OK
|
|
||||||
[http-client] [INF] response: HTTP body: 170 bytes, expected: 170 bytes
|
|
||||||
[http-client] [INF] do_async_http_req: [1] Send /
|
|
||||||
[http-client] [INF] response: HTTP server response status: OK
|
|
||||||
[http-client] [INF] do_async_http_req: [1] Send /post_test.php
|
|
||||||
[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] do_async_http_req: [1] Send /big-file.html
|
|
||||||
[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 418 bytes piece of data
|
|
||||||
[http-client] [INF] response: HTTP server response status: OK
|
|
||||||
[http-client] [INF] response: HTTP body: 1528 bytes, expected: 1528 bytes
|
|
|
@ -1,44 +0,0 @@
|
||||||
CONFIG_NETWORKING=y
|
|
||||||
CONFIG_NET_TCP=y
|
|
||||||
CONFIG_ENTROPY_GENERATOR=y
|
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
|
||||||
CONFIG_NET_LOG=y
|
|
||||||
CONFIG_LOG=y
|
|
||||||
CONFIG_INIT_STACKS=y
|
|
||||||
|
|
||||||
CONFIG_NET_PKT_RX_COUNT=32
|
|
||||||
CONFIG_NET_PKT_TX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_RX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_TX_COUNT=32
|
|
||||||
|
|
||||||
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
|
|
||||||
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
|
|
||||||
|
|
||||||
CONFIG_NET_MAX_CONTEXTS=16
|
|
||||||
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_CLIENT=y
|
|
||||||
|
|
||||||
CONFIG_NET_IPV6=y
|
|
||||||
CONFIG_NET_IPV4=y
|
|
||||||
#CONFIG_NET_DHCPV4=y
|
|
||||||
|
|
||||||
CONFIG_HTTPS=n
|
|
||||||
CONFIG_MBEDTLS=n
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=y
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
|
||||||
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
|
||||||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV6=y
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV4=y
|
|
||||||
|
|
||||||
CONFIG_NET_SHELL=y
|
|
||||||
CONFIG_NET_STATISTICS=y
|
|
||||||
|
|
||||||
CONFIG_NET_MGMT=y
|
|
||||||
CONFIG_NET_MGMT_EVENT=y
|
|
||||||
|
|
||||||
CONFIG_MAIN_STACK_SIZE=2048
|
|
|
@ -1,51 +0,0 @@
|
||||||
CONFIG_NETWORKING=y
|
|
||||||
CONFIG_NET_TCP=y
|
|
||||||
CONFIG_ENTROPY_GENERATOR=y
|
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
|
||||||
CONFIG_NET_LOG=y
|
|
||||||
CONFIG_LOG=y
|
|
||||||
CONFIG_INIT_STACKS=y
|
|
||||||
|
|
||||||
CONFIG_NET_PKT_RX_COUNT=32
|
|
||||||
CONFIG_NET_PKT_TX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_RX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_TX_COUNT=32
|
|
||||||
|
|
||||||
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
|
|
||||||
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
|
|
||||||
|
|
||||||
CONFIG_NET_MAX_CONTEXTS=16
|
|
||||||
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_CLIENT=y
|
|
||||||
|
|
||||||
CONFIG_NET_IPV6=y
|
|
||||||
|
|
||||||
CONFIG_HTTPS=n
|
|
||||||
CONFIG_MBEDTLS=y
|
|
||||||
CONFIG_MBEDTLS_BUILTIN=y
|
|
||||||
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
|
|
||||||
CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2500
|
|
||||||
CONFIG_MBEDTLS_ENABLE_HEAP=y
|
|
||||||
CONFIG_MBEDTLS_HEAP_SIZE=12000
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=y
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV6=y
|
|
||||||
|
|
||||||
CONFIG_NET_SHELL=y
|
|
||||||
CONFIG_NET_STATISTICS=y
|
|
||||||
|
|
||||||
CONFIG_NET_MGMT=y
|
|
||||||
CONFIG_NET_MGMT_EVENT=y
|
|
||||||
|
|
||||||
CONFIG_BT=y
|
|
||||||
CONFIG_BT_DEBUG_LOG=y
|
|
||||||
CONFIG_BT_SMP=y
|
|
||||||
CONFIG_BT_PERIPHERAL=y
|
|
||||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
|
||||||
CONFIG_BT_DEVICE_NAME="Test Http_Client"
|
|
||||||
CONFIG_NET_L2_BT=y
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
CONFIG_NETWORKING=y
|
|
||||||
CONFIG_NET_TCP=y
|
|
||||||
CONFIG_ENTROPY_GENERATOR=y
|
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
|
||||||
CONFIG_NET_LOG=y
|
|
||||||
CONFIG_LOG=y
|
|
||||||
CONFIG_INIT_STACKS=y
|
|
||||||
|
|
||||||
CONFIG_NET_PKT_RX_COUNT=32
|
|
||||||
CONFIG_NET_PKT_TX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_RX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_TX_COUNT=32
|
|
||||||
|
|
||||||
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
|
|
||||||
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
|
|
||||||
|
|
||||||
CONFIG_NET_MAX_CONTEXTS=16
|
|
||||||
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_CLIENT=y
|
|
||||||
|
|
||||||
CONFIG_NET_IPV6=y
|
|
||||||
CONFIG_NET_IPV4=y
|
|
||||||
#CONFIG_NET_DHCPV4=y
|
|
||||||
|
|
||||||
CONFIG_HTTPS=y
|
|
||||||
CONFIG_MBEDTLS=y
|
|
||||||
CONFIG_MBEDTLS_BUILTIN=y
|
|
||||||
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
|
|
||||||
CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2500
|
|
||||||
CONFIG_MBEDTLS_ENABLE_HEAP=y
|
|
||||||
CONFIG_MBEDTLS_HEAP_SIZE=30000
|
|
||||||
|
|
||||||
CONFIG_NET_APP_TLS=y
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=y
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
|
||||||
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
|
||||||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV6=y
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV4=y
|
|
||||||
|
|
||||||
CONFIG_NET_SHELL=y
|
|
||||||
CONFIG_NET_STATISTICS=y
|
|
||||||
|
|
||||||
CONFIG_NET_MGMT=y
|
|
||||||
CONFIG_NET_MGMT_EVENT=y
|
|
||||||
|
|
||||||
CONFIG_MAIN_STACK_SIZE=1504
|
|
|
@ -1,15 +0,0 @@
|
||||||
common:
|
|
||||||
harness: net
|
|
||||||
tags: net http
|
|
||||||
sample:
|
|
||||||
name: HTTP Client
|
|
||||||
tests:
|
|
||||||
test:
|
|
||||||
depends_on: netif
|
|
||||||
min_flash: 140
|
|
||||||
test_bt:
|
|
||||||
extra_args: CONF_FILE="prj_bt.conf"
|
|
||||||
platform_whitelist: qemu_x86 qemu_cortex_m3
|
|
||||||
test_tls:
|
|
||||||
extra_args: CONF_FILE="prj_tls.conf"
|
|
||||||
platform_whitelist: qemu_x86 qemu_cortex_m3
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 Intel Corporation
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define APP_REQ_TIMEOUT K_SECONDS(10)
|
|
||||||
|
|
||||||
/* 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 POST_CONTENT_TYPE "application/x-www-form-urlencoded"
|
|
||||||
#define POST_PAYLOAD "os=ZephyrRTOS&arch=" CONFIG_ARCH
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
#define SERVER_PORT 4443
|
|
||||||
#else
|
|
||||||
#define SERVER_PORT 8000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_NET_IPV6)
|
|
||||||
#define SERVER_ADDR CONFIG_NET_CONFIG_PEER_IPV6_ADDR
|
|
||||||
#else
|
|
||||||
#define SERVER_ADDR CONFIG_NET_CONFIG_PEER_IPV4_ADDR
|
|
||||||
#endif
|
|
Binary file not shown.
|
@ -1,495 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 Intel Corporation
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <logging/log.h>
|
|
||||||
LOG_MODULE_REGISTER(net_http_client_sample, LOG_LEVEL_DBG);
|
|
||||||
|
|
||||||
#include <zephyr.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <net/net_core.h>
|
|
||||||
#include <net/net_ip.h>
|
|
||||||
|
|
||||||
#include <net/http.h>
|
|
||||||
|
|
||||||
#include <net/net_app.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define MAX_ITERATIONS 20
|
|
||||||
#define WAIT_TIME (APP_REQ_TIMEOUT * 2)
|
|
||||||
|
|
||||||
/* The OPTIONS method is problematic as the HTTP server might not support
|
|
||||||
* it so turn it off by default.
|
|
||||||
*/
|
|
||||||
#define SEND_OPTIONS 0
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that the http_ctx is quite large so be careful if that is
|
|
||||||
* allocated from stack.
|
|
||||||
*/
|
|
||||||
static struct http_ctx http_ctx;
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
#define HOSTNAME "localhost" /* for cert verification if that is enabled */
|
|
||||||
|
|
||||||
/* The result buf size is set to large enough so that we can receive max size
|
|
||||||
* buf back. Note that mbedtls needs also be configured to have equal size
|
|
||||||
* value for its buffer size. See MBEDTLS_SSL_MAX_CONTENT_LEN option in TLS
|
|
||||||
* config file.
|
|
||||||
*/
|
|
||||||
#define RESULT_BUF_SIZE MBEDTLS_SSL_MAX_CONTENT_LEN
|
|
||||||
static u8_t https_result_buf[RESULT_BUF_SIZE];
|
|
||||||
|
|
||||||
#define INSTANCE_INFO "Zephyr HTTPS client #1"
|
|
||||||
|
|
||||||
#define HTTPS_STACK_SIZE (8 * 1024)
|
|
||||||
|
|
||||||
NET_STACK_DEFINE(HTTPS, https_stack, HTTPS_STACK_SIZE, HTTPS_STACK_SIZE);
|
|
||||||
|
|
||||||
NET_APP_TLS_POOL_DEFINE(ssl_pool, 10);
|
|
||||||
#endif /* CONFIG_HTTPS */
|
|
||||||
|
|
||||||
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
|
|
||||||
NET_PKT_TX_SLAB_DEFINE(http_cli_tx, 15);
|
|
||||||
NET_PKT_DATA_POOL_DEFINE(http_cli_data, 30);
|
|
||||||
|
|
||||||
static struct k_mem_slab *tx_slab(void)
|
|
||||||
{
|
|
||||||
return &http_cli_tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct net_buf_pool *data_pool(void)
|
|
||||||
{
|
|
||||||
return &http_cli_data;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#if defined(CONFIG_NET_L2_BT)
|
|
||||||
#error "TCP connections over Bluetooth need CONFIG_NET_CONTEXT_NET_PKT_POOL "\
|
|
||||||
"defined."
|
|
||||||
#endif /* CONFIG_NET_L2_BT */
|
|
||||||
|
|
||||||
#define tx_slab NULL
|
|
||||||
#define data_pool NULL
|
|
||||||
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
|
|
||||||
|
|
||||||
#if !defined(RESULT_BUF_SIZE)
|
|
||||||
#define RESULT_BUF_SIZE 1600
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This will contain the returned HTTP response to a sent request */
|
|
||||||
static u8_t result[RESULT_BUF_SIZE];
|
|
||||||
|
|
||||||
struct waiter {
|
|
||||||
struct http_ctx *ctx;
|
|
||||||
struct k_sem wait;
|
|
||||||
size_t total_len;
|
|
||||||
size_t header_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
/* Load the certificates etc. In this sample app, we verify that
|
|
||||||
* the server is the test server we are communicating against to.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const char echo_apps_cert_der[] = {
|
|
||||||
#include "echo-apps-cert.der.inc"
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
|
|
||||||
static const unsigned char client_psk[] = {
|
|
||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
||||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char client_psk_id[] = "Client_identity";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int setup_cert(struct net_app_ctx *ctx, void *cert)
|
|
||||||
{
|
|
||||||
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
|
|
||||||
mbedtls_ssl_conf_psk(&ctx->tls.mbedtls.conf,
|
|
||||||
client_psk, sizeof(client_psk),
|
|
||||||
(const unsigned char *)client_psk_id,
|
|
||||||
sizeof(client_psk_id) - 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
||||||
{
|
|
||||||
mbedtls_x509_crt *ca_cert = cert;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mbedtls_x509_crt_parse_der(ca_cert,
|
|
||||||
echo_apps_cert_der,
|
|
||||||
sizeof(echo_apps_cert_der));
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("mbedtls_x509_crt_parse_der failed "
|
|
||||||
"(-0x%x)", -ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
mbedtls_ssl_conf_ca_chain(&ctx->tls.mbedtls.conf,
|
|
||||||
ca_cert, NULL);
|
|
||||||
mbedtls_ssl_conf_authmode(&ctx->tls.mbedtls.conf,
|
|
||||||
MBEDTLS_SSL_VERIFY_REQUIRED);
|
|
||||||
|
|
||||||
mbedtls_ssl_conf_cert_profile(&ctx->tls.mbedtls.conf,
|
|
||||||
&mbedtls_x509_crt_profile_default);
|
|
||||||
}
|
|
||||||
#endif /* MBEDTLS_X509_CRT_PARSE_C */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_HTTPS */
|
|
||||||
|
|
||||||
static int do_sync_http_req(struct http_ctx *ctx,
|
|
||||||
enum http_method method,
|
|
||||||
const char *url,
|
|
||||||
const char *content_type,
|
|
||||||
const char *payload,
|
|
||||||
int count)
|
|
||||||
{
|
|
||||||
struct http_request req = {};
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
req.method = method;
|
|
||||||
req.url = url;
|
|
||||||
req.protocol = " " HTTP_PROTOCOL;
|
|
||||||
|
|
||||||
LOG_INF("[%d] Send %s", count, url);
|
|
||||||
|
|
||||||
ret = http_client_send_req(ctx, &req, NULL, result, sizeof(result),
|
|
||||||
NULL, APP_REQ_TIMEOUT);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot send %s request (%d)", http_method_str(method),
|
|
||||||
ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->http.rsp.data_len > sizeof(result)) {
|
|
||||||
LOG_ERR("Result buffer overflow by %zd bytes",
|
|
||||||
ctx->http.rsp.data_len - sizeof(result));
|
|
||||||
|
|
||||||
ret = -E2BIG;
|
|
||||||
} else {
|
|
||||||
LOG_INF("HTTP server response status: %s",
|
|
||||||
ctx->http.rsp.http_status);
|
|
||||||
|
|
||||||
if (ctx->http.parser.http_errno) {
|
|
||||||
if (method == HTTP_OPTIONS) {
|
|
||||||
/* Ignore error if OPTIONS is not found */
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INF("HTTP parser status: %s",
|
|
||||||
http_errno_description(
|
|
||||||
ctx->http.parser.http_errno));
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method != HTTP_HEAD) {
|
|
||||||
if (ctx->http.rsp.body_found) {
|
|
||||||
LOG_INF("HTTP body: %zd bytes, "
|
|
||||||
"expected: %zd bytes",
|
|
||||||
ctx->http.rsp.processed,
|
|
||||||
ctx->http.rsp.content_length);
|
|
||||||
} else {
|
|
||||||
LOG_ERR("Error detected during HTTP msg "
|
|
||||||
"processing");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
http_close(ctx);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void response(struct http_ctx *ctx,
|
|
||||||
u8_t *data, size_t buflen,
|
|
||||||
size_t datalen,
|
|
||||||
enum http_final_call data_end,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
struct waiter *waiter = user_data;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (data_end == HTTP_DATA_MORE) {
|
|
||||||
LOG_INF("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->http.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->http.rsp.body_start -
|
|
||||||
ctx->http.rsp.response_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
waiter->total_len += datalen;
|
|
||||||
|
|
||||||
LOG_INF("HTTP server response status: %s", ctx->http.rsp.http_status);
|
|
||||||
|
|
||||||
if (ctx->http.parser.http_errno) {
|
|
||||||
if (ctx->http.req.method == HTTP_OPTIONS) {
|
|
||||||
/* Ignore error if OPTIONS is not found */
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INF("HTTP parser status: %s",
|
|
||||||
http_errno_description(ctx->http.parser.http_errno));
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->http.req.method != HTTP_HEAD &&
|
|
||||||
ctx->http.req.method != HTTP_OPTIONS) {
|
|
||||||
if (ctx->http.rsp.body_found) {
|
|
||||||
LOG_INF("HTTP body: %zd bytes, expected: %zd bytes",
|
|
||||||
ctx->http.rsp.processed,
|
|
||||||
ctx->http.rsp.content_length);
|
|
||||||
} else {
|
|
||||||
LOG_ERR("Error detected during HTTP msg processing");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waiter->total_len !=
|
|
||||||
waiter->header_len + ctx->http.rsp.content_length) {
|
|
||||||
LOG_ERR("Error while receiving data, "
|
|
||||||
"received %zd expected %zd bytes",
|
|
||||||
waiter->total_len, waiter->header_len +
|
|
||||||
ctx->http.rsp.content_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
k_sem_give(&waiter->wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_async_http_req(struct http_ctx *ctx,
|
|
||||||
enum http_method method,
|
|
||||||
const char *url,
|
|
||||||
const char *content_type,
|
|
||||||
const char *payload,
|
|
||||||
int count)
|
|
||||||
{
|
|
||||||
struct http_request req = {};
|
|
||||||
struct waiter waiter;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
req.method = method;
|
|
||||||
req.url = url;
|
|
||||||
req.protocol = " " HTTP_PROTOCOL;
|
|
||||||
|
|
||||||
k_sem_init(&waiter.wait, 0, 1);
|
|
||||||
|
|
||||||
waiter.total_len = 0;
|
|
||||||
|
|
||||||
LOG_INF("[%d] Send %s", count, url);
|
|
||||||
|
|
||||||
ret = http_client_send_req(ctx, &req, response, result, sizeof(result),
|
|
||||||
&waiter, APP_REQ_TIMEOUT);
|
|
||||||
if (ret < 0 && ret != -EINPROGRESS) {
|
|
||||||
LOG_ERR("Cannot send %s request (%d)", http_method_str(method),
|
|
||||||
ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k_sem_take(&waiter.wait, WAIT_TIME)) {
|
|
||||||
LOG_ERR("Timeout while waiting HTTP response");
|
|
||||||
ret = -ETIMEDOUT;
|
|
||||||
http_request_cancel(ctx);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
out:
|
|
||||||
http_close(ctx);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int do_sync_reqs(struct http_ctx *ctx, int count)
|
|
||||||
{
|
|
||||||
int max_count = 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, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = do_sync_http_req(&http_ctx, HTTP_HEAD, "/",
|
|
||||||
NULL, NULL, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SEND_OPTIONS
|
|
||||||
ret = do_sync_http_req(&http_ctx, HTTP_OPTIONS, "/index.html",
|
|
||||||
NULL, NULL, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ret = do_sync_http_req(&http_ctx, HTTP_POST, "/post_test.php",
|
|
||||||
POST_CONTENT_TYPE, POST_PAYLOAD,
|
|
||||||
max_count - count);
|
|
||||||
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()
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int do_async_reqs(struct http_ctx *ctx, int count)
|
|
||||||
{
|
|
||||||
int max_count = count;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* These examples use the HTTP client API asynchronously so they
|
|
||||||
* do set the callback parameter.
|
|
||||||
*/
|
|
||||||
while (count--) {
|
|
||||||
ret = do_async_http_req(&http_ctx, HTTP_GET, "/index.html",
|
|
||||||
NULL, NULL, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = do_async_http_req(&http_ctx, HTTP_HEAD, "/",
|
|
||||||
NULL, NULL, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SEND_OPTIONS
|
|
||||||
ret = do_async_http_req(&http_ctx, HTTP_OPTIONS, "/index.html",
|
|
||||||
NULL, NULL, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ret = do_async_http_req(&http_ctx, HTTP_POST, "/post_test.php",
|
|
||||||
POST_CONTENT_TYPE, POST_PAYLOAD,
|
|
||||||
max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = do_async_http_req(&http_ctx, HTTP_GET, "/big-file.html",
|
|
||||||
NULL, NULL, max_count - count);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void http_received(struct http_ctx *ctx,
|
|
||||||
struct net_pkt *pkt,
|
|
||||||
int status,
|
|
||||||
u32_t flags,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
if (!status) {
|
|
||||||
if (pkt) {
|
|
||||||
LOG_DBG("Received %d bytes data",
|
|
||||||
net_pkt_appdatalen(pkt));
|
|
||||||
net_pkt_unref(pkt);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_ERR("Receive error (%d)", status);
|
|
||||||
|
|
||||||
if (pkt) {
|
|
||||||
net_pkt_unref(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = http_client_init(&http_ctx, SERVER_ADDR, SERVER_PORT, NULL,
|
|
||||||
K_FOREVER);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("HTTP init failed (%d)", ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
|
|
||||||
net_app_set_net_pkt_pool(&http_ctx.app_ctx, tx_slab, data_pool);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
http_set_cb(&http_ctx, NULL, http_received, NULL, NULL);
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
ret = http_client_set_tls(&http_ctx,
|
|
||||||
https_result_buf,
|
|
||||||
sizeof(https_result_buf),
|
|
||||||
(u8_t *)INSTANCE_INFO,
|
|
||||||
strlen(INSTANCE_INFO),
|
|
||||||
setup_cert,
|
|
||||||
HOSTNAME,
|
|
||||||
NULL,
|
|
||||||
&ssl_pool,
|
|
||||||
https_stack,
|
|
||||||
K_THREAD_STACK_SIZEOF(https_stack));
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("HTTPS init failed (%d)", ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LOG_INF("--------Sending %d sync request--------", MAX_ITERATIONS);
|
|
||||||
|
|
||||||
ret = do_sync_reqs(&http_ctx, MAX_ITERATIONS);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INF("--------Sending %d async request--------", MAX_ITERATIONS);
|
|
||||||
|
|
||||||
ret = do_async_reqs(&http_ctx, MAX_ITERATIONS);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
http_release(&http_ctx);
|
|
||||||
|
|
||||||
LOG_INF("Done!");
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.13.1)
|
|
||||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
|
||||||
project(http_server)
|
|
||||||
|
|
||||||
FILE(GLOB app_sources src/*.c)
|
|
||||||
target_sources(app PRIVATE ${app_sources})
|
|
||||||
|
|
||||||
include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake)
|
|
||||||
|
|
||||||
# List of files that are used to generate .h file that can be included
|
|
||||||
# into .c file.
|
|
||||||
foreach(inc_file
|
|
||||||
echo-apps-cert.der
|
|
||||||
echo-apps-key.der
|
|
||||||
index.html
|
|
||||||
)
|
|
||||||
generate_inc_file_for_target(
|
|
||||||
app
|
|
||||||
src/${inc_file}
|
|
||||||
${ZEPHYR_BINARY_DIR}/include/generated/${inc_file}.inc
|
|
||||||
)
|
|
||||||
endforeach()
|
|
|
@ -1,195 +0,0 @@
|
||||||
.. _http-server-sample:
|
|
||||||
|
|
||||||
HTTP Server
|
|
||||||
###########
|
|
||||||
|
|
||||||
Overview
|
|
||||||
********
|
|
||||||
|
|
||||||
The HTTP Server sample application for Zephyr implements a basic TCP server
|
|
||||||
on top of the HTTP Server Library that is able to receive HTTP 1.1 requests,
|
|
||||||
parse them and write back the responses.
|
|
||||||
|
|
||||||
The source code for this sample application can be found at:
|
|
||||||
:file:`samples/net/http_server`.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
************
|
|
||||||
|
|
||||||
- Linux machine with wget and the screen terminal emulator
|
|
||||||
- Either QEMU or real device like Freedom Board (FRDM-K64F)
|
|
||||||
- For QEMU see this :ref:`networking_with_qemu`
|
|
||||||
- LAN for testing purposes (Ethernet)
|
|
||||||
|
|
||||||
|
|
||||||
Building and Running
|
|
||||||
********************
|
|
||||||
|
|
||||||
Currently, the HTTP Server application is configured to listen at port 80.
|
|
||||||
If you want to modify the http-server sample application, please check
|
|
||||||
the configuration settings in :file:`samples/net/http_server/src/main.c` file
|
|
||||||
and also in the :file:`samples/net/http_server/src/config.h` file.
|
|
||||||
|
|
||||||
To use QEMU for testing, follow the :ref:`networking_with_qemu` guide.
|
|
||||||
|
|
||||||
This sample code supports both static and dynamic (DHCPv4) IP addresses that
|
|
||||||
can be defined in the project configuration file:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
|
||||||
|
|
||||||
Adding URLs
|
|
||||||
===========
|
|
||||||
|
|
||||||
To define a new URL or to change how a URL is processed by the HTTP server,
|
|
||||||
open the :file:`samples/net/http_server/src/main.c` file and locate the
|
|
||||||
following lines:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
http_server_add_default(&http_urls, default_handler);
|
|
||||||
http_server_add_url(&http_urls, "/headers", HTTP_URL_STANDARD);
|
|
||||||
http_server_add_url(&http_urls, "/index.html", HTTP_URL_STANDARD);
|
|
||||||
|
|
||||||
The first line defines how Zephyr will deal with unknown URLs. In this case,
|
|
||||||
it will respond with a soft HTTP 404 status code, i.e. an HTTP 200 OK status
|
|
||||||
code with a 404 Not Found HTML body.
|
|
||||||
|
|
||||||
To build this sample on your Linux host computer, open a terminal window,
|
|
||||||
locate the source code of this sample application and type:
|
|
||||||
|
|
||||||
.. zephyr-app-commands::
|
|
||||||
:zephyr-app: samples/net/http_server
|
|
||||||
:host-os: unix
|
|
||||||
:board: qemu_x86
|
|
||||||
:goals: run
|
|
||||||
:compact:
|
|
||||||
|
|
||||||
|
|
||||||
Sample Output
|
|
||||||
=============
|
|
||||||
|
|
||||||
Assume in this example that this HTTP server is configured to listen at
|
|
||||||
IPv4 address 192.168.1.120 and IPv6 address 2001:db8::1 port 80.
|
|
||||||
On your Linux host computer, open a terminal window and type:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
wget 192.168.1.120/index.html
|
|
||||||
|
|
||||||
wget will show:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
--2017-01-17 00:37:44-- http://192.168.1.120/
|
|
||||||
Connecting to 192.168.1.120:80... connected.
|
|
||||||
HTTP request sent, awaiting response... 200 OK
|
|
||||||
Length: unspecified [text/html]
|
|
||||||
Saving to: 'index.html'
|
|
||||||
|
|
||||||
The HTML file generated by Zephyr and downloaded by wget is:
|
|
||||||
|
|
||||||
.. code-block:: html
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Zephyr HTTP Server</title>
|
|
||||||
</head>
|
|
||||||
<body><h1><center>It Works!</center></h1></body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
The screen application will display the following information:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
[http-server] [DBG] http_connected: (0x00403fa0): HTTP connect attempt URL /index.html
|
|
||||||
[http-server] [DBG] http_serve_index_html: (0x00403fa0): Sending index.html (170 bytes) to client
|
|
||||||
[http-server] [DBG] http_closed: (0x00403fa0): Connection 0x004004c0 closed
|
|
||||||
|
|
||||||
To obtain the HTTP Header Fields web page, use the following command:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
wget 192.168.1.120/headers -O index.html
|
|
||||||
|
|
||||||
wget will show:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
--2017-01-19 22:09:55-- http://192.168.1.120/headers
|
|
||||||
Connecting to 192.168.1.120:80... connected.
|
|
||||||
HTTP request sent, awaiting response... 200 OK
|
|
||||||
Length: unspecified [text/html]
|
|
||||||
Saving to: 'index.html'
|
|
||||||
|
|
||||||
This is the HTML file generated by Zephyr and downloaded by wget:
|
|
||||||
|
|
||||||
.. code-block:: html
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Zephyr HTTP Server</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Zephyr HTTP server</h1>
|
|
||||||
<h2>HTTP Header Fields</h2>
|
|
||||||
<ul>
|
|
||||||
<li>User-Agent: Wget/1.16 (linux-gnu)</li>
|
|
||||||
<li>Accept: */*</li>
|
|
||||||
<li>Host: 192.168.1.120</li>
|
|
||||||
<li>Connection: Keep-Alive</li>
|
|
||||||
</ul>
|
|
||||||
<h2>HTTP Method: GET</h2>
|
|
||||||
<h2>URL: /headers</h2>
|
|
||||||
<h2>Server: arm</h2>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
To test the 404 Not Found soft error, use the following command:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
wget 192.168.1.120/not_found -O index.html
|
|
||||||
|
|
||||||
Zephyr will generate an HTTP response with the following header:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: text/html
|
|
||||||
Transfer-Encoding: chunked
|
|
||||||
|
|
||||||
and this is the HTML message that wget will save:
|
|
||||||
|
|
||||||
.. code-block:: html
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Zephyr HTTP Server</title>
|
|
||||||
</head>
|
|
||||||
<body><h1><center>404 Not Found</center></h1></body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
HTTPS Server
|
|
||||||
============
|
|
||||||
|
|
||||||
The sample code also includes a HTTPS (HTTP over TLS) server example
|
|
||||||
running side by side with the HTTP server, this server runs on QEMU.
|
|
||||||
In order to compile and run the code execute:
|
|
||||||
|
|
||||||
.. zephyr-app-commands::
|
|
||||||
:zephyr-app: samples/net/http_server
|
|
||||||
:host-os: unix
|
|
||||||
:board: qemu_x86
|
|
||||||
:conf: prj_tls.conf
|
|
||||||
:goals: run
|
|
||||||
:compact:
|
|
||||||
|
|
||||||
|
|
||||||
Known Issues and Limitations
|
|
||||||
============================
|
|
||||||
|
|
||||||
- Currently, this sample application only generates HTTP responses in
|
|
||||||
chunk transfer mode.
|
|
|
@ -1,26 +0,0 @@
|
||||||
# USB Device settings
|
|
||||||
CONFIG_USB=y
|
|
||||||
CONFIG_USB_DEVICE_STACK=y
|
|
||||||
|
|
||||||
# Select USB Configurations
|
|
||||||
CONFIG_USB_DEVICE_NETWORK_ECM=y
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
CONFIG_USB_DRIVER_LOG_LEVEL_INF=y
|
|
||||||
CONFIG_USB_DEVICE_LOG_LEVEL_INF=y
|
|
||||||
|
|
||||||
# Zero Configuration
|
|
||||||
# Remove hardcoded addresses
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=n
|
|
||||||
|
|
||||||
CONFIG_NET_IF_MCAST_IPV4_ADDR_COUNT=2
|
|
||||||
CONFIG_NET_IPV4_AUTO=y
|
|
||||||
CONFIG_NET_HOSTNAME_ENABLE=y
|
|
||||||
CONFIG_DNS_RESOLVER=y
|
|
||||||
CONFIG_LLMNR_RESOLVER=y
|
|
||||||
CONFIG_LLMNR_RESPONDER=y
|
|
||||||
|
|
||||||
# Reduce image size
|
|
||||||
CONFIG_NET_STATISTICS=n
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV6=n
|
|
||||||
CONFIG_NET_IPV6=n
|
|
|
@ -1,43 +0,0 @@
|
||||||
CONFIG_NETWORKING=y
|
|
||||||
CONFIG_NET_TCP=y
|
|
||||||
CONFIG_ENTROPY_GENERATOR=y
|
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
|
||||||
CONFIG_NET_LOG=y
|
|
||||||
CONFIG_LOG=y
|
|
||||||
CONFIG_INIT_STACKS=y
|
|
||||||
|
|
||||||
CONFIG_NET_PKT_RX_COUNT=32
|
|
||||||
CONFIG_NET_PKT_TX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_RX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_TX_COUNT=32
|
|
||||||
|
|
||||||
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
|
|
||||||
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
|
|
||||||
|
|
||||||
CONFIG_NET_MAX_CONTEXTS=16
|
|
||||||
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_SERVER=y
|
|
||||||
# Allow two concurrent connections
|
|
||||||
CONFIG_NET_TCP_BACKLOG_SIZE=2
|
|
||||||
CONFIG_NET_APP_SERVER_NUM_CONN=2
|
|
||||||
|
|
||||||
CONFIG_NET_IPV6=y
|
|
||||||
CONFIG_NET_IPV4=y
|
|
||||||
#CONFIG_NET_DHCPV4=y
|
|
||||||
|
|
||||||
CONFIG_HTTPS=n
|
|
||||||
CONFIG_MBEDTLS=n
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=y
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV6=y
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV4=y
|
|
||||||
|
|
||||||
CONFIG_NET_SHELL=y
|
|
||||||
CONFIG_NET_STATISTICS=y
|
|
||||||
|
|
||||||
CONFIG_NET_MGMT=y
|
|
||||||
CONFIG_NET_MGMT_EVENT=y
|
|
|
@ -1,50 +0,0 @@
|
||||||
CONFIG_NETWORKING=y
|
|
||||||
CONFIG_NET_TCP=y
|
|
||||||
CONFIG_ENTROPY_GENERATOR=y
|
|
||||||
CONFIG_NET_LOG=y
|
|
||||||
CONFIG_LOG=y
|
|
||||||
CONFIG_INIT_STACKS=y
|
|
||||||
|
|
||||||
CONFIG_NET_PKT_RX_COUNT=16
|
|
||||||
CONFIG_NET_PKT_TX_COUNT=16
|
|
||||||
CONFIG_NET_BUF_RX_COUNT=16
|
|
||||||
CONFIG_NET_BUF_TX_COUNT=16
|
|
||||||
|
|
||||||
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
|
|
||||||
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
|
|
||||||
|
|
||||||
CONFIG_STDOUT_CONSOLE=y
|
|
||||||
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_SERVER=y
|
|
||||||
|
|
||||||
CONFIG_NET_IPV6=y
|
|
||||||
CONFIG_NET_IPV4=n
|
|
||||||
|
|
||||||
CONFIG_HTTPS=y
|
|
||||||
CONFIG_MBEDTLS=y
|
|
||||||
CONFIG_MBEDTLS_BUILTIN=y
|
|
||||||
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
|
|
||||||
CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2500
|
|
||||||
CONFIG_MBEDTLS_ENABLE_HEAP=y
|
|
||||||
CONFIG_MBEDTLS_HEAP_SIZE=12000
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=y
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_BT_NODE=y
|
|
||||||
|
|
||||||
CONFIG_NET_SHELL=y
|
|
||||||
|
|
||||||
CONFIG_NET_MGMT=y
|
|
||||||
CONFIG_NET_MGMT_EVENT=y
|
|
||||||
|
|
||||||
CONFIG_BT=y
|
|
||||||
CONFIG_BT_DEBUG_LOG=y
|
|
||||||
CONFIG_BT_SMP=y
|
|
||||||
CONFIG_BT_PERIPHERAL=y
|
|
||||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
|
||||||
CONFIG_BT_DEVICE_NAME="Test Http_Server"
|
|
||||||
CONFIG_NET_UDP=y
|
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
|
||||||
CONFIG_NET_L2_BT=y
|
|
||||||
CONFIG_NET_STATISTICS=y
|
|
|
@ -1,48 +0,0 @@
|
||||||
CONFIG_NETWORKING=y
|
|
||||||
CONFIG_NET_TCP=y
|
|
||||||
CONFIG_ENTROPY_GENERATOR=y
|
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
|
||||||
CONFIG_NET_LOG=y
|
|
||||||
CONFIG_LOG=y
|
|
||||||
CONFIG_INIT_STACKS=y
|
|
||||||
|
|
||||||
CONFIG_NET_PKT_RX_COUNT=32
|
|
||||||
CONFIG_NET_PKT_TX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_RX_COUNT=32
|
|
||||||
CONFIG_NET_BUF_TX_COUNT=32
|
|
||||||
|
|
||||||
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
|
|
||||||
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
|
|
||||||
|
|
||||||
CONFIG_NET_MAX_CONTEXTS=16
|
|
||||||
|
|
||||||
CONFIG_MBEDTLS_DEBUG=n
|
|
||||||
CONFIG_MBEDTLS_DEBUG_LEVEL=1
|
|
||||||
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_SERVER=y
|
|
||||||
|
|
||||||
CONFIG_NET_IPV6=y
|
|
||||||
CONFIG_NET_IPV4=y
|
|
||||||
#CONFIG_NET_DHCPV4=y
|
|
||||||
|
|
||||||
CONFIG_HTTPS=y
|
|
||||||
CONFIG_MBEDTLS=y
|
|
||||||
CONFIG_MBEDTLS_BUILTIN=y
|
|
||||||
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
|
|
||||||
CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2500
|
|
||||||
CONFIG_MBEDTLS_ENABLE_HEAP=y
|
|
||||||
CONFIG_MBEDTLS_HEAP_SIZE=30000
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_SETTINGS=y
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
|
||||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
|
||||||
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV6=y
|
|
||||||
CONFIG_NET_CONFIG_NEED_IPV4=y
|
|
||||||
|
|
||||||
CONFIG_NET_SHELL=y
|
|
||||||
CONFIG_NET_STATISTICS=y
|
|
||||||
|
|
||||||
CONFIG_NET_MGMT=y
|
|
||||||
CONFIG_NET_MGMT_EVENT=y
|
|
|
@ -1,28 +0,0 @@
|
||||||
sample:
|
|
||||||
name: HTTP Server
|
|
||||||
tests:
|
|
||||||
test:
|
|
||||||
harness: net
|
|
||||||
depends_on: netif
|
|
||||||
tags: net http
|
|
||||||
min_flash: 140
|
|
||||||
test_bt:
|
|
||||||
harness: net
|
|
||||||
extra_args: CONF_FILE="prj_bt.conf"
|
|
||||||
platform_whitelist: qemu_x86
|
|
||||||
tags: net http
|
|
||||||
test_usbnet_ecm:
|
|
||||||
harness: net
|
|
||||||
extra_args: OVERLAY_CONFIG="overlay-netusb.conf"
|
|
||||||
tags: net usb
|
|
||||||
depends_on: usb_device
|
|
||||||
min_ram: 64
|
|
||||||
test_usbnet_eem:
|
|
||||||
harness: net
|
|
||||||
extra_args: OVERLAY_CONFIG="overlay-netusb.conf"
|
|
||||||
extra_configs:
|
|
||||||
- CONFIG_USB_DEVICE_NETWORK_ECM=n
|
|
||||||
- CONFIG_USB_DEVICE_NETWORK_EEM=y
|
|
||||||
tags: net usb
|
|
||||||
depends_on: usb_device
|
|
||||||
min_ram: 64
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 Intel Corporation
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CONFIG_H_
|
|
||||||
#define _CONFIG_H_
|
|
||||||
|
|
||||||
/* 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)
|
|
||||||
|
|
||||||
#ifdef CONFIG_NET_CONFIG_SETTINGS
|
|
||||||
#ifdef CONFIG_NET_IPV6
|
|
||||||
#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV6_ADDR
|
|
||||||
#else
|
|
||||||
#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifdef CONFIG_NET_IPV6
|
|
||||||
#define ZEPHYR_ADDR "2001:db8::1"
|
|
||||||
#else
|
|
||||||
#define ZEPHYR_ADDR "192.0.2.1"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ZEPHYR_PORT
|
|
||||||
#define ZEPHYR_PORT 8080
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HTTP_AUTH_URL "/auth"
|
|
||||||
#define HTTP_AUTH_TYPE "Basic"
|
|
||||||
|
|
||||||
/* HTTP Basic Auth, see https://tools.ietf.org/html/rfc7617 */
|
|
||||||
#define HTTP_AUTH_REALM "Zephyr"
|
|
||||||
#define HTTP_AUTH_USERNAME "zephyr"
|
|
||||||
#define HTTP_AUTH_PASSWORD "0123456789"
|
|
||||||
|
|
||||||
#endif
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,12 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Zephyr HTTP server sample</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div>
|
|
||||||
<p>It works!</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,479 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 Intel Corporation
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
#define LOG_MODULE_NAME net_https_server_sample
|
|
||||||
#else
|
|
||||||
#define LOG_MODULE_NAME net_http_server_sample
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <logging/log.h>
|
|
||||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);
|
|
||||||
|
|
||||||
#include <zephyr.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <net/net_context.h>
|
|
||||||
|
|
||||||
#include <net/http.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
/* Sets the network parameters */
|
|
||||||
|
|
||||||
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
|
|
||||||
NET_PKT_TX_SLAB_DEFINE(http_srv_tx, 15);
|
|
||||||
NET_PKT_DATA_POOL_DEFINE(http_srv_data, 30);
|
|
||||||
|
|
||||||
static struct k_mem_slab *tx_slab(void)
|
|
||||||
{
|
|
||||||
return &http_srv_tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct net_buf_pool *data_pool(void)
|
|
||||||
{
|
|
||||||
return &http_srv_data;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#if defined(CONFIG_NET_L2_BT)
|
|
||||||
#error "TCP connections over Bluetooth need CONFIG_NET_CONTEXT_NET_PKT_POOL "\
|
|
||||||
"defined."
|
|
||||||
#endif /* CONFIG_NET_L2_BT */
|
|
||||||
|
|
||||||
#define tx_slab NULL
|
|
||||||
#define data_pool NULL
|
|
||||||
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
|
|
||||||
|
|
||||||
#define RESULT_BUF_SIZE 1500
|
|
||||||
static u8_t http_result[RESULT_BUF_SIZE];
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
#if !defined(CONFIG_HTTPS_STACK_SIZE)
|
|
||||||
#define CONFIG_HTTPS_STACK_SIZE 8192
|
|
||||||
#endif /* CONFIG_HTTPS_STACK_SIZE */
|
|
||||||
|
|
||||||
#define APP_BANNER "Run HTTPS server"
|
|
||||||
#define INSTANCE_INFO "Zephyr HTTPS example server #1"
|
|
||||||
|
|
||||||
/* Note that each HTTPS context needs its own stack as there will be
|
|
||||||
* a separate thread for each HTTPS context.
|
|
||||||
*/
|
|
||||||
NET_STACK_DEFINE(HTTPS, https_stack, CONFIG_NET_APP_TLS_STACK_SIZE,
|
|
||||||
CONFIG_NET_APP_TLS_STACK_SIZE);
|
|
||||||
|
|
||||||
#define RX_FIFO_DEPTH 4
|
|
||||||
K_MEM_POOL_DEFINE(ssl_rx_pool, 4, 64, RX_FIFO_DEPTH, 4);
|
|
||||||
|
|
||||||
#else /* CONFIG_HTTPS */
|
|
||||||
#define APP_BANNER "Run HTTP server"
|
|
||||||
#endif /* CONFIG_HTTPS */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that the http_server_ctx and http_server_urls are quite large so be
|
|
||||||
* careful if those are allocated from stack.
|
|
||||||
*/
|
|
||||||
static struct http_ctx http_ctx;
|
|
||||||
static struct http_server_urls http_urls;
|
|
||||||
|
|
||||||
void panic(const char *msg)
|
|
||||||
{
|
|
||||||
if (msg) {
|
|
||||||
LOG_ERR("%s", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
k_sleep(K_FOREVER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HTTP_STATUS_200_OK "HTTP/1.1 200 OK\r\n" \
|
|
||||||
"Content-Type: text/html\r\n" \
|
|
||||||
"Transfer-Encoding: chunked\r\n"
|
|
||||||
|
|
||||||
#define HTTP_STATUS_200_OK_GZ "HTTP/1.1 200 OK\r\n" \
|
|
||||||
"Content-Type: text/html\r\n" \
|
|
||||||
"Transfer-Encoding: chunked\r\n" \
|
|
||||||
"Content-Encoding: gzip\r\n"
|
|
||||||
|
|
||||||
#define HTML_HEADER "<html><head>" \
|
|
||||||
"<title>Zephyr HTTP Server</title>" \
|
|
||||||
"</head><body><h1>" \
|
|
||||||
"<center>Zephyr HTTP server</center></h1>\r\n"
|
|
||||||
|
|
||||||
#define HTML_FOOTER "</body></html>\r\n"
|
|
||||||
|
|
||||||
static int http_response(struct http_ctx *ctx, const char *header,
|
|
||||||
const char *payload, size_t payload_len,
|
|
||||||
char *str, const struct sockaddr *dst)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, header, dst, str);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot add HTTP header (%d)", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, HTTP_CRLF, dst, str);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_send_chunk(ctx, payload, payload_len, dst, str);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot send data to peer (%d)", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_send_chunk(ctx, NULL, 0, dst, NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot send data to peer (%d)", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return http_send_flush(ctx, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int http_response_soft_404(struct http_ctx *ctx,
|
|
||||||
const struct sockaddr *dst)
|
|
||||||
{
|
|
||||||
static const char *not_found =
|
|
||||||
HTML_HEADER
|
|
||||||
"<h2><center>404 Not Found</center></h2>"
|
|
||||||
HTML_FOOTER;
|
|
||||||
|
|
||||||
return http_response(ctx, HTTP_STATUS_200_OK, not_found,
|
|
||||||
strlen(not_found), "Error", dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints the received HTTP header fields as an HTML list */
|
|
||||||
static void print_http_headers(struct http_ctx *ctx,
|
|
||||||
char *str, int size)
|
|
||||||
{
|
|
||||||
int offset;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = snprintf(str, size,
|
|
||||||
HTML_HEADER
|
|
||||||
"<h2>HTTP Header Fields</h2>\r\n<ul>\r\n");
|
|
||||||
if (ret < 0 || ret >= size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = ret;
|
|
||||||
|
|
||||||
for (u8_t i = 0; i < ctx->http.field_values_ctr; i++) {
|
|
||||||
struct http_field_value *kv = &ctx->http.field_values[i];
|
|
||||||
|
|
||||||
ret = snprintf(str + offset, size - offset,
|
|
||||||
"<li>%.*s: %.*s</li>\r\n",
|
|
||||||
kv->key_len, kv->key,
|
|
||||||
kv->value_len, kv->value);
|
|
||||||
if (ret < 0 || ret >= size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = snprintf(str + offset, size - offset, "</ul>\r\n");
|
|
||||||
if (ret < 0 || ret >= size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ret;
|
|
||||||
|
|
||||||
ret = snprintf(str + offset, size - offset,
|
|
||||||
"<h2>HTTP Method: %s</h2>\r\n",
|
|
||||||
http_method_str(ctx->http.parser.method));
|
|
||||||
if (ret < 0 || ret >= size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ret;
|
|
||||||
|
|
||||||
ret = snprintf(str + offset, size - offset,
|
|
||||||
"<h2>URL: %.*s</h2>\r\n",
|
|
||||||
ctx->http.url_len, ctx->http.url);
|
|
||||||
if (ret < 0 || ret >= size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ret;
|
|
||||||
|
|
||||||
snprintf(str + offset, size - offset,
|
|
||||||
"<h2>Server: %s</h2>"HTML_FOOTER, CONFIG_ARCH);
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_serve_headers(struct http_ctx *ctx,
|
|
||||||
const struct sockaddr *dst)
|
|
||||||
{
|
|
||||||
#define HTTP_MAX_BODY_STR_SIZE 1024
|
|
||||||
static char html_body[HTTP_MAX_BODY_STR_SIZE];
|
|
||||||
|
|
||||||
print_http_headers(ctx, html_body, HTTP_MAX_BODY_STR_SIZE);
|
|
||||||
|
|
||||||
return http_response(ctx, HTTP_STATUS_200_OK, html_body,
|
|
||||||
strlen(html_body), "Headers", dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int http_serve_index_html(struct http_ctx *ctx,
|
|
||||||
const struct sockaddr *dst)
|
|
||||||
{
|
|
||||||
static const char index_html[] = {
|
|
||||||
#include "index.html.inc"
|
|
||||||
};
|
|
||||||
|
|
||||||
LOG_DBG("Sending index.html (%zd bytes) to client",
|
|
||||||
sizeof(index_html));
|
|
||||||
|
|
||||||
return http_response(ctx, HTTP_STATUS_200_OK, index_html,
|
|
||||||
sizeof(index_html), "Index", dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void http_connected(struct http_ctx *ctx,
|
|
||||||
enum http_connection_type type,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
char url[32];
|
|
||||||
int len = min(sizeof(url) - 1, ctx->http.url_len);
|
|
||||||
|
|
||||||
memcpy(url, ctx->http.url, len);
|
|
||||||
url[len] = '\0';
|
|
||||||
|
|
||||||
LOG_DBG("%s connect attempt URL %s",
|
|
||||||
type == HTTP_CONNECTION ? "HTTP" : "WS", url);
|
|
||||||
|
|
||||||
if (type == HTTP_CONNECTION) {
|
|
||||||
if (strncmp(ctx->http.url, "/",
|
|
||||||
ctx->http.url_len) == 0) {
|
|
||||||
http_serve_index_html(ctx, dst);
|
|
||||||
http_close(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(ctx->http.url, "/index.html",
|
|
||||||
ctx->http.url_len) == 0) {
|
|
||||||
http_serve_index_html(ctx, dst);
|
|
||||||
http_close(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(ctx->http.url, "/headers",
|
|
||||||
ctx->http.url_len) == 0) {
|
|
||||||
http_serve_headers(ctx, dst);
|
|
||||||
http_close(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Give 404 error for all the other URLs we do not want to handle
|
|
||||||
* right now.
|
|
||||||
*/
|
|
||||||
http_response_soft_404(ctx, dst);
|
|
||||||
http_close(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void http_received(struct http_ctx *ctx,
|
|
||||||
struct net_pkt *pkt,
|
|
||||||
int status,
|
|
||||||
u32_t flags,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
if (!status) {
|
|
||||||
if (pkt) {
|
|
||||||
LOG_DBG("Received %d bytes data",
|
|
||||||
net_pkt_appdatalen(pkt));
|
|
||||||
net_pkt_unref(pkt);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_ERR("Receive error (%d)", status);
|
|
||||||
|
|
||||||
if (pkt) {
|
|
||||||
net_pkt_unref(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void http_sent(struct http_ctx *ctx,
|
|
||||||
int status,
|
|
||||||
void *user_data_send,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
LOG_DBG("%s sent", (char *)user_data_send);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void http_closed(struct http_ctx *ctx,
|
|
||||||
int status,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
LOG_DBG("Connection %p closed", ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *get_string(int str_len, const char *str)
|
|
||||||
{
|
|
||||||
static char buf[64];
|
|
||||||
int len = min(str_len, sizeof(buf) - 1);
|
|
||||||
|
|
||||||
memcpy(buf, str, len);
|
|
||||||
buf[len] = '\0';
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum http_verdict default_handler(struct http_ctx *ctx,
|
|
||||||
enum http_connection_type type,
|
|
||||||
const struct sockaddr *dst)
|
|
||||||
{
|
|
||||||
LOG_DBG("No handler for %s URL %s",
|
|
||||||
type == HTTP_CONNECTION ? "HTTP" : "WS",
|
|
||||||
get_string(ctx->http.url_len, ctx->http.url));
|
|
||||||
|
|
||||||
if (type == HTTP_CONNECTION) {
|
|
||||||
http_response_soft_404(ctx, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
return HTTP_VERDICT_DROP;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
/* Load the certificates and private RSA key. */
|
|
||||||
|
|
||||||
static const char echo_apps_cert_der[] = {
|
|
||||||
#include "echo-apps-cert.der.inc"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char echo_apps_key_der[] = {
|
|
||||||
#include "echo-apps-key.der.inc"
|
|
||||||
};
|
|
||||||
|
|
||||||
static int setup_cert(struct net_app_ctx *app_ctx,
|
|
||||||
mbedtls_x509_crt *cert,
|
|
||||||
mbedtls_pk_context *pkey)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mbedtls_x509_crt_parse(cert, echo_apps_cert_der,
|
|
||||||
sizeof(echo_apps_cert_der));
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("mbedtls_x509_crt_parse returned %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mbedtls_pk_parse_key(pkey, echo_apps_key_der,
|
|
||||||
sizeof(echo_apps_key_der),
|
|
||||||
NULL, 0);
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("mbedtls_pk_parse_key returned %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_HTTPS */
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
struct sockaddr addr, *server_addr;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There are several options here for binding to local address.
|
|
||||||
* 1) The server address can be left empty in which case the
|
|
||||||
* library will bind to both IPv4 and IPv6 addresses and to
|
|
||||||
* port 80 which is the default.
|
|
||||||
* 2) The server address can be partially filled, meaning that
|
|
||||||
* the address can be left to 0 and port can be set if a value
|
|
||||||
* other than 80 is desired. If the protocol family in sockaddr
|
|
||||||
* is set to AF_UNSPEC, then both IPv4 and IPv6 socket is bound.
|
|
||||||
* 3) The address can be set to some real value. There is a helper
|
|
||||||
* function that can be used to fill the socket address struct.
|
|
||||||
*/
|
|
||||||
#define ADDR_OPTION 1
|
|
||||||
|
|
||||||
#if ADDR_OPTION == 1
|
|
||||||
server_addr = NULL;
|
|
||||||
|
|
||||||
ARG_UNUSED(addr);
|
|
||||||
|
|
||||||
#elif ADDR_OPTION == 2
|
|
||||||
/* Accept any local listening address */
|
|
||||||
(void)memset(&addr, 0, sizeof(addr));
|
|
||||||
|
|
||||||
net_sin(&addr)->sin_port = htons(ZEPHYR_PORT);
|
|
||||||
|
|
||||||
/* In this example, listen only IPv4 */
|
|
||||||
addr.family = AF_INET;
|
|
||||||
|
|
||||||
server_addr = &addr;
|
|
||||||
|
|
||||||
#elif ADDR_OPTION == 3
|
|
||||||
/* Set the bind address according to your configuration */
|
|
||||||
(void)memset(&addr, 0, sizeof(addr));
|
|
||||||
|
|
||||||
/* In this example, listen only IPv6 */
|
|
||||||
addr.family = AF_INET6;
|
|
||||||
|
|
||||||
ret = http_server_set_local_addr(&addr, ZEPHYR_ADDR, ZEPHYR_PORT);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot set local address (%d)", ret);
|
|
||||||
panic(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
server_addr = &addr;
|
|
||||||
|
|
||||||
#else
|
|
||||||
server_addr = NULL;
|
|
||||||
|
|
||||||
ARG_UNUSED(addr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
http_server_add_default(&http_urls, default_handler);
|
|
||||||
http_server_add_url(&http_urls, "/headers", HTTP_URL_STANDARD);
|
|
||||||
http_server_add_url(&http_urls, "/index.html", HTTP_URL_STANDARD);
|
|
||||||
http_server_add_url(&http_urls, "/", HTTP_URL_STANDARD);
|
|
||||||
|
|
||||||
ret = http_server_init(&http_ctx, &http_urls, server_addr, http_result,
|
|
||||||
sizeof(http_result), "Zephyr HTTP Server",
|
|
||||||
NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot initialize HTTP server (%d)", ret);
|
|
||||||
panic(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
|
|
||||||
net_app_set_net_pkt_pool(&http_ctx.app_ctx, tx_slab, data_pool);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
http_set_cb(&http_ctx, http_connected, http_received,
|
|
||||||
http_sent, http_closed);
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTPS)
|
|
||||||
ret = http_server_set_tls(&http_ctx,
|
|
||||||
APP_BANNER,
|
|
||||||
INSTANCE_INFO,
|
|
||||||
strlen(INSTANCE_INFO),
|
|
||||||
setup_cert,
|
|
||||||
NULL,
|
|
||||||
&ssl_rx_pool,
|
|
||||||
https_stack,
|
|
||||||
K_THREAD_STACK_SIZEOF(https_stack));
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Cannot enable TLS support (%d)", ret);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If needed, the HTTP parser callbacks can be set according to
|
|
||||||
* applications own needs before enabling the server. In this example
|
|
||||||
* we use the default callbacks defined in HTTP server API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
http_server_enable(&http_ctx);
|
|
||||||
}
|
|
|
@ -6,11 +6,3 @@ endif()
|
||||||
|
|
||||||
zephyr_library_sources_if_kconfig(http_parser.c)
|
zephyr_library_sources_if_kconfig(http_parser.c)
|
||||||
zephyr_library_sources_if_kconfig(http_parser_url.c)
|
zephyr_library_sources_if_kconfig(http_parser_url.c)
|
||||||
|
|
||||||
zephyr_library_sources_if_kconfig(http.c)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
|
@ -3,84 +3,6 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
|
|
||||||
config HTTP
|
|
||||||
bool "HTTP support"
|
|
||||||
help
|
|
||||||
This option enables the HTTP library
|
|
||||||
|
|
||||||
if HTTP
|
|
||||||
|
|
||||||
config HTTP_SERVER
|
|
||||||
bool "HTTP server support"
|
|
||||||
select HTTP_PARSER
|
|
||||||
select HTTP_PARSER_URL
|
|
||||||
select NET_APP_SERVER
|
|
||||||
help
|
|
||||||
Enables HTTP server routines.
|
|
||||||
|
|
||||||
config HTTP_CLIENT
|
|
||||||
bool "HTTP client support"
|
|
||||||
select HTTP_PARSER
|
|
||||||
select HTTP_PARSER_URL
|
|
||||||
select NET_APP_CLIENT
|
|
||||||
help
|
|
||||||
Enables HTTP client routines.
|
|
||||||
|
|
||||||
config HTTP_HEADERS
|
|
||||||
int "HTTP header field max number of items"
|
|
||||||
depends on HTTP_SERVER
|
|
||||||
default 20 if WEBSOCKET
|
|
||||||
default 8
|
|
||||||
help
|
|
||||||
Number of HTTP header field items that an HTTP server
|
|
||||||
application will handle. If websocket is enabled, then the
|
|
||||||
default needs to be much bigger.
|
|
||||||
|
|
||||||
config HTTPS
|
|
||||||
bool "HTTPS support"
|
|
||||||
select NET_APP_TLS
|
|
||||||
help
|
|
||||||
Enables HTTPS support.
|
|
||||||
|
|
||||||
config HTTP_SERVER_CONNECTIONS
|
|
||||||
int "Max number of concurrent HTTP server connections"
|
|
||||||
default NET_APP_SERVER_NUM_CONN
|
|
||||||
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, so set CONFIG_NET_APP_SERVER_NUM_CONN and
|
|
||||||
CONFIG_NET_TCP_BACKLOG_SIZE to 1 in that case.
|
|
||||||
|
|
||||||
config HTTP_SERVER_NUM_URLS
|
|
||||||
int "Max number of URLs that the HTTP server will handle"
|
|
||||||
default 8
|
|
||||||
depends on HTTP_SERVER
|
|
||||||
help
|
|
||||||
This value determines how many URLs this HTTP server can handle.
|
|
||||||
|
|
||||||
config HTTP_CLIENT_NETWORK_TIMEOUT
|
|
||||||
int "Default network activity timeout in seconds"
|
|
||||||
default 20
|
|
||||||
depends on HTTP_CLIENT
|
|
||||||
help
|
|
||||||
Default network activity timeout in seconds. This setting is used
|
|
||||||
for TCP connection timeout.
|
|
||||||
|
|
||||||
module = HTTP
|
|
||||||
module-dep = NET_LOG
|
|
||||||
module-str = Log level for HTTP
|
|
||||||
module-help = Enables routing engine debug messages.
|
|
||||||
source "subsys/net/Kconfig.template.log_config.net"
|
|
||||||
|
|
||||||
config NET_DEBUG_HTTP_CONN
|
|
||||||
bool "Debug HTTP connections"
|
|
||||||
depends on HTTP && NET_LOG
|
|
||||||
help
|
|
||||||
Enables HTTP connection tracking.
|
|
||||||
|
|
||||||
endif # HTTP
|
|
||||||
|
|
||||||
config HTTP_PARSER
|
config HTTP_PARSER
|
||||||
bool "HTTP Parser support"
|
bool "HTTP Parser support"
|
||||||
select HTTP_PARSER_URL
|
select HTTP_PARSER_URL
|
||||||
|
|
|
@ -1,290 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 Intel Corporation.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <logging/log.h>
|
|
||||||
LOG_MODULE_REGISTER(net_http, CONFIG_HTTP_LOG_LEVEL);
|
|
||||||
|
|
||||||
#include <zephyr.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <version.h>
|
|
||||||
#include <misc/printk.h>
|
|
||||||
|
|
||||||
#include <net/net_core.h>
|
|
||||||
#include <net/net_ip.h>
|
|
||||||
#include <net/http.h>
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!ctx) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx->is_init) {
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->cb.connect = connect_cb;
|
|
||||||
ctx->cb.recv = recv_cb;
|
|
||||||
ctx->cb.send = send_cb;
|
|
||||||
ctx->cb.close = close_cb;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_close(struct http_ctx *ctx)
|
|
||||||
{
|
|
||||||
if (!ctx) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx->is_init) {
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
http_send_flush(ctx, NULL);
|
|
||||||
if (ctx->pending) {
|
|
||||||
net_pkt_unref(ctx->pending);
|
|
||||||
ctx->pending = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_NET_DEBUG_HTTP_CONN)
|
|
||||||
if (!ctx->is_client) {
|
|
||||||
http_server_conn_del(ctx);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_WEBSOCKET)
|
|
||||||
if (ctx->websocket.pending) {
|
|
||||||
net_pkt_unref(ctx->websocket.pending);
|
|
||||||
ctx->websocket.pending = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->websocket.data_waiting = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return net_app_close(&ctx->app_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_release(struct http_ctx *ctx)
|
|
||||||
{
|
|
||||||
if (!ctx) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx->is_init) {
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->is_tls = false;
|
|
||||||
|
|
||||||
#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_NET_DEBUG_HTTP_CONN)
|
|
||||||
if (!ctx->is_client) {
|
|
||||||
http_server_conn_del(ctx);
|
|
||||||
http_server_disable(ctx);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ctx->pending) {
|
|
||||||
net_pkt_unref(ctx->pending);
|
|
||||||
ctx->pending = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->is_init = false;
|
|
||||||
|
|
||||||
return net_app_release(&ctx->app_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_send_msg_raw(struct http_ctx *ctx, struct net_pkt *pkt,
|
|
||||||
void *user_send_data)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
NET_DBG("[%p] Sending %zd bytes data", ctx, net_pkt_get_len(pkt));
|
|
||||||
|
|
||||||
ret = net_app_send_pkt(&ctx->app_ctx, pkt, NULL, 0, 0,
|
|
||||||
user_send_data);
|
|
||||||
if (!ret) {
|
|
||||||
/* 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct net_pkt *get_net_pkt(struct http_ctx *ctx,
|
|
||||||
const struct sockaddr *dst)
|
|
||||||
{
|
|
||||||
if (!dst) {
|
|
||||||
return net_app_get_net_pkt(&ctx->app_ctx, AF_UNSPEC,
|
|
||||||
ctx->timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return net_app_get_net_pkt_with_dst(&ctx->app_ctx, dst, ctx->timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_prepare_and_send(struct http_ctx *ctx,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_send_data)
|
|
||||||
{
|
|
||||||
size_t added;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (!ctx->pending) {
|
|
||||||
ctx->pending = get_net_pkt(ctx, dst);
|
|
||||||
if (!ctx->pending) {
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = net_pkt_append(ctx->pending, payload_len, payload,
|
|
||||||
ctx->timeout);
|
|
||||||
if (!ret || ret > payload_len) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
added = ret;
|
|
||||||
|
|
||||||
payload_len -= added;
|
|
||||||
if (payload_len) {
|
|
||||||
payload += added;
|
|
||||||
/* Not all data could be added, send what we have now
|
|
||||||
* and allocate new stuff to be sent.
|
|
||||||
*/
|
|
||||||
ret = http_send_flush(ctx, user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (payload_len);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (ctx->pending) {
|
|
||||||
net_pkt_unref(ctx->pending);
|
|
||||||
ctx->pending = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_send_flush(struct http_ctx *ctx, void *user_send_data)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!ctx->pending) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_send_msg_raw(ctx, ctx->pending, user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->pending = NULL;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_send_chunk(struct http_ctx *ctx, const char *buf, size_t len,
|
|
||||||
const struct sockaddr *dst, void *user_send_data)
|
|
||||||
{
|
|
||||||
char chunk_header[16];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!buf) {
|
|
||||||
len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintk(chunk_header, sizeof(chunk_header), "%x" HTTP_CRLF,
|
|
||||||
(unsigned int)len);
|
|
||||||
|
|
||||||
ret = http_prepare_and_send(ctx, chunk_header, strlen(chunk_header),
|
|
||||||
dst, user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len) {
|
|
||||||
ret = http_prepare_and_send(ctx, buf, len, dst, user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_prepare_and_send(ctx, HTTP_CRLF, sizeof(HTTP_CRLF) - 1, dst,
|
|
||||||
user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _http_add_header(struct http_ctx *ctx, s32_t timeout,
|
|
||||||
const char *name, const char *value,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_send_data)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = http_prepare_and_send(ctx, name, strlen(name), dst,
|
|
||||||
user_send_data);
|
|
||||||
if (value && ret >= 0) {
|
|
||||||
ret = http_prepare_and_send(ctx, ": ", strlen(": "), dst,
|
|
||||||
user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_prepare_and_send(ctx, value, strlen(value), dst,
|
|
||||||
user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_prepare_and_send(ctx, HTTP_CRLF, strlen(HTTP_CRLF),
|
|
||||||
dst, user_send_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_add_header(struct http_ctx *ctx, const char *field,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_send_data)
|
|
||||||
{
|
|
||||||
return _http_add_header(ctx, ctx->timeout, field, NULL, dst,
|
|
||||||
user_send_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_add_header_field(struct http_ctx *ctx, const char *name,
|
|
||||||
const char *value,
|
|
||||||
const struct sockaddr *dst,
|
|
||||||
void *user_send_data)
|
|
||||||
{
|
|
||||||
return _http_add_header(ctx, ctx->timeout, name, value, dst,
|
|
||||||
user_send_data);
|
|
||||||
}
|
|
|
@ -1,739 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 Intel Corporation.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <logging/log.h>
|
|
||||||
LOG_MODULE_DECLARE(net_http, CONFIG_HTTP_LOG_LEVEL);
|
|
||||||
|
|
||||||
#include <zephyr.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <version.h>
|
|
||||||
|
|
||||||
#include <net/net_core.h>
|
|
||||||
#include <net/net_ip.h>
|
|
||||||
#include <net/http.h>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
|
|
||||||
(void)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;
|
|
||||||
|
|
||||||
(void)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, NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, " ", NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, req->url, NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, req->protocol, NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, HTTP_CRLF, NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req->host) {
|
|
||||||
ret = http_add_header_field(ctx, HTTP_HOST, req->host,
|
|
||||||
NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req->header_fields) {
|
|
||||||
ret = http_add_header(ctx, req->header_fields, NULL, 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,
|
|
||||||
NULL, 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,
|
|
||||||
NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_add_header(ctx, HTTP_CRLF, NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_prepare_and_send(ctx, req->payload,
|
|
||||||
req->payload_size,
|
|
||||||
NULL, user_data);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = http_add_header(ctx, HTTP_EOF, NULL, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void print_info(struct http_ctx *ctx,
|
|
||||||
enum http_method method)
|
|
||||||
{
|
|
||||||
if (CONFIG_HTTP_LOG_LEVEL >= LOG_LEVEL_INF) {
|
|
||||||
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),
|
|
||||||
log_strdup(ctx->http.req.host), log_strdup(local),
|
|
||||||
log_strdup(remote),
|
|
||||||
ntohs(net_sin(&ctx->app_ctx.default_ctx->remote)->
|
|
||||||
sin_port));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (CONFIG_HTTP_LOG_LEVEL >= LOG_LEVEL_INF) {
|
|
||||||
#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, log_strdup(output));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
log_strdup(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ctx->http.req.method == HTTP_PUT ||
|
|
||||||
ctx->http.req.method == HTTP_POST)
|
|
||||||
&& 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 (CONFIG_HTTP_LOG_LEVEL >= LOG_LEVEL_INF) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = 0U;
|
|
||||||
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 = 0U;
|
|
||||||
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 && app_ctx->default_ctx) {
|
|
||||||
ctx->cb.connect(ctx, HTTP_CONNECTION,
|
|
||||||
&app_ctx->default_ctx->remote,
|
|
||||||
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;
|
|
||||||
|
|
||||||
(void)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 */
|
|
File diff suppressed because it is too large
Load diff
|
@ -210,14 +210,6 @@ CONFIG_MQTT_LIB=y
|
||||||
CONFIG_MQTT_KEEPALIVE=60
|
CONFIG_MQTT_KEEPALIVE=60
|
||||||
CONFIG_MQTT_LIB_TLS=y
|
CONFIG_MQTT_LIB_TLS=y
|
||||||
|
|
||||||
# HTTP
|
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_HTTP_SERVER=y
|
|
||||||
CONFIG_HTTP_CLIENT=y
|
|
||||||
CONFIG_HTTP_PARSER=y
|
|
||||||
CONFIG_HTTP_PARSER_STRICT=y
|
|
||||||
CONFIG_HTTP_LOG_LEVEL_DBG=y
|
|
||||||
|
|
||||||
# VLAN
|
# VLAN
|
||||||
CONFIG_NET_VLAN=y
|
CONFIG_NET_VLAN=y
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ CONFIG_ENTROPY_GENERATOR=y
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
CONFIG_ZTEST_STACKSIZE=1024
|
CONFIG_ZTEST_STACKSIZE=1024
|
||||||
CONFIG_HTTP_PARSER=y
|
CONFIG_HTTP_PARSER=y
|
||||||
CONFIG_HTTP=y
|
|
||||||
CONFIG_ZTEST=y
|
CONFIG_ZTEST=y
|
||||||
CONFIG_MAIN_STACK_SIZE=1280
|
CONFIG_MAIN_STACK_SIZE=1280
|
||||||
# Enable strict parser by uncommenting the following line
|
# Enable strict parser by uncommenting the following line
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue