net: http: Add HTTP server library support

This commit creates a HTTP server library. So instead of creating
a complex HTTP server application for serving HTTP requests, the
developer can use the HTTP server API to create HTTP server
insteances. This commit also adds support for creating HTTPS servers.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2017-05-08 14:27:29 +03:00 committed by Anas Nashif
commit a174d2eba7
21 changed files with 2339 additions and 1673 deletions

View file

@ -7,13 +7,42 @@
#ifndef __HTTP_H__
#define __HTTP_H__
#if defined(CONFIG_HTTPS)
#if defined(CONFIG_MBEDTLS)
#if !defined(CONFIG_MBEDTLS_CFG_FILE)
#include "mbedtls/config.h"
#else
#include CONFIG_MBEDTLS_CFG_FILE
#endif /* CONFIG_MBEDTLS_CFG_FILE */
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#include <stdlib.h>
#define mbedtls_time_t time_t
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif /* MBEDTLS_PLATFORM_C */
#include <mbedtls/ssl_cookie.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/ssl.h>
#include <mbedtls/error.h>
#include <mbedtls/debug.h>
#endif /* CONFIG_MBEDTLS */
#endif /* CONFIG_HTTPS */
#define HTTP_CRLF "\r\n"
#if defined(CONFIG_HTTP_CLIENT)
#include <net/http_parser.h>
#include <net/net_context.h>
#define HTTP_CRLF "\r\n"
/* Is there more data to come */
enum http_final_call {
HTTP_DATA_MORE = 0,
@ -395,20 +424,17 @@ int http_client_init(struct http_client_ctx *http_ctx,
* @param http_ctx HTTP context.
*/
void http_client_release(struct http_client_ctx *http_ctx);
#endif
#endif /* CONFIG_HTTP_CLIENT */
#if defined(CONFIG_HTTP_SERVER)
#include <net/net_context.h>
#if defined(CONFIG_HTTP_PARSER)
#include <net/http_parser.h>
#endif
/* HTTP server context state */
enum HTTP_CTX_STATE {
HTTP_CTX_FREE = 0,
HTTP_CTX_IN_USE
struct http_server_ctx;
enum http_url_flags {
HTTP_URL_STANDARD = 0,
};
/* HTTP header fields struct */
@ -417,54 +443,441 @@ struct http_field_value {
* containing the HTTP field name
*/
const char *key;
/** Length of the field name */
u16_t key_len;
/** Value, this variable will point to the beginning of the string
* containing the field value
*/
const char *value;
/** Length of the field name */
u16_t key_len;
/** Length of the field value */
u16_t value_len;
};
typedef int (*http_url_cb_t)(struct http_server_ctx *ctx);
/* HTTP root URL struct, used for pattern matching */
struct http_root_url {
/** URL */
const char *root;
/** Callback that is called when this URL is received */
http_url_cb_t write_cb;
/** Length of the URL */
u16_t root_len;
/** Flags for this URL (values are from enum http_url_flags) */
u8_t flags;
/** Is this URL resource used or not */
u8_t is_used;
};
/* Collection of URLs that this server will handle */
struct http_server_urls {
/* First item is the default handler and it is always there.
*/
struct http_root_url default_url;
struct http_root_url urls[CONFIG_HTTP_SERVER_NUM_URLS];
};
#if defined(CONFIG_HTTPS)
/* Internal information for managing HTTPS data */
struct https_context {
struct net_pkt *rx_pkt;
struct net_buf *frag;
struct k_sem tx_sem;
struct k_fifo rx_fifo;
int remaining;
};
/**
* @typedef https_server_cert_cb_t
* @brief Callback used when the API user needs to setup the
* HTTPS certs.
*
* @param ctx HTTPS server context.
* @param cert MBEDTLS certificate
* @param pkey MBEDTLS private key
*
* @return 0 if ok, <0 if there is an error
*/
typedef int (*https_server_cert_cb_t)(struct http_server_ctx *ctx,
mbedtls_x509_crt *cert,
mbedtls_pk_context *pkey);
/**
* @typedef https_entropy_src_cb_t
* @brief Callback used when the API user needs to setup the entropy source.
* @detail This is the same as mbedtls_entropy_f_source_ptr callback.
*
* @param data Callback-specific data pointer
* @param output Data to fill
* @param len Maximum size to provide
* @param olen The actual amount of bytes put into the buffer (Can be 0)
*/
typedef int (*https_entropy_src_cb_t)(void *data, unsigned char *output,
size_t len, size_t *olen);
#endif /* CONFIG_HTTPS */
typedef int (*http_send_data_t)(struct net_pkt *pkt,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data);
/* The HTTP server context struct */
struct http_server_ctx {
u8_t state;
/** Collection of URLs that this server context will handle */
struct http_server_urls *urls;
/** Collection of header fields */
struct http_field_value field_values[CONFIG_HTTP_HEADER_FIELD_ITEMS];
/** Number of header field elements */
u16_t field_values_ctr;
#if defined(CONFIG_NET_IPV4)
/** IPv4 stack network context for listening */
struct net_context *net_ipv4_ctx;
#endif
#if defined(CONFIG_NET_IPV6)
/** IPv6 stack network context for listening */
struct net_context *net_ipv6_ctx;
#endif
/** HTTP Request URL */
const char *url;
/** URL's length */
u16_t url_len;
/** Function that is called when data is received from network. */
net_context_recv_cb_t recv_cb;
/**IP stack network context */
struct net_context *net_ctx;
/** Function that is called when data is sent to network. */
http_send_data_t send_data;
/** Network timeout */
s32_t timeout;
#if defined(CONFIG_HTTP_PARSER)
/** HTTP parser */
struct http_parser parser;
/** HTTP parser settings */
struct http_parser_settings parser_settings;
#endif
/** Running status of the server. If true, then the server is enabled.
* If false then it is disabled and will not serve clients.
* The server is disabled by default after initialization and will need
* to be enabled manually.
*/
bool enabled;
/** Is this instance HTTPS or not.
*/
bool is_https;
struct {
/** From which net_context the request came from */
struct net_context *net_ctx;
/** HTTP parser */
struct http_parser parser;
/** HTTP parser settings */
struct http_parser_settings settings;
/** Collection of header fields */
struct http_field_value
field_values[CONFIG_HTTP_HEADER_FIELD_ITEMS];
/** HTTP Request URL */
const char *url;
/** Where the request is stored, this is to be provided
* by the user.
*/
u8_t *request_buf;
/** Request buffer maximum length */
size_t request_buf_len;
/** Length of the data in the request buf. */
size_t data_len;
/** Number of header field elements */
u16_t field_values_ctr;
/** URL's length */
u16_t url_len;
} req;
#if defined(CONFIG_HTTPS)
struct {
/** HTTPS stack for mbedtls library. */
u8_t *stack;
/** HTTPS stack size. */
int stack_size;
/** HTTPS thread id */
k_tid_t tid;
/** HTTPS thread */
struct k_thread thread;
/** Memory pool for RX data */
struct k_mem_pool *pool;
/** mbedtls related configuration. */
struct {
struct https_context ssl_ctx;
https_server_cert_cb_t cert_cb;
https_entropy_src_cb_t entropy_src_cb;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt srvcert;
mbedtls_pk_context pkey;
u8_t *personalization_data;
size_t personalization_data_len;
} mbedtls;
} https;
#endif /* CONFIG_HTTPS */
};
/**
* @brief Initialize user supplied HTTP context.
*
* @detail Caller can set the various callback fields in http_ctx and
* http_ctx.req.parser after this call if needed.
*
* @param http_ctx HTTP context.
* @param urls Array of URLs that the server instance will serve. If the
* server receives a HTTP request into one of the URLs, it will call user
* supplied callback. If no such URL is registered, a default handler will
* be called (if set by the user). If no data handler is found, the request
* is dropped.
* @param server_addr Socket address of the local network interface and TCP
* port where the data is being waited. If the socket family is set to
* AF_UNSPEC, then both IPv4 and IPv6 is started to be listened. If the
* address is set to be INADDR_ANY (for IPv4) or unspecified address (all bits
* zeros for IPv6), then the HTTP server will select proper IP address to bind
* to. If caller has not specified HTTP listening port, then port 80 is being
* listened. The parameter can be left NULL in which case a listener to port 80
* using IPv4 and IPv6 is created. Note that if IPv4 or IPv6 is disabled, then
* the corresponding disabled service listener is not created.
* @param request_buf Caller supplied buffer where the HTTP request will be
* stored
* @param request_buf_len Length of the caller suppied buffer.
* @param server_banner Print information about started service. This is only
* printed if HTTP debugging is activated. The parameter can be set to NULL if
* no extra prints are needed.
*
* @return Return 0 if ok, <0 if error.
*/
int http_server_init(struct http_server_ctx *http_ctx,
struct http_server_urls *urls,
struct sockaddr *server_addr,
u8_t *request_buf,
size_t request_buf_len,
const char *server_banner);
#if defined(CONFIG_HTTPS)
/**
* @brief Initialize user supplied HTTP context. This function must be
* used if HTTPS server is created.
*
* @detail Caller can set the various callback fields in http_ctx and
* http_ctx.req.parser after this call if needed.
*
* @param http_ctx HTTP context.
* @param urls Array of URLs that the server instance will serve. If the
* server receives a HTTP request into one of the URLs, it will call user
* supplied callback. If no such URL is registered, a default handler will
* be called (if set by the user). If no data handler is found, the request
* is dropped.
* @param server_addr Socket address of the local network interface and TCP
* port where the data is being waited. If the socket family is set to
* AF_UNSPEC, then both IPv4 and IPv6 is started to be listened. If the
* address is set to be INADDR_ANY (for IPv4) or unspecified address (all bits
* zeros for IPv6), then the HTTP server will select proper IP address to bind
* to. If caller has not specified HTTP listening port, then port 80 is being
* listened. The parameter can be left NULL in which case a listener to port 80
* using IPv4 and IPv6 is created. Note that if IPv4 or IPv6 is disabled, then
* the corresponding disabled service listener is not created.
* @param request_buf Caller supplied buffer where the HTTP request will be
* stored
* @param request_buf_len Length of the caller suppied buffer.
* @param server_banner Print information about started service. This is only
* printed if HTTP debugging is activated. The parameter can be set to NULL if
* no extra prints are needed.
* @param personalization_data Personalization data (Device specific
* identifiers) for random number generator. (Can be NULL).
* @param personalization_data_len Length of the personalization data.
* @param cert_cb User supplied callback that setups the certifacates.
* @param entropy_src_cb User supplied callback that setup the entropy. This
* can be set to NULL, in which case default entropy source is used.
* @param pool Memory pool for RX data reads.
* @param https_stack HTTPS thread stack.
* @param https_stack_len HTTP thread stack size.
*
* @return Return 0 if ok, <0 if error.
*/
int https_server_init(struct http_server_ctx *http_ctx,
struct http_server_urls *urls,
struct sockaddr *server_addr,
u8_t *request_buf,
size_t request_buf_len,
const char *server_banner,
u8_t *personalization_data,
size_t personalization_data_len,
https_server_cert_cb_t cert_cb,
https_entropy_src_cb_t entropy_src_cb,
struct k_mem_pool *pool,
u8_t *https_stack,
size_t https_stack_len);
#endif /* CONFIG_HTTPS */
/**
* @brief Release all the resources allocated for HTTP context.
*
* @param http_ctx HTTP context.
*/
void http_server_release(struct http_server_ctx *http_ctx);
/**
* @brief Enable HTTP server that is related to this context.
*
* @detail The HTTP server will start to serve request after this.
*
* @param http_ctx HTTP context.
*
* @return Previous status of the server.
*/
bool http_server_enable(struct http_server_ctx *http_ctx);
/**
* @brief Disable HTTP server that is related to this context.
*
* @detail The HTTP server will stop to serve request after this.
*
* @param http_ctx HTTP context.
*
* @return Previous status of the server.
*/
bool http_server_disable(struct http_server_ctx *http_ctx);
/**
* @brief Helper function that can be used to fill the server (local) sockaddr
* struct.
*
* @param addr Sockaddr struct to be filled.
* @param myaddr Address that the local IP address. If left NULL, then the
* proper system address is used.
* @param port TCP port to use.
*
* @return 0 if ok, <0 if error.
*/
int http_server_set_local_addr(struct sockaddr *addr, const char *myaddr,
u16_t port);
/**
* @brief Add a handler for a given URL.
*
* @detail Register a handler which is called if the server receives a
* request to a given URL.
*
* @param urls URL struct that will contain all the URLs the user wants to
* register.
* @param url URL string.
* @param flags Flags for the URL.
* @param write_cb Callback that is called when URL is requested.
*
* @return NULL if the URL is already registered, pointer to URL if
* registering was ok.
*/
struct http_root_url *http_server_add_url(struct http_server_urls *urls,
const char *url, u8_t flags,
http_url_cb_t write_cb);
/**
* @brief Delete the handler for a given URL.
*
* @detail Unregister the previously registered URL handler.
*
* @param urls URL struct that will contain all the URLs the user has
* registered.
* @param url URL string.
*
* @return 0 if ok, <0 if error.
*/
int http_server_del_url(struct http_server_urls *urls, const char *url);
/**
* @brief Add default URL handler.
*
* @detail If no URL handler is found, then call this handler. There can
* be only one default handler in the URL struct.
*
* @param urls URL struct that will contain all the URLs the user has
* registered.
* @param write_cb Callback that is called when non-registered URL is
* requested.
*
* @return NULL if default URL is already registered, pointer to default
* URL if registering was ok.
*/
struct http_root_url *http_server_add_default(struct http_server_urls *urls,
http_url_cb_t write_cb);
/**
* @brief Delete the default URL handler.
*
* @detail Unregister the previously registered default URL handler.
*
* @param urls URL struct that will contain all the URLs the user has
* registered.
*
* @return 0 if ok, <0 if error.
*/
int http_server_del_default(struct http_server_urls *urls);
/**
* @brief Send HTTP response to client.
*
* @param ctx HTTP context.
* @param http_header HTTP headers to send.
* @param html_payload HTML payload to send.
*
* @return 0 if ok, <0 if error.
*/
int http_response(struct http_server_ctx *ctx, const char *http_header,
const char *html_payload);
/**
* @brief Send HTTP 400 response to client.
*
* @detail HTTP 400 error code indicates a Bad Request
*
* @param ctx HTTP context.
* @param html_payload HTML payload to send.
*
* @return 0 if ok, <0 if error.
*/
int http_response_400(struct http_server_ctx *ctx, const char *html_payload);
/**
* @brief Send HTTP 403 response to client.
*
* @detail HTTP 403 error code indicates a Forbidden Request
*
* @param ctx HTTP context.
* @param html_payload HTML payload to send.
*
* @return 0 if ok, <0 if error.
*/
int http_response_403(struct http_server_ctx *ctx, const char *html_payload);
/**
* @brief Send HTTP 404 response to client.
*
* @detail HTTP 404 error code indicates a Not Found resource.
*
* @param ctx HTTP context.
* @param html_payload HTML payload to send.
*
* @return 0 if ok, <0 if error.
*/
int http_response_404(struct http_server_ctx *ctx, const char *html_payload);
#endif
#endif /* CONFIG_HTTP_SERVER */
#endif
#endif /* __HTTP_H__ */

View file

@ -4,10 +4,8 @@
# SPDX-License-Identifier: Apache-2.0
#
BOARD ?= frdm_k64f
BOARD ?= qemu_x86
CONF_FILE ?= prj_$(BOARD).conf
include $(ZEPHYR_BASE)/Makefile.inc
ifeq ($(BOARD), qemu_x86)
include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack
endif
include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack

View file

@ -7,14 +7,13 @@ Overview
********
The HTTP Server sample application for Zephyr implements a basic TCP server
on top of the HTTP Parser Library that is able to receive HTTP 1.1 requests,
on top of the HTTP Server Library that is able to receive HTTP 1.1 requests,
parse them and write back the responses.
This sample code generates HTTP 1.1 responses dynamically
and does not serve content from a file system. The source code includes
examples on how to write HTTP 1.1 responses: 200 OK, 400 Bad Request,
403 Forbidden, 404 Not Found and soft HTTP errors like 200 OK with a 404
Not Found HTML message.
This sample code generates HTTP 1.1 responses dynamically and does not serve
content from a file system. The source code includes examples on how to write
HTTP 1.1 responses: 200 OK, 400 Bad Request, 403 Forbidden, 404 Not Found and
soft HTTP errors like 200 OK with a 404 Not Found HTML message.
The source code for this sample application can be found at:
:file:`samples/net/http_server`.
@ -23,39 +22,24 @@ Requirements
************
- Linux machine with wget and the screen terminal emulator
- Freedom Board (FRDM-K64F)
- Either QEMU or real device like Freedom Board (FRDM-K64F)
- LAN for testing purposes (Ethernet)
Building and Running
********************
Currently, the HTTP Server application is configured to listen at port 80,
This value is defined in the :file:`samples/net/http_server/src/config.h`
file:
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.
.. code-block:: c
#define ZEPHYR_PORT 80
Open the project configuration file for your platform, for example the
:file:`prj_frdm_k64f.conf` file is the configuration file for the
:ref:`frdm_k64f` board. 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.
This sample code only supports static IP addresses that are defined in the
project configuration file:
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_APP_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101"
CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
Adding URLs
===========
@ -66,9 +50,11 @@ following lines:
.. code-block:: c
http_url_default_handler(http_write_soft_404_not_found);
http_url_add("/headers", HTTP_URL_STANDARD, http_write_header_fields);
http_url_add("/index.html", HTTP_URL_STANDARD, http_write_it_works);
http_server_add_default(&http_urls, http_response_soft_404);
http_server_add_url(&http_urls, "/headers", HTTP_URL_STANDARD,
http_response_header_fields);
http_server_add_url(&http_urls, "/index.html", HTTP_URL_STANDARD,
http_response_it_works);
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
@ -114,19 +100,20 @@ serial console under other operating systems.
Sample Output
=============
Assume that this HTTP server is configured to listen at 192.168.1.101 port 80.
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.101/index.html
wget 192.168.1.120/index.html
wget will show:
.. code-block:: console
--2017-01-17 00:37:44-- http://192.168.1.101/
Connecting to 192.168.1.101:80... connected.
--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'
@ -146,32 +133,38 @@ The screen application will display the following information:
.. code-block:: console
[dev/eth_mcux] [INF] eth_0_init: Enabled 100M full-duplex mode.
[dev/eth_mcux] [DBG] eth_0_init: MAC 00:04:9f:c9:29:6e
Zephyr HTTP Server
Address: 192.168.1.101, port: 80
----------------------------------------------------
[print_client_banner:42] Connection accepted
Address: 192.168.1.10, port: 54327
[http_ctx_get:268] Free ctx found, index: 0
[http_write:59] net_pkt_get_tx, rc: 0 <OK>
[http_write:82] net_context_send: 0 <OK>
[http_rx_tx:86] Connection closed by peer
[dev/eth_mcux] [DBG] eth_0_init: MAC 00:04:9f:52:44:02
[sample/net] [INF] net_sample_app_init: Run HTTPS server
[sample/net] [INF] setup_dhcpv4: Running
[dev/eth_mcux] [DBG] eth_0_init: MAC 00:04:9f:f1:80:9e
[sample/net] [INF] net_sample_app_init: Run HTTPS server
[sample/net] [INF] setup_dhcpv4: Running dhcpv4 client...
[sample/net] [INF] ipv6_event_handler: IPv6 address: 2001:db8::1
[dev/eth_mcux] [INF] eth_mcux_phy_event: Enabled 10M half-duplex mode.
[sample/net] [INF] ipv4_addr_add_handler: IPv4 address: 192.168.1.120
[sample/net] [INF] ipv4_addr_add_handler: Lease time: 86400 seconds
[sample/net] [INF] ipv4_addr_add_handler: Subnet: 255.255.255.0
[sample/net] [INF] ipv4_addr_add_handler: Router: 192.168.1.1
[https/server] [INF] new_server: Zephyr HTTPS Server (0x20002370)
[https/server] [DBG] https_handler: (0x2000b5b4): HTTPS handler starting
[https/server] [INF] new_server: Zephyr HTTP Server (0x20001840)
[https/server] [INF] new_client: HTTP connection from 192.168.1.107:44410 (0x20006af4)
[https/server] [DBG] http_recv: (0x2000d6b4): Received 336 bytes data
[https/server] [DBG] http_process_recv: (0x2000d6b4): Calling handler 0x00000d89 context 0x20001840
To obtain the HTTP Header Fields web page, use the following command:
.. code-block:: console
wget 192.168.1.101/headers -O index.html
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.101/headers
Connecting to 192.168.1.101:80... connected.
--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'
@ -190,7 +183,7 @@ This is the HTML file generated by Zephyr and downloaded by wget:
<ul>
<li>User-Agent: Wget/1.16 (linux-gnu)</li>
<li>Accept: */*</li>
<li>Host: 192.168.1.101</li>
<li>Host: 192.168.1.120</li>
<li>Connection: Keep-Alive</li>
</ul>
<h2>HTTP Method: GET</h2>
@ -203,7 +196,7 @@ To test the 404 Not Found soft error, use the following command:
.. code-block:: console
wget 192.168.1.101/not_found -O index.html
wget 192.168.1.120/not_found -O index.html
Zephyr will generate an HTTP response with the following header:
@ -224,32 +217,11 @@ and this is the HTML message that wget will save:
<body><h1><center>404 Not Found</center></h1></body>
</html>
To test the HTTP Basic Authentication functionality, use the
following command:
.. code-block:: console
wget 192.168.1.101/auth -O index.html --user=zephyr --password=0123456789
the :file:`index.html` file will contain the following information:
.. code-block:: html
<html>
<head>
<title>Zephyr HTTP Server</title>
</head>
<body>
<h1><center>Zephyr HTTP server</center></h1>
<h2><center>user: zephyr, password: 0123456789</center></h2>
</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.
running side by side with the HTTP server, this server runs on QEMU.
In order to compile and run the code execute:
.. code-block:: console
@ -266,25 +238,23 @@ The app will show the following on the screen:
.. code-block:: console
Zephyr HTTP Server
Address: 192.0.2.1, port: 80
Zephyr HTTPS Server
Address: 192.0.2.1, port: 443
failed
! mbedtls_ssl_handshake returned -29312
[https/server] [INF] new_client: HTTPS connection from 192.168.1.107:35982 (0x20006b4c)
[https/server] [DBG] https_handler: (0x2000b5b4): Read HTTPS request
[https/server] [DBG] https_handler: (0x2000b5b4): Write HTTPS response
[https/server] [DBG] http_process_recv: (0x2000b5b4): Calling handler 0x00000ce9 context 0x20002370
Now execute the following command on a different terminal window
.. code-block:: console
wget https://192.0.2.1 --no-check-certificate
wget https://192.168.1.120 --no-check-certificate
This will be shown on the screen
.. code-block:: console
Connecting to 192.0.2.1:443... connected.
WARNING: cannot verify 192.0.2.1's certificate
Connecting to 192.168.1.120:443... connected.
WARNING: cannot verify 192.168.1.120's certificate
Unable to locally verify the issuer's authority.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
@ -304,7 +274,4 @@ Known Issues and Limitations
- Currently, this sample application only generates HTTP responses in
chunk transfer mode.
- Clients must close the connection to allow the HTTP server to release
the network context and accept another connection.
- The use of mbedTLS and IPv6 takes more than the available ram for the
emulation platform, so only IPv4 works for now in QEMU.
- Basic authentication is not yet implemented.

View file

@ -11,26 +11,38 @@ CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_IPV6_RA_RDNSS=y
CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_HTTP_SERVER=y
CONFIG_HTTP_PARSER=y
CONFIG_SYS_LOG_SHOW_COLOR=y
CONFIG_SYS_LOG_NET_LEVEL=4
CONFIG_NET_DEBUG_HTTP=y
CONFIG_HTTP_SERVER=y
# Enable IPv6 support
CONFIG_NET_IPV6=y
# Enable IPv4 support
CONFIG_NET_IPV4=n
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_NET_APP_SETTINGS=y
CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101"
CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_NET_SHELL=y
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y
# ENC28J60 Ethernet Device
CONFIG_ETH_ENC28J60=y
@ -42,4 +54,3 @@ CONFIG_ETH_ENC28J60_0_MAC5=0x36
# Arduino 101
CONFIG_SPI=y

View file

@ -1,7 +1,6 @@
CONFIG_NETWORKING=y
CONFIG_NET_TCP=y
CONFIG_RANDOM_GENERATOR=y
CONFIG_NET_ARP=y
CONFIG_NET_LOG=y
CONFIG_INIT_STACKS=y
@ -10,24 +9,32 @@ CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_IPV6_RA_RDNSS=y
CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
CONFIG_STDOUT_CONSOLE=y
CONFIG_HTTP_SERVER=y
CONFIG_HTTP_PARSER=y
CONFIG_SYS_LOG_SHOW_COLOR=y
CONFIG_SYS_LOG_NET_LEVEL=4
CONFIG_NET_DEBUG_HTTP=y
CONFIG_HTTP_SERVER=y
# Enable IPv6 support
CONFIG_NET_IPV6=y
# Enable IPv4 support
CONFIG_NET_IPV4=n
CONFIG_HTTPS=y
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
CONFIG_NET_APP_SETTINGS=y
CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101"
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_NET_SHELL=y
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_DEBUG_LOG=y
@ -40,7 +47,7 @@ CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_NET_L2_BLUETOOTH=y
CONFIG_NET_L2_BLUETOOTH_ZEP1656=y
CONFIG_NET_DEBUG_L2_BLUETOOTH=y
CONFIG_SYS_LOG_SHOW_COLOR=y
CONFIG_NET_STATISTICS=y
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2
# This sample is meant to be compilable in QEMU only
CONFIG_RAM_SIZE=300

View file

@ -11,21 +11,33 @@ CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_IPV6_RA_RDNSS=y
CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_STDOUT_CONSOLE=y
CONFIG_HTTP_SERVER=y
CONFIG_HTTP_PARSER=y
CONFIG_SYS_LOG_SHOW_COLOR=y
CONFIG_SYS_LOG_NET_LEVEL=4
CONFIG_NET_DEBUG_HTTP=y
# Enable IPv6 support
CONFIG_NET_IPV6=n
# Enable IPv4 support
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_NET_APP_SETTINGS=y
CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101"
CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_NET_SHELL=y
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y

View file

@ -8,29 +8,35 @@ CONFIG_INIT_STACKS=y
CONFIG_NET_PKT_RX_COUNT=16
CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=8
CONFIG_NET_BUF_TX_COUNT=8
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_IPV6_RA_RDNSS=y
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=2
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
CONFIG_STDOUT_CONSOLE=y
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_SYS_LOG_SHOW_COLOR=y
CONFIG_SYS_LOG_NET_LEVEL=2
CONFIG_NET_DEBUG_HTTP=y
CONFIG_HTTP_SERVER=y
CONFIG_HTTP_PARSER=y
# Enable IPv6 support
CONFIG_NET_IPV6=n
# Enable IPv4 support
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_NET_APP_SETTINGS=y
CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_MAX_CONTEXTS=16
CONFIG_NET_SHELL=y
CONFIG_NET_STATISTICS=y
#CONFIG_MBEDTLS=y
#CONFIG_MBEDTLS_BUILTIN=y
#CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y

View file

@ -4,15 +4,6 @@
# SPDX-License-Identifier: Apache-2.0
#
obj-y += main.o
obj-y += http_utils.o
obj-y += http_server.o
obj-y += http_write_utils.o
ifdef CONFIG_MBEDTLS
obj-y += https_server.o ssl_utils.o
endif
include $(ZEPHYR_BASE)/samples/net/common/Makefile.common
ifeq ($(CONFIG_NET_L2_BLUETOOTH), y)
ccflags-y +=-I${ZEPHYR_BASE}/samples/bluetooth/
obj-y += ../../../bluetooth/gatt/ipss.o
endif
obj-y += main.o

View file

@ -7,35 +7,27 @@
#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_APP_SETTINGS
#ifdef CONFIG_NET_IPV6
#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV6_ADDR
#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV6_ADDR
#else
#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV4_ADDR
#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV4_ADDR
#endif
#else
#ifdef CONFIG_NET_IPV6
#define ZEPHYR_ADDR "2001:db8::1"
#define ZEPHYR_ADDR "2001:db8::1"
#else
#define ZEPHYR_ADDR "192.168.1.101"
#define ZEPHYR_ADDR "192.0.2.1"
#endif
#endif
#define ZEPHYR_PORT 80
#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"
#define HTTP_AUTH_CREDENTIALS "emVwaHlyOjAxMjM0NTY3ODk="
#define APP_SLEEP_MSECS 500
#ifdef CONFIG_MBEDTLS
#define SERVER_PORT 443
#ifndef ZEPHYR_PORT
#define ZEPHYR_PORT 8080
#endif
#endif

View file

@ -14,57 +14,10 @@
#include <net/net_pkt.h>
#include <stdio.h>
#define URL_DEFAULT_HANDLER_INDEX 0
#define HTTP_BUF_CTR HTTP_MAX_NUMBER_SERVER_CTX
#define HTTP_BUF_SIZE 1024
NET_BUF_POOL_DEFINE(http_msg_pool, HTTP_BUF_CTR, HTTP_BUF_SIZE, 0, NULL);
void http_accept_cb(struct net_context *net_ctx, struct sockaddr *addr,
socklen_t addr_len, int status, void *data)
{
struct http_server_ctx *http_ctx = NULL;
ARG_UNUSED(addr_len);
ARG_UNUSED(data);
if (status != 0) {
net_context_put(net_ctx);
return;
}
print_client_banner(addr);
http_ctx = http_ctx_get();
if (!http_ctx) {
net_context_put(net_ctx);
return;
}
http_ctx_set(http_ctx, net_ctx);
net_context_recv(net_ctx, http_rx_tx, K_NO_WAIT, http_ctx);
}
/**
* @brief http_ctx_release Releases an HTTP context
* @return 0, future versions may return error codes
*/
static
int http_ctx_release(struct http_server_ctx *http_ctx);
/**
* @brief parser_init Initializes some parser-related fields at the
* http_server_ctx structure
* @param ctx HTTP server context
* @param net_ctx Network context
* @return 0 on success
* @return -EINVAL on error
*/
static
int parser_init(struct http_server_ctx *ctx);
/**
* @brief parser_parse_request Parses an HTTP REQUEST
* @param ctx HTTP server context
@ -82,359 +35,17 @@ static int http_url_cmp(const char *url, u16_t url_len,
static void http_tx(struct http_server_ctx *http_ctx);
void http_rx_tx(struct net_context *net_ctx, struct net_pkt *rx, int status,
void *user_data)
{
struct http_server_ctx *http_ctx = NULL;
struct net_buf *data = NULL;
u16_t rcv_len;
u16_t offset;
int parsed_len;
int rc;
if (status) {
printf("[%s:%d] Status code: %d, <%s>\n",
__func__, __LINE__, status, RC_STR(status));
goto lb_exit;
}
if (!user_data) {
printf("[%s:%d] User data is null\n", __func__, __LINE__);
goto lb_exit;
}
http_ctx = (struct http_server_ctx *)user_data;
if (http_ctx->net_ctx != net_ctx) {
printf("[%s:%d] Wrong network context received\n",
__func__, __LINE__);
goto lb_exit;
}
if (!rx) {
printf("[%s:%d] Connection closed by peer\n",
__func__, __LINE__);
goto lb_exit;
}
rcv_len = net_pkt_appdatalen(rx);
if (rcv_len == 0) {
/* don't print info about zero-length app data buffers */
goto lb_exit;
}
data = net_buf_alloc(&http_msg_pool, APP_SLEEP_MSECS);
if (data == NULL) {
printf("[%s:%d] Data buffer alloc error\n", __func__, __LINE__);
goto lb_exit;
}
offset = net_pkt_get_len(rx) - rcv_len;
rc = net_frag_linear_copy(data, rx->frags, offset, rcv_len);
if (rc != 0) {
printf("[%s:%d] Linear copy error\n", __func__, __LINE__);
goto lb_exit;
}
data->data[min(data->size - 1, rcv_len)] = 0;
parser_init(http_ctx);
parsed_len = parser_parse_request(http_ctx, data);
if (parsed_len <= 0) {
printf("[%s:%d] Received: %u bytes, only parsed: %d bytes\n",
__func__, __LINE__, rcv_len, parsed_len);
}
if (http_ctx->parser.http_errno != HPE_OK) {
http_response_400(http_ctx, NULL);
} else {
http_tx(http_ctx);
}
lb_exit:
net_pkt_frag_unref(data);
net_pkt_unref(rx);
http_ctx_release(http_ctx);
}
/**
* @brief on_header_field HTTP Parser callback for header fields
* @param parser HTTP Parser
* @param at Points to where the field begins
* @param length Field's length
* @return 0 (always)
*/
static
int on_header_field(struct http_parser *parser, const char *at, size_t length)
{
struct http_server_ctx *ctx = (struct http_server_ctx *)parser->data;
if (ctx->field_values_ctr >= HTTP_PARSER_MAX_FIELD_VALUES) {
return 0;
}
ctx->field_values[ctx->field_values_ctr].key = at;
ctx->field_values[ctx->field_values_ctr].key_len = length;
return 0;
}
/**
* @brief on_header_value HTTP Parser callback for header values
* @param parser HTTP Parser
* @param at Points to where the value begins
* @param length Value's length
* @return 0 (always)
*/
static
int on_header_value(struct http_parser *parser, const char *at, size_t length)
{
struct http_server_ctx *ctx = (struct http_server_ctx *)parser->data;
if (ctx->field_values_ctr >= HTTP_PARSER_MAX_FIELD_VALUES) {
return 0;
}
ctx->field_values[ctx->field_values_ctr].value = at;
ctx->field_values[ctx->field_values_ctr].value_len = length;
ctx->field_values_ctr++;
return 0;
}
/**
* @brief on_url HTTP Parser callback for URLs
* @param parser HTTP Parser
* @param at Points to where the value begins
* @param length Value's length
* @return 0 (always)
*/
static
int on_url(struct http_parser *parser, const char *at, size_t length)
{
struct http_server_ctx *ctx = (struct http_server_ctx *)parser->data;
ctx->url = at;
ctx->url_len = length;
return 0;
}
static
int parser_init(struct http_server_ctx *ctx)
{
memset(ctx->field_values, 0x00, sizeof(ctx->field_values));
ctx->parser_settings.on_header_field = on_header_field;
ctx->parser_settings.on_header_value = on_header_value;
ctx->parser_settings.on_url = on_url;
http_parser_init(&ctx->parser, HTTP_REQUEST);
/* This circular reference is useful when some parser callbacks
* want to access some internal data structures
*/
ctx->parser.data = ctx;
return 0;
}
static
int parser_parse_request(struct http_server_ctx *ctx, struct net_buf *rx)
{
int rc;
ctx->field_values_ctr = 0;
rc = http_parser_execute(&ctx->parser, &ctx->parser_settings,
rx->data, rx->len);
if (rc < 0) {
printf("[%s:%d] http_parser_execute: %s\n\t%s\n",
__func__, __LINE__,
http_errno_name(ctx->parser.http_errno),
http_errno_description(ctx->parser.http_errno));
rc = -EINVAL;
goto exit_routine;
}
exit_routine:
return rc;
}
/**
* @brief server_collection This is a collection of server ctx structs
*/
static struct http_server_ctx server_collection[HTTP_MAX_NUMBER_SERVER_CTX];
static struct http_server_ctx server_ctx[CONFIG_HTTP_SERVER_CONNECTIONS];
/**
* @brief http_url_ctx Just one URL context per application
*/
static struct http_url_ctx url_ctx;
int http_ctx_init(void)
{
memset(server_collection, 0x00, sizeof(server_collection));
memset(&url_ctx, 0x00, sizeof(url_ctx));
/* 0 is reserved for the default handler */
url_ctx.urls_ctr = 1;
return 0;
}
struct http_server_ctx *http_ctx_get(void)
{
int i;
for (i = 0; i < HTTP_MAX_NUMBER_SERVER_CTX; i++) {
if (server_collection[i].state == HTTP_CTX_FREE) {
printf("[%s:%d] Free ctx found, index: %d\n",
__func__, __LINE__, i);
memset(server_collection + i, 0x00,
sizeof(struct http_server_ctx));
return server_collection + i;
}
}
return NULL;
}
int http_ctx_set(struct http_server_ctx *http_ctx, struct net_context *net_ctx)
{
if (http_ctx == NULL || net_ctx == NULL) {
return -EINVAL;
}
http_ctx->state = HTTP_CTX_IN_USE;
http_ctx->net_ctx = net_ctx;
return 0;
}
static int http_ctx_release(struct http_server_ctx *http_ctx)
{
if (http_ctx == NULL) {
return 0;
}
http_ctx->state = HTTP_CTX_FREE;
return 0;
}
int http_url_default_handler(int (*write_cb)(struct http_server_ctx *))
{
if (write_cb == NULL) {
return -EINVAL;
}
url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].flags = 0x00;
url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].root = NULL;
url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].root_len = 0;
url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].write_cb = write_cb;
return 0;
}
int http_url_add(const char *url, u8_t flags,
int (*write_cb)(struct http_server_ctx *http_ctx))
{
struct http_root_url *root = NULL;
if (url_ctx.urls_ctr >= HTTP_MAX_NUMBER_URL) {
return -ENOMEM;
}
root = &url_ctx.urls[url_ctx.urls_ctr];
root->root = url;
/* this will speed-up some future operations */
root->root_len = strlen(url);
root->flags = flags;
root->write_cb = write_cb;
url_ctx.urls_ctr++;
return 0;
}
static int http_url_cmp(const char *url, u16_t url_len,
const char *root_url, u16_t root_url_len)
{
if (url_len < root_url_len) {
return -EINVAL;
}
if (memcmp(url, root_url, root_url_len) == 0) {
if (url_len == root_url_len) {
return 0;
}
/* Here we evlaute the following conditions:
* root_url = /images, url = /images/ -> OK
* root_url = /images/, url = /images/img.png -> OK
* root_url = /images/, url = /images_and_docs -> ERROR
*/
if (url_len > root_url_len) {
if (root_url[root_url_len - 1] == '/') {
return 0;
}
if (url[root_url_len] == '/') {
return 0;
}
}
}
return -EINVAL;
}
static struct http_root_url *http_url_find(struct http_server_ctx *http_ctx)
{
u16_t url_len = http_ctx->url_len;
const char *url = http_ctx->url;
struct http_root_url *root_url;
u8_t i;
int rc;
/* at some point we must come up with something better */
for (i = 1; i < url_ctx.urls_ctr; i++) {
root_url = &url_ctx.urls[i];
rc = http_url_cmp(url, url_len,
root_url->root, root_url->root_len);
if (rc == 0) {
return root_url;
}
}
return NULL;
}
static void http_tx(struct http_server_ctx *http_ctx)
{
struct http_root_url *root_url;
root_url = http_url_find(http_ctx);
if (!root_url) {
root_url = &url_ctx.urls[URL_DEFAULT_HANDLER_INDEX];
}
if (root_url->write_cb) {
root_url->write_cb(http_ctx);
} else {
printf("[%s:%d] No default handler for %.*s\n",
__func__, __LINE__,
http_ctx->url_len, http_ctx->url);
}
}
int http_auth(struct http_server_ctx *ctx)
{
const char *auth_str = "Authorization: Basic "HTTP_AUTH_CREDENTIALS;

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _HTTP_TYPES_H_
#define _HTTP_TYPES_H_
#include <net/net_context.h>
#include <net/http.h>
/* Max number of HTTP header field-value pairs */
#define HTTP_PARSER_MAX_FIELD_VALUES CONFIG_HTTP_HEADER_FIELD_ITEMS
/* Max number of HTTP server context available to this app */
#define HTTP_MAX_NUMBER_SERVER_CTX CONFIG_NET_MAX_CONTEXTS
/* Max number of URLs this server will handle */
#define HTTP_MAX_NUMBER_URL 16
enum HTTP_URL_FLAGS {
HTTP_URL_STANDARD = 0
};
/**
* @brief http_root_url HTTP root URL struct, used for pattern matching
*/
struct http_root_url {
const char *root;
u16_t root_len;
u8_t flags;
int (*write_cb)(struct http_server_ctx *http_ctx);
};
/**
* @brief http_url_ctx Collection of URLs that this server will handle
*/
struct http_url_ctx {
struct http_root_url urls[HTTP_MAX_NUMBER_URL];
u8_t urls_ctr;
};
#endif

View file

@ -1,51 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "http_utils.h"
#include <stdio.h>
#include "config.h"
#define IP6_SIZE 16
#define IP4_SIZE 4
#define STR_IP6_ADDR 64
#define STR_IP4_ADDR 16
static
void print_ipaddr(const struct sockaddr *ip_addr)
{
#ifdef CONFIG_NET_IPV6
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ip_addr;
void *raw = (void *)&addr->sin6_addr;
u16_t port = addr->sin6_port;
sa_family_t family = AF_INET6;
char str[STR_IP6_ADDR];
#else
struct sockaddr_in *addr = (struct sockaddr_in *)ip_addr;
void *raw = (void *)&addr->sin_addr;
u16_t port = addr->sin_port;
sa_family_t family = AF_INET;
char str[STR_IP4_ADDR];
#endif
net_addr_ntop(family, raw, str, sizeof(str));
printf("Address: %s, port: %d\n", str, ntohs(port));
}
void print_client_banner(const struct sockaddr *addr)
{
printf("\n----------------------------------------------------\n");
printf("[%s:%d] Connection accepted\n", __func__, __LINE__);
print_ipaddr(addr);
}
void print_server_banner(const struct sockaddr *addr)
{
printf("Zephyr HTTP Server\n");
print_ipaddr(addr);
}

View file

@ -1,18 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __HTTP_UTILS_H__
#define __HTTP_UTILS_H__
#include <net/net_ip.h>
#define RC_STR(rc) (rc == 0 ? "OK" : "ERROR")
void print_client_banner(const struct sockaddr *addr);
void print_server_banner(const struct sockaddr *addr);
#endif

View file

@ -11,100 +11,11 @@
#include <stdio.h>
#define HTTP_STATUS_200_OK "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Transfer-Encoding: chunked\r\n" \
"\r\n"
#define HTTP_401_STATUS_US "HTTP/1.1 401 Unauthorized status\r\n" \
"WWW-Authenticate: Basic realm=" \
"\""HTTP_AUTH_REALM"\"\r\n\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"
/* Prints the received HTTP header fields as an HTML list */
static void print_http_headers(struct http_server_ctx *ctx,
char *str, u16_t size)
{
struct http_parser *parser = &ctx->parser;
u16_t offset = 0;
offset = snprintf(str, size,
HTML_HEADER
"<h2>HTTP Header Fields</h2>\r\n<ul>\r\n");
if (offset >= size) {
return;
}
for (u8_t i = 0; i < ctx->field_values_ctr; i++) {
struct http_field_value *kv = &ctx->field_values[i];
offset += snprintf(str + offset, size - offset,
"<li>%.*s: %.*s</li>\r\n",
kv->key_len, kv->key,
kv->value_len, kv->value);
if (offset >= size) {
return;
}
}
offset += snprintf(str + offset, size - offset, "</ul>\r\n");
if (offset >= size) {
return;
}
offset += snprintf(str + offset, size - offset,
"<h2>HTTP Method: %s</h2>\r\n",
http_method_str(parser->method));
if (offset >= size) {
return;
}
offset += snprintf(str + offset, size - offset,
"<h2>URL: %.*s</h2>\r\n",
ctx->url_len, ctx->url);
if (offset >= size) {
return;
}
snprintf(str + offset, size - offset,
"<h2>Server: %s</h2>"HTML_FOOTER, CONFIG_ARCH);
}
#define HTTP_MAX_BODY_STR_SIZE 512
static char html_body[HTTP_MAX_BODY_STR_SIZE];
int http_response_header_fields(struct http_server_ctx *ctx)
{
print_http_headers(ctx, html_body, HTTP_MAX_BODY_STR_SIZE);
return http_response(ctx, HTTP_STATUS_200_OK, html_body);
}
int http_response_it_works(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_STATUS_200_OK, HTML_HEADER
"<body><h2><center>It Works!</center></h2>"
HTML_FOOTER);
}
int http_response_401(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_401_STATUS_US, NULL);
}
int http_response_soft_404(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_STATUS_200_OK, HTML_HEADER
"<h2><center>404 Not Found</center></h2>"
HTML_FOOTER);
}
int http_response_auth(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_STATUS_200_OK,

View file

@ -1,407 +0,0 @@
/* Minimal TLS server.
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#include <zephyr.h>
#include <stdio.h>
#include <errno.h>
#include <misc/printk.h>
#if !defined(CONFIG_MBEDTLS_CFG_FILE)
#include "mbedtls/config.h"
#else
#include CONFIG_MBEDTLS_CFG_FILE
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#include <stdlib.h>
#define mbedtls_time_t time_t
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif
#include <string.h>
#include <net/net_context.h>
#include <net/net_if.h>
#include "config.h"
#include "ssl_utils.h"
#include "test_certs.h"
#include "mbedtls/ssl_cookie.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/x509.h"
#include "mbedtls/ssl.h"
#include "mbedtls/error.h"
#include "mbedtls/debug.h"
#if defined(MBEDTLS_DEBUG_C)
#include "mbedtls/debug.h"
#define DEBUG_THRESHOLD 0
#endif
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
#include "mbedtls/memory_buffer_alloc.h"
static unsigned char heap[12000];
#endif
/*
* Hardcoded values for server host and port
*/
static const char *pers = "tls_server";
#if defined(CONFIG_NET_IPV6)
static struct in6_addr server_addr;
#else
static struct in_addr server_addr;
#endif
struct parsed_url {
const char *url;
u16_t url_len;
};
#define HTTP_RESPONSE \
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" \
"<h2>Zephyr TLS Test Server</h2>\r\n" \
"<p>Successful connection</p>\r\n"
#define HTTP_NOT_FOUND \
"HTTP/1.0 404 NOT FOUND\r\nContent-Type: text/html\r\n\r\n" \
"<h2>Zephyr TLS page not found </h2>\r\n" \
"<p>Successful connection</p>\r\n"
static void my_debug(void *ctx, int level,
const char *file, int line, const char *str)
{
const char *p, *basename;
ARG_UNUSED(ctx);
/* Extract basename from file */
for (p = basename = file; *p != '\0'; p++) {
if (*p == '/' || *p == '\\') {
basename = p + 1;
}
}
mbedtls_printf("%s:%04d: |%d| %s", basename, line, level, str);
}
static int entropy_source(void *data, unsigned char *output, size_t len,
size_t *olen)
{
u32_t seed;
ARG_UNUSED(data);
seed = sys_rand32_get();
if (len > sizeof(seed)) {
len = sizeof(seed);
}
memcpy(output, &seed, len);
*olen = len;
return 0;
}
static
int on_url(struct http_parser *parser, const char *at, size_t length)
{
struct parsed_url *req = (struct parsed_url *)parser->data;
req->url = at;
req->url_len = length;
return 0;
}
static unsigned char payload[256];
void https_server(void)
{
struct ssl_context ctx;
struct parsed_url request;
int ret, len = 0;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt srvcert;
mbedtls_pk_context pkey;
mbedtls_platform_set_printf(printk);
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
mbedtls_memory_buffer_alloc_init(heap, sizeof(heap));
#endif
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold(DEBUG_THRESHOLD);
mbedtls_ssl_conf_dbg(&conf, my_debug, NULL);
#endif
mbedtls_x509_crt_init(&srvcert);
mbedtls_pk_init(&pkey);
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
/*
* 1. Load the certificates and private RSA key
*/
ret = mbedtls_x509_crt_parse(&srvcert, rsa_example_cert_der,
rsa_example_cert_der_len);
if (ret != 0) {
mbedtls_printf(" failed\n !"
" mbedtls_x509_crt_parse returned %d\n\n", ret);
goto exit;
}
ret = mbedtls_pk_parse_key(&pkey, rsa_example_keypair_der,
rsa_example_keypair_der_len, NULL, 0);
if (ret != 0) {
mbedtls_printf(" failed\n !"
" mbedtls_pk_parse_key returned %d\n\n", ret);
goto exit;
}
/*
* 3. Seed the RNG
*/
mbedtls_entropy_add_source(&entropy, entropy_source, NULL,
MBEDTLS_ENTROPY_MAX_GATHER,
MBEDTLS_ENTROPY_SOURCE_STRONG);
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)pers, strlen(pers));
if (ret != 0) {
mbedtls_printf(" failed\n !"
" mbedtls_ctr_drbg_seed returned %d\n", ret);
goto exit;
}
/*
* 4. Setup stuff
*/
ret = mbedtls_ssl_config_defaults(&conf,
MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (ret != 0) {
mbedtls_printf(" failed\n !"
" mbedtls_ssl_config_defaults returned %d\n\n",
ret);
goto exit;
}
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_ssl_conf_dbg(&conf, my_debug, NULL);
mbedtls_ssl_conf_ca_chain(&conf, srvcert.next, NULL);
ret = mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey);
if (ret != 0) {
mbedtls_printf(" failed\n !"
" mbedtls_ssl_conf_own_cert returned %d\n\n",
ret);
goto exit;
}
ret = mbedtls_ssl_setup(&ssl, &conf);
if (ret != 0) {
mbedtls_printf(" failed\n !"
" mbedtls_ssl_setup returned %d\n\n", ret);
goto exit;
}
/*
* 3. Wait until a client connects
*/
ret = ssl_init(&ctx, &server_addr);
if (ret != 0) {
mbedtls_printf(" failed\n ! ssl_init returned %d\n\n", ret);
goto exit;
}
/*
* 4. Prepare http parser
*/
http_parser_init(&ctx.parser, HTTP_REQUEST);
http_parser_settings_init(&ctx.parser_settings);
ctx.parser.data = &request;
ctx.parser_settings.on_url = on_url;
mbedtls_printf("Zephyr HTTPS Server\n");
mbedtls_printf("Address: %s, port: %d\n", ZEPHYR_ADDR, SERVER_PORT);
reset:
mbedtls_ssl_session_reset(&ssl);
mbedtls_ssl_set_bio(&ssl, &ctx, ssl_tx, ssl_rx, NULL);
/*
* 5. Handshake
*/
do {
ret = mbedtls_ssl_handshake(&ssl);
if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
if (ret < 0) {
mbedtls_printf(" failed\n !"
" mbedtls_ssl_handshake returned %d\n\n",
ret);
goto reset;
}
}
} while (ret != 0);
/*
* 6. Read the HTTPS Request
*/
mbedtls_printf("Read HTTPS request\n");
do {
len = sizeof(payload) - 1;
memset(payload, 0, sizeof(payload));
ret = mbedtls_ssl_read(&ssl, payload, len);
if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
continue;
}
if (ret <= 0) {
switch (ret) {
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
mbedtls_printf(" connection was"
" closed gracefully\n");
goto close;
case MBEDTLS_ERR_NET_CONN_RESET:
mbedtls_printf(" connection was"
" reset by peer\n");
break;
default:
mbedtls_printf
(" mbedtls_ssl_read returned -0x%x\n",
-ret);
break;
}
break;
}
len = ret;
ret = http_parser_execute(&ctx.parser, &ctx.parser_settings,
payload, len);
if (ret < 0) {
}
} while (ret < 0);
/*
* 7. Write the Response
*/
mbedtls_printf("Write HTTPS response\n");
if (!strncmp("/index.html", request.url, request.url_len)) {
len = snprintf((char *)payload, sizeof(payload),
HTTP_RESPONSE);
} else {
len = snprintf((char *)payload, sizeof(payload),
HTTP_NOT_FOUND);
}
do {
ret = mbedtls_ssl_write(&ssl, payload, len);
if (ret == MBEDTLS_ERR_NET_CONN_RESET) {
mbedtls_printf(" failed\n !"
" peer closed the connection\n");
goto reset;
}
if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
if (ret < 0) {
mbedtls_printf(" failed\n !"
" mbedtls_ssl_write"
" returned %d\n", ret);
goto exit;
}
}
} while (ret <= 0);
close:
mbedtls_ssl_close_notify(&ssl);
ret = 0;
goto reset;
exit:
#ifdef MBEDTLS_ERROR_C
if (ret != 0) {
mbedtls_strerror(ret, payload, 100);
mbedtls_printf("Last error was: %d - %s\n", ret, payload);
}
#endif
mbedtls_ssl_free(&ssl);
mbedtls_ssl_config_free(&conf);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
}
#define STACK_SIZE 8192
u8_t stack[STACK_SIZE];
struct k_thread https_thread;
static inline int init_app(void)
{
#if defined(CONFIG_NET_IPV6)
if (net_addr_pton(AF_INET6, ZEPHYR_ADDR, &server_addr) < 0) {
mbedtls_printf("Invalid IPv6 address %s", ZEPHYR_ADDR);
}
if (!net_if_ipv6_addr_add(net_if_get_default(), &server_addr,
NET_ADDR_MANUAL, 0)) {
return -EIO;
}
#else
if (net_addr_pton(AF_INET, ZEPHYR_ADDR, &server_addr) < 0) {
mbedtls_printf("Invalid IPv4 address %s", ZEPHYR_ADDR);
}
if (!net_if_ipv4_addr_add(net_if_get_default(), &server_addr,
NET_ADDR_MANUAL, 0)) {
return -EIO;
}
#endif
return 0;
}
void https_server_start(void)
{
if (init_app() != 0) {
printk("Cannot initialize network\n");
return;
}
k_thread_create(&https_thread, stack, STACK_SIZE,
(k_thread_entry_t) https_server,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, 0);
}

