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:
Jukka Rissanen 2019-01-24 12:50:14 +02:00 committed by Anas Nashif
commit 4043909d69
31 changed files with 0 additions and 5166 deletions

View file

@ -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
*************** ***************

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -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
)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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!");
}

View file

@ -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()

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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);
}

View file

@ -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)

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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