View file

@ -4,144 +4,305 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <net/net_context.h>
#include <misc/printk.h>
#if defined(CONFIG_NET_L2_BLUETOOTH)
#include <bluetooth/bluetooth.h>
#include <gatt/ipss.h>
#if 1
#if defined(CONFIG_HTTPS)
#define SYS_LOG_DOMAIN "https-server"
#else
#define SYS_LOG_DOMAIN "http-server"
#endif
#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#define NET_LOG_ENABLED 1
#endif
#include "http_types.h"
#include "http_server.h"
#include "http_utils.h"
#include "http_write_utils.h"
#include <zephyr.h>
#include <stdio.h>
#include <net/net_context.h>
#include <net/http.h>
#include <net_sample_app.h>
#include "config.h"
/* Sets the network parameters */
static
int network_setup(struct net_context **net_ctx, net_tcp_accept_cb_t accept_cb,
const char *addr, u16_t port);
#if defined(CONFIG_MBEDTLS)
#include "ssl_utils.h"
#endif
#define RESULT_BUF_SIZE 1024
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_HTTPS_STACK_SIZE,
CONFIG_HTTPS_STACK_SIZE);
#define RX_FIFO_DEPTH 4
K_MEM_POOL_DEFINE(ssl_rx_pool, 4, 64, RX_FIFO_DEPTH, 4);
static struct http_server_ctx https_ctx;
static u8_t https_result[RESULT_BUF_SIZE];
#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_server_ctx http_ctx;
static struct http_server_urls http_urls;
void panic(const char *msg)
{
if (msg) {
NET_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" \
"\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"
/* Prints the received HTTP header fields as an HTML list */
static void print_http_headers(struct http_server_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->req.field_values_ctr; i++) {
struct http_field_value *kv = &ctx->req.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->req.parser.method));
if (ret < 0 || ret >= size) {
return;
}
offset += ret;
ret = snprintf(str + offset, size - offset,
"<h2>URL: %.*s</h2>\r\n",
ctx->req.url_len, ctx->req.url);
if (ret < 0 || ret >= size) {
return;
}
offset += ret;
snprintf(str + offset, size - offset,
"<h2>Server: %s</h2>"HTML_FOOTER, CONFIG_ARCH);
}
int http_response_header_fields(struct http_server_ctx *ctx)
{
#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);
}
static int http_response_it_works(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_STATUS_200_OK, HTML_HEADER
"<body><h2><center>It Works!</center></h2>"
HTML_FOOTER);
}
static int http_response_soft_404(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_STATUS_200_OK, HTML_HEADER
"<h2><center>404 Not Found</center></h2>"
HTML_FOOTER);
}
#if defined(CONFIG_HTTPS)
/* Load the certificates and private RSA key. */
#include "test_certs.h"
static int setup_cert(struct http_server_ctx *ctx,
mbedtls_x509_crt *cert,
mbedtls_pk_context *pkey)
{
int ret;
ret = mbedtls_x509_crt_parse(cert, rsa_example_cert_der,
rsa_example_cert_der_len);
if (ret != 0) {
NET_ERR("mbedtls_x509_crt_parse returned %d", ret);
return ret;
}
ret = mbedtls_pk_parse_key(pkey, rsa_example_keypair_der,
rsa_example_keypair_der_len, NULL, 0);
if (ret != 0) {
NET_ERR("mbedtls_pk_parse_key returned %d", ret);
return ret;
}
return 0;
}
#endif /* CONFIG_HTTPS */
void main(void)
{
#if defined(CONFIG_NET_L2_BLUETOOTH)
int err;
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
ipss_init();
err = ipss_advertise();
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
#endif
struct net_context *net_ctx = NULL;
http_ctx_init();
http_url_default_handler(http_response_soft_404);
http_url_add(HTTP_AUTH_URL, HTTP_URL_STANDARD, http_auth);
http_url_add("/headers", HTTP_URL_STANDARD,
http_response_header_fields);
http_url_add("/index.html", HTTP_URL_STANDARD, http_response_it_works);
network_setup(&net_ctx, http_accept_cb, ZEPHYR_ADDR, ZEPHYR_PORT);
}
static
int network_setup(struct net_context **net_ctx, net_tcp_accept_cb_t accept_cb,
const char *addr, u16_t port)
{
struct sockaddr local_sock;
void *ptr;
int rc;
*net_ctx = NULL;
#ifdef CONFIG_NET_IPV6
net_sin6(&local_sock)->sin6_port = htons(port);
local_sock.family = AF_INET6;
ptr = &(net_sin6(&local_sock)->sin6_addr);
rc = net_addr_pton(AF_INET6, addr, ptr);
#else
net_sin(&local_sock)->sin_port = htons(port);
local_sock.family = AF_INET;
ptr = &(net_sin(&local_sock)->sin_addr);
rc = net_addr_pton(AF_INET, addr, ptr);
#endif
if (rc) {
printk("Invalid IP address/Port: %s, %d\n", addr, port);
return rc;
}
#ifdef CONFIG_NET_IPV6
ptr = net_if_ipv6_addr_add(net_if_get_default(),
&net_sin6(&local_sock)->sin6_addr,
NET_ADDR_MANUAL, 0);
#else
ptr = net_if_ipv4_addr_add(net_if_get_default(),
&net_sin(&local_sock)->sin_addr,
NET_ADDR_MANUAL, 0);
#endif
struct sockaddr addr, *server_addr;
u32_t flags = 0;
int ret;
/*
* If we have both IPv6 and IPv4 enabled, then set the
* startup flags so that we wait until both are ready
* before continuing.
*/
#if defined(CONFIG_NET_IPV6)
rc = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, net_ctx);
flags |= NET_SAMPLE_NEED_IPV6;
#endif
#if defined(CONFIG_NET_IPV4)
flags |= NET_SAMPLE_NEED_IPV4;
#endif
ret = net_sample_app_init(APP_BANNER, flags, APP_STARTUP_TIME);
if (ret < 0) {
panic("Application init failed");
}
/*
* 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 */
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 */
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) {
NET_ERR("Cannot set local address (%d)", ret);
panic(NULL);
}
server_addr = &addr;
#else
rc = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, net_ctx);
server_addr = NULL;
ARG_UNUSED(addr);
#endif
if (rc != 0) {
printk("net_context_get error\n");
return rc;
http_server_add_default(&http_urls, http_response_soft_404);
http_server_add_url(&http_urls, "/headers", HTTP_URL_STANDARD,
http_response_header_fields);
http_server_add_url(&http_urls, "/index.html", HTTP_URL_STANDARD,
http_response_it_works);
#if defined(CONFIG_HTTPS)
ret = https_server_init(&https_ctx, &http_urls, server_addr,
https_result, sizeof(https_result),
"Zephyr HTTPS Server",
INSTANCE_INFO, strlen(INSTANCE_INFO),
setup_cert, NULL, &ssl_rx_pool,
https_stack, sizeof(https_stack));
if (ret < 0) {
NET_ERR("Cannot initialize HTTPS server (%d)", ret);
panic(NULL);
}
rc = net_context_bind(*net_ctx, (const struct sockaddr *)&local_sock,
sizeof(local_sock));
if (rc != 0) {
printk("net_context_bind error\n");
goto lb_error;
}
rc = net_context_listen(*net_ctx, 0);
if (rc != 0) {
printk("net_context_listen error\n");
goto lb_error;
}
rc = net_context_accept(*net_ctx, accept_cb, 0, NULL);
if (rc != 0) {
printk("net_context_accept error\n");
goto lb_error;
}
print_server_banner(&local_sock);
#if defined(CONFIG_MBEDTLS)
https_server_start();
http_server_enable(&https_ctx);
#endif
return 0;
lb_error:
net_context_put(*net_ctx);
*net_ctx = NULL;
ret = http_server_init(&http_ctx, &http_urls, server_addr, http_result,
sizeof(http_result), "Zephyr HTTP Server");
if (ret < 0) {
NET_ERR("Cannot initialize HTTP server (%d)", ret);
panic(NULL);
}
return rc;
/*
* 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

@ -1,289 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <net/net_core.h>
#include <net/net_context.h>
#include <net/net_pkt.h>
#include <net/net_if.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.h>
#if !defined(CONFIG_MBEDTLS_CFG_FILE)
#include "mbedtls/config.h"
#else
#include CONFIG_MBEDTLS_CFG_FILE
#endif
#include "mbedtls/ssl.h"
#include "config.h"
#include "ssl_utils.h"
#define RX_FIFO_DEPTH 4
K_MEM_POOL_DEFINE(rx_pkts, 4, 64, RX_FIFO_DEPTH, 4);
static void ssl_received(struct net_context *context,
struct net_buf *buf, int status, void *user_data)
{
struct ssl_context *ctx = user_data;
struct rx_fifo_block *rx_data = NULL;
struct k_mem_block block;
ARG_UNUSED(context);
ARG_UNUSED(status);
if (!net_pkt_appdatalen(buf)) {
net_pkt_unref(buf);
return;
}
k_mem_pool_alloc(&rx_pkts, &block,
sizeof(struct rx_fifo_block), K_FOREVER);
rx_data = block.data;
rx_data->buf = buf;
/* For freeing memory later */
memcpy(&rx_data->block, &block, sizeof(struct k_mem_block));
k_fifo_put(&ctx->rx_fifo, (void *)rx_data);
}
static inline void ssl_sent(struct net_context *context,
int status, void *token, void *user_data)
{
struct ssl_context *ctx = user_data;
k_sem_give(&ctx->tx_sem);
}
int ssl_tx(void *context, const unsigned char *buf, size_t size)
{
struct ssl_context *ctx = context;
struct net_context *net_ctx;
struct net_buf *send_buf;
int rc, len;
net_ctx = ctx->net_ctx;
send_buf = net_pkt_get_tx(net_ctx, K_NO_WAIT);
if (!send_buf) {
return MBEDTLS_ERR_SSL_ALLOC_FAILED;
}
len = net_pkt_append(send_buf, size, (u8_t *) buf, K_FOREVER);
rc = net_context_send(send_buf, ssl_sent, K_NO_WAIT, NULL, ctx);
if (rc < 0) {
net_pkt_unref(send_buf);
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
}
k_sem_take(&ctx->tx_sem, K_FOREVER);
return len;
}
int ssl_rx(void *context, unsigned char *buf, size_t size)
{
struct ssl_context *ctx = context;
u16_t read_bytes;
struct rx_fifo_block *rx_data;
u8_t *ptr;
int pos;
int len;
int rc = 0;
if (ctx->frag == NULL) {
rx_data = k_fifo_get(&ctx->rx_fifo, K_FOREVER);
ctx->rx_pkt = rx_data->buf;
k_mem_pool_free(&rx_data->block);
read_bytes = net_pkt_appdatalen(ctx->rx_pkt);
ctx->remaining = read_bytes;
ctx->frag = ctx->rx_pkt->frags;
ptr = net_pkt_appdata(ctx->rx_pkt);
len = ptr - ctx->frag->data;
net_buf_pull(ctx->frag, len);
} else {
read_bytes = ctx->remaining;
ptr = ctx->frag->data;
}
len = ctx->frag->len;
pos = 0;
if (read_bytes > size) {
while (ctx->frag) {
read_bytes = len < (size - pos) ? len : (size - pos);
memcpy(buf + pos, ptr, read_bytes);
pos += read_bytes;
if (pos < size) {
ctx->frag = ctx->frag->frags;
ptr = ctx->frag->data;
len = ctx->frag->len;
} else {
if (read_bytes == len) {
ctx->frag = ctx->frag->frags;
} else {
net_buf_pull(ctx->frag, read_bytes);
}
ctx->remaining -= size;
return size;
}
}
} else {
while (ctx->frag) {
memcpy(buf + pos, ptr, len);
pos += len;
ctx->frag = ctx->frag->frags;
if (!ctx->frag) {
break;
}
ptr = ctx->frag->data;
len = ctx->frag->len;
}
net_pkt_unref(ctx->rx_pkt);
ctx->rx_pkt = NULL;
ctx->frag = NULL;
ctx->remaining = 0;
if (read_bytes != pos) {
return -EIO;
}
rc = read_bytes;
}
return rc;
}
static void ssl_accepted(struct net_context *context,
struct sockaddr *addr,
socklen_t addrlen, int error, void *user_data)
{
int ret;
struct ssl_context *ctx = user_data;
ctx->net_ctx = context;
ret = net_context_recv(context, ssl_received, 0, user_data);
if (ret < 0) {
printk("Cannot receive TCP packet (family %d)",
net_context_get_family(context));
}
}
#if defined(CONFIG_NET_IPV6)
int ssl_init(struct ssl_context *ctx, void *addr)
{
struct net_context *tcp_ctx = { 0 };
struct sockaddr_in6 my_addr = { 0 };
struct in6_addr *server_addr = addr;
int rc;
k_sem_init(&ctx->tx_sem, 0, UINT_MAX);
k_fifo_init(&ctx->rx_fifo);
my_mcast_addr.sin6_family = AF_INET6;
net_ipaddr_copy(&my_addr.sin6_addr, server_addr);
my_addr.sin6_family = AF_INET6;
my_addr.sin6_port = htons(SERVER_PORT);
rc = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, &tcp_ctx);
if (rc < 0) {
printk("Cannot get network context for IPv6 TCP (%d)", rc);
return -EIO;
}
rc = net_context_bind(tcp_ctx, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr_in6));
if (rc < 0) {
printk("Cannot bind IPv6 TCP port %d (%d)", SERVER_PORT, rc);
goto error;
}
ctx->rx_pkt = NULL;
ctx->remaining = 0;
ctx->net_ctx = tcp_ctx;
rc = net_context_listen(ctx->net_ctx, 0);
if (rc < 0) {
printk("Cannot listen IPv6 TCP (%d)", rc);
return -EIO;
}
rc = net_context_accept(ctx->net_ctx, ssl_accepted, 0, ctx);
if (rc < 0) {
printk("Cannot accept IPv4 (%d)", rc);
return -EIO;
}
return 0;
error:
net_context_put(tcp_ctx);
return -EINVAL;
}
#else
int ssl_init(struct ssl_context *ctx, void *addr)
{
struct net_context *tcp_ctx = { 0 };
struct sockaddr_in my_addr4 = { 0 };
struct in_addr *server_addr = addr;
int rc;
k_sem_init(&ctx->tx_sem, 0, UINT_MAX);
k_fifo_init(&ctx->rx_fifo);
net_ipaddr_copy(&my_addr4.sin_addr, server_addr);
my_addr4.sin_family = AF_INET;
my_addr4.sin_port = htons(SERVER_PORT);
rc = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, &tcp_ctx);
if (rc < 0) {
printk("Cannot get network context for IPv4 TCP (%d)", rc);
return -EIO;
}
rc = net_context_bind(tcp_ctx, (struct sockaddr *)&my_addr4,
sizeof(struct sockaddr_in));
if (rc < 0) {
printk("Cannot bind IPv4 TCP port %d (%d)", SERVER_PORT, rc);
goto error;
}
ctx->rx_pkt = NULL;
ctx->remaining = 0;
ctx->net_ctx = tcp_ctx;
rc = net_context_listen(ctx->net_ctx, 0);
if (rc < 0) {
printk("Cannot listen IPv4 (%d)", rc);
return -EIO;
}
rc = net_context_accept(ctx->net_ctx, ssl_accepted, 0, ctx);
if (rc < 0) {
printk("Cannot accept IPv4 (%d)", rc);
return -EIO;
}
return 0;
error:
net_context_put(tcp_ctx);
return -EINVAL;
}
#endif

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SSL_UTILS_H_
#define _SSL_UTILS_H_
#include <net/net_core.h>
#include <net/http_parser.h>
struct rx_fifo_block {
sys_snode_t snode;
struct k_mem_block block;
struct net_buf *buf;
};
struct ssl_context {
struct net_context *net_ctx;
struct net_buf *rx_pkt;
struct net_buf *frag;
struct k_sem tx_sem;
struct k_fifo rx_fifo;
struct http_parser_settings parser_settings;
struct http_parser parser;
int remaining;
};
int ssl_init(struct ssl_context *ctx, void *addr);
int ssl_tx(void *ctx, const unsigned char *buf, size_t size);
int ssl_rx(void *ctx, unsigned char *buf, size_t size);
void https_server_start(void);
#endif

View file

@ -1,10 +1,10 @@
[test]
tags = net http
build_only = true
platform_whitelist = frdm_k64f qemu_x86 arduino_101
platform_whitelist = frdm_k64f qemu_x86
[test_bt]
tags = net bluetooth
build_only = true
extra_args = CONF_FILE="prj_bt.conf"
platform_whitelist = arduino_101
platform_whitelist = qemu_x86

View file

@ -13,9 +13,25 @@ config HTTP_SERVER
bool "HTTP server support"
default n
select HTTP
select HTTP_PARSER
help
Enables HTTP server routines
config HTTP_SERVER_CONNECTIONS
int "Max number of HTTP server connections"
default NET_MAX_CONTEXTS
depends on HTTP_SERVER
help
This value determines how many simultaneous HTTP connections the
HTTP server can serve.
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_HEADER_FIELD_ITEMS
int "HTTP header field max number of items"
depends on HTTP_SERVER
@ -48,6 +64,33 @@ config HTTP_PARSER_STRICT
help
This option enables the strict parsing option
config HTTPS
bool "HTTPS support"
default n
depends on HTTP
depends on MBEDTLS
help
Enables HTTPS support.
config HTTPS_STACK_SIZE
int "HTTPS thread stack size"
default 8192
depends on HTTPS
help
HTTPS thread stack size. The mbedtls routines will use this stack
thus it is by default very large.
config HTTPS_HEAP_SIZE
int "HTTPS heap size for mbedtls"
default 12000
depends on HTTPS
depends on MBEDTLS
help
HTTPS heap size. The mbedtls routines will use this heap if enabled.
See ext/lib/crypto/mbedtls/include/mbedtls/config.h and
MBEDTLS_MEMORY_BUFFER_ALLOC_C option for details. This option is not
enabled by default.
config NET_DEBUG_HTTP
bool "Debug HTTP"
default n

File diff suppressed because it is too large Load diff