summaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
authorFlavio Santes <flavio.santes@intel.com>2017-01-13 00:43:23 -0600
committerJukka Rissanen <jukka.rissanen@linux.intel.com>2017-02-03 15:59:20 +0200
commit53e9245b33c4c08f1b1c062fb871024f0869e530 (patch)
treece64f040444067eb6024a0345b1aef0d7a0672c1 /samples
parent6930f93fd3d2debfa556800c7e44e20f5d458d66 (diff)
samples/net/http: Add the HTTP server sample application
Add the HTTP server sample application on top of the HTTP Parser Library. This sample application is based on TCP and HTTP chunk transfer code found at: https://gerrit.zephyrproject.org/r/#/c/9977/ A README file with sample output and a detailed description of this application is also provided. Jira: ZEP-820 Jira: ZEP-1542 Jira: ZEP-1556 Change-Id: I649104a256190577000bbac118136d5bc21f83bf Signed-off-by: Flavio Santes <flavio.santes@intel.com>
Diffstat (limited to 'samples')
-rw-r--r--samples/net/http_server/Makefile13
-rw-r--r--samples/net/http_server/README.rst232
-rw-r--r--samples/net/http_server/prj_frdm_k64f.conf29
-rw-r--r--samples/net/http_server/src/Makefile10
-rw-r--r--samples/net/http_server/src/config.h28
-rw-r--r--samples/net/http_server/src/http_server.c409
-rw-r--r--samples/net/http_server/src/http_server.h51
-rw-r--r--samples/net/http_server/src/http_types.h98
-rw-r--r--samples/net/http_server/src/http_utils.c51
-rw-r--r--samples/net/http_server/src/http_utils.h18
-rw-r--r--samples/net/http_server/src/http_write_utils.c183
-rw-r--r--samples/net/http_server/src/http_write_utils.h63
-rw-r--r--samples/net/http_server/src/main.c142
-rw-r--r--samples/net/http_server/testcase.ini4
14 files changed, 1331 insertions, 0 deletions
diff --git a/samples/net/http_server/Makefile b/samples/net/http_server/Makefile
new file mode 100644
index 000000000..304240745
--- /dev/null
+++ b/samples/net/http_server/Makefile
@@ -0,0 +1,13 @@
+#
+# Copyright (c) 2017 Intel Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+BOARD ?= frdm_k64f
+CONF_FILE ?= prj_$(BOARD).conf
+
+include $(ZEPHYR_BASE)/Makefile.inc
+ifeq ($(BOARD), qemu_x86)
+ include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack
+endif
diff --git a/samples/net/http_server/README.rst b/samples/net/http_server/README.rst
new file mode 100644
index 000000000..27b77829c
--- /dev/null
+++ b/samples/net/http_server/README.rst
@@ -0,0 +1,232 @@
+HTTP Server
+###########
+
+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,
+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.
+
+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
+- 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:
+
+.. 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:
+
+.. code-block:: console
+
+ CONFIG_NET_SAMPLES_MY_IPV6_ADDR="2001:db8::1"
+ CONFIG_NET_SAMPLES_MY_IPV4_ADDR="192.168.1.101"
+
+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_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);
+
+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.
+
+The second line must be interpreted as follows: requests to /headers,
+/headers/index.html and in general to /headers/xxx, will trigger the
+http_write_header_fields routine that prints the received HTTP
+Header Fields. In this case, "xxx" must be understood as any resource
+under the /headers/ URL.
+
+The third line will trigger a routine that prints an HTML It Works!
+message when the /index.html or /index.html/xxx URLs are found.
+
+To build this sample on your Linux host computer, open a terminal window,
+locate the source code of this sample application and type:
+
+.. code-block:: console
+
+ make BOARD=frdm_k64f
+
+The FRDM K64F board is detected as a USB storage device. The board
+must be mounted (i.e. to /mnt) to 'flash' the binary:
+
+.. code-block:: console
+
+ $ cp outdir/frdm_k64f/zephyr.bin /mnt
+
+On Linux, use the 'dmesg' program to find the right USB device for the
+FRDM serial console. Assuming that this device is ttyACM0, open a
+terminal window and type:
+
+.. code-block:: console
+
+ $ screen /dev/ttyACM0 115200
+
+Once the binary is loaded into the FRDM board, press the RESET button.
+
+Refer to the board documentation in Zephyr, :ref:`frdm_k64f`,
+for more information about this board and how to access the FRDM
+serial console under other operating systems.
+
+
+Sample Output
+=============
+
+Assume that this HTTP server is configured to listen at 192.168.1.101 port 80.
+On your Linux host computer, open a terminal window and type:
+
+.. code-block:: console
+
+ wget 192.168.1.101/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.
+ 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
+
+ [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_nbuf_get_tx, rc: 0 <OK>
+ [http_write:82] net_context_send: 0 <OK>
+ [http_rx_tx:86] Connection closed by peer
+
+
+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 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.
+ 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.101</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.101/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>
+
+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.
diff --git a/samples/net/http_server/prj_frdm_k64f.conf b/samples/net/http_server/prj_frdm_k64f.conf
new file mode 100644
index 000000000..460320094
--- /dev/null
+++ b/samples/net/http_server/prj_frdm_k64f.conf
@@ -0,0 +1,29 @@
+CONFIG_NETWORKING=y
+CONFIG_NET_TCP=y
+CONFIG_RANDOM_GENERATOR=y
+CONFIG_NET_ARP=y
+CONFIG_NET_L2_ETHERNET=y
+CONFIG_NET_LOG=y
+CONFIG_INIT_STACKS=y
+
+CONFIG_NET_NBUF_RX_COUNT=16
+CONFIG_NET_NBUF_TX_COUNT=16
+CONFIG_NET_NBUF_DATA_COUNT=16
+
+CONFIG_NET_IPV6_RA_RDNSS=y
+CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3
+
+CONFIG_STDOUT_CONSOLE=y
+
+CONFIG_HTTP_PARSER=y
+
+# Enable IPv6 support
+CONFIG_NET_IPV6=n
+# Enable IPv4 support
+CONFIG_NET_IPV4=y
+
+CONFIG_NET_SAMPLES_IP_ADDRESSES=y
+CONFIG_NET_SAMPLES_MY_IPV6_ADDR="2001:db8::1"
+CONFIG_NET_SAMPLES_MY_IPV4_ADDR="192.168.1.101"
+
+CONFIG_NET_MAX_CONTEXTS=16
diff --git a/samples/net/http_server/src/Makefile b/samples/net/http_server/src/Makefile
new file mode 100644
index 000000000..11d9be117
--- /dev/null
+++ b/samples/net/http_server/src/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2017 Intel Corporation
+#
+# 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
diff --git a/samples/net/http_server/src/config.h b/samples/net/http_server/src/config.h
new file mode 100644
index 000000000..672b66363
--- /dev/null
+++ b/samples/net/http_server/src/config.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#ifdef CONFIG_NET_SAMPLES_IP_ADDRESSES
+#ifdef CONFIG_NET_IPV6
+#define ZEPHYR_ADDR CONFIG_NET_SAMPLES_MY_IPV6_ADDR
+#else
+#define ZEPHYR_ADDR CONFIG_NET_SAMPLES_MY_IPV4_ADDR
+#endif
+#else
+#ifdef CONFIG_NET_IPV6
+#define ZEPHYR_ADDR "2001:db8::1"
+#else
+#define ZEPHYR_ADDR "192.168.1.101"
+#endif
+#endif
+
+#define ZEPHYR_PORT 80
+
+#define APP_SLEEP_MSECS 500
+
+#endif
diff --git a/samples/net/http_server/src/http_server.c b/samples/net/http_server/src/http_server.c
new file mode 100644
index 000000000..3355a4ce0
--- /dev/null
+++ b/samples/net/http_server/src/http_server.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "http_types.h"
+#include "http_server.h"
+#include "http_utils.h"
+#include "http_write_utils.h"
+#include "config.h"
+
+#include <net/http_parser.h>
+#include <net/nbuf.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);
+
+/**
+ * @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
+ * @param rx Input buffer
+ * @return 0 on success
+ * @return -EINVAL on error
+ */
+static
+int parser_parse_request(struct http_server_ctx *ctx, struct net_buf *rx);
+
+static struct http_root_url *http_url_find(struct http_server_ctx *http_ctx);
+
+static int http_url_cmp(const char *url, uint16_t url_len,
+ const char *root_url, uint16_t root_url_len);
+
+static void http_tx(struct http_server_ctx *http_ctx);
+
+void http_rx_tx(struct net_context *net_ctx, struct net_buf *rx, int status,
+ void *user_data)
+{
+ struct http_server_ctx *http_ctx = NULL;
+ struct net_buf *data = NULL;
+ uint16_t rcv_len;
+ uint16_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__);
+ http_ctx_release(http_ctx);
+ goto lb_exit;
+ }
+
+ rcv_len = net_nbuf_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_buf_frags_len(rx) - rcv_len;
+ rc = net_nbuf_linear_copy(data, rx, offset, rcv_len);
+ if (rc != 0) {
+ printf("[%s:%d] Linear copy error\n", __func__, __LINE__);
+ goto lb_exit;
+ }
+
+ 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_write_400_bad_request(http_ctx);
+ } else {
+ http_tx(http_ctx);
+ }
+
+lb_exit:
+ net_buf_unref(data);
+ net_buf_unref(rx);
+}
+
+/**
+ * @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];
+
+/**
+ * @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].used == 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->used = 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->used = 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, uint8_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, uint16_t url_len,
+ const char *root_url, uint16_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)
+{
+ uint16_t url_len = http_ctx->url_len;
+ const char *url = http_ctx->url;
+ struct http_root_url *root_url;
+ uint8_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);
+ }
+}
diff --git a/samples/net/http_server/src/http_server.h b/samples/net/http_server/src/http_server.h
new file mode 100644
index 000000000..45751c56f
--- /dev/null
+++ b/samples/net/http_server/src/http_server.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _HTTP_SERVER_H_
+#define _HTTP_SERVER_H_
+
+#include <net/net_context.h>
+
+/**
+ * @brief http_rx_tx Reads the HTTP request from the `rx` buffer
+ * and writes an HTTP 1.1 200 OK response with client
+ * header fields or an error message
+ * @param ctx The network context
+ * @param rx The reception buffer
+ * @param status Connection status, see `net_context_recv_cb_t`
+ * @param user_data User-provided data
+ */
+void http_rx_tx(struct net_context *ctx, struct net_buf *rx, int status,
+ void *user_data);
+
+/**
+ * @brief http_ctx_init Initializes the HTTP server contexts collection
+ * @return 0, future versions may return error codes
+ */
+int http_ctx_init(void);
+
+/**
+ * @brief http_ctx_get Gets an HTTP server context struct
+ * @return A valid HTTP server ctx on success
+ * @return NULL on error
+ */
+struct http_server_ctx *http_ctx_get(void);
+
+/**
+ * @brief http_ctx_set Assigns the net_ctx pointer to the HTTP ctx
+ * @param http_ctx HTTP server context struct
+ * @param net_ctx Network context
+ * @return 0 on success
+ * @return -EINVAL on error
+ */
+int http_ctx_set(struct http_server_ctx *http_ctx, struct net_context *net_ctx);
+
+int http_url_default_handler(int (*write_cb)(struct http_server_ctx *));
+
+int http_url_add(const char *url, uint8_t flags,
+ int (*write_cb)(struct http_server_ctx *http_ctx));
+
+#endif
diff --git a/samples/net/http_server/src/http_types.h b/samples/net/http_server/src/http_types.h
new file mode 100644
index 000000000..abec59cb3
--- /dev/null
+++ b/samples/net/http_server/src/http_types.h
@@ -0,0 +1,98 @@
+/*
+ * 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_parser.h>
+
+/* Max number of HTTP header field-value pairs */
+#define HTTP_PARSER_MAX_FIELD_VALUES 10
+
+/* Max number of HTTP server context available to this app */
+#define HTTP_MAX_NUMBER_SERVER_CTX 16
+
+/* Max number of URLs this server will handle */
+#define HTTP_MAX_NUMBER_URL 16
+
+/**
+ * @brief HTTP_CTX_STATE enum HTTP context state
+ */
+enum HTTP_CTX_STATE {
+ HTTP_CTX_FREE = 0,
+ HTTP_CTX_IN_USE
+};
+
+/**
+ * @brief http_field_value HTTP header fields struct
+ */
+struct http_field_value {
+ /** Field name, this variable will point to the beginning of the string
+ * containing the HTTP field name
+ */
+ const char *key;
+ /** Value, this variable will point to the beginning of the string
+ * containing the field's value
+ */
+ const char *value;
+
+ /** Length of the field name */
+ uint16_t key_len;
+ /** Length of the field's value */
+ uint16_t value_len;
+};
+
+/**
+ * @brief http_server_ctx The HTTP server context struct
+ */
+struct http_server_ctx {
+
+ uint8_t used;
+
+ /** Collection of header fields */
+ struct http_field_value field_values[HTTP_PARSER_MAX_FIELD_VALUES];
+ /** Number of header field elements */
+ uint16_t field_values_ctr;
+
+ /** HTTP Request URL */
+ const char *url;
+ /** URL's length */
+ uint16_t url_len;
+
+ /** HTTP Parser settings */
+ struct http_parser_settings parser_settings;
+ /** The HTTP Parser struct */
+ struct http_parser parser;
+
+ /**IP stack network context */
+ struct net_context *net_ctx;
+};
+
+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;
+ uint16_t root_len;
+
+ uint8_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];
+ uint8_t urls_ctr;
+};
+
+#endif
diff --git a/samples/net/http_server/src/http_utils.c b/samples/net/http_server/src/http_utils.c
new file mode 100644
index 000000000..6db5b5f84
--- /dev/null
+++ b/samples/net/http_server/src/http_utils.c
@@ -0,0 +1,51 @@
+/*
+ * 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;
+ uint16_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;
+ uint16_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);
+}
diff --git a/samples/net/http_server/src/http_utils.h b/samples/net/http_server/src/http_utils.h
new file mode 100644
index 000000000..9fb3bb69a
--- /dev/null
+++ b/samples/net/http_server/src/http_utils.h
@@ -0,0 +1,18 @@
+/*
+ * 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
diff --git a/samples/net/http_server/src/http_write_utils.c b/samples/net/http_server/src/http_write_utils.c
new file mode 100644
index 000000000..a96853040
--- /dev/null
+++ b/samples/net/http_server/src/http_write_utils.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "http_write_utils.h"
+#include "http_types.h"
+#include "http_utils.h"
+
+#include <net/nbuf.h>
+#include <stdio.h>
+
+uint16_t http_strlen(const char *str)
+{
+ if (str) {
+ return strlen(str);
+ }
+
+ return 0;
+}
+
+int http_add_header(struct net_buf *tx, const char *str)
+{
+ net_nbuf_append(tx, strlen(str), (uint8_t *)str);
+
+ return 0;
+}
+
+int http_add_chunk(struct net_buf *tx, const char *str)
+{
+ char chunk_header[16];
+ char *rn = "\r\n";
+ uint16_t str_len;
+
+ str_len = http_strlen(str);
+
+ snprintf(chunk_header, sizeof(chunk_header), "%x\r\n", str_len);
+ net_nbuf_append(tx, strlen(chunk_header), chunk_header);
+
+ if (str_len) {
+ net_nbuf_append(tx, str_len, (uint8_t *)str);
+ }
+
+ net_nbuf_append(tx, strlen(rn), rn);
+
+ return 0;
+}
+
+int http_write(struct http_server_ctx *ctx, const char *http_header,
+ const char *html_header, const char *html_body,
+ const char *html_footer)
+{
+ struct net_buf *tx;
+ int rc = -EINVAL;
+
+ tx = net_nbuf_get_tx(ctx->net_ctx);
+ printf("[%s:%d] net_nbuf_get_tx, rc: %d <%s>\n",
+ __func__, __LINE__, !tx, RC_STR(!tx));
+ if (tx == NULL) {
+ goto exit_routine;
+ }
+
+ http_add_header(tx, http_header);
+ if (html_header) {
+ http_add_chunk(tx, html_header);
+ }
+
+ if (html_body) {
+ http_add_chunk(tx, html_body);
+ }
+
+ if (html_footer) {
+ http_add_chunk(tx, html_footer);
+ }
+
+ /* like EOF */
+ http_add_chunk(tx, NULL);
+
+ rc = net_context_send(tx, NULL, 0, NULL, NULL);
+ printf("[%s:%d] net_context_send: %d <%s>\n",
+ __func__, __LINE__, rc, RC_STR(rc));
+ if (rc != 0) {
+ goto exit_routine;
+ }
+
+ tx = NULL;
+
+exit_routine:
+ /* unref can handle NULL buffers, so we are covered */
+ net_nbuf_unref(tx);
+
+ return rc;
+}
+
+/* Prints the received HTTP header fields as an HTML list */
+static void print_http_headers(struct http_server_ctx *ctx,
+ char *str, uint16_t size)
+{
+ struct http_parser *parser = &ctx->parser;
+ uint16_t offset = 0;
+
+ offset = snprintf(str, size,
+ "<body><h1>Zephyr HTTP server</h1>"
+ "<h2>HTTP Header Fields</h2>\r\n<ul>\r\n");
+ if (offset >= size) {
+ return;
+ }
+
+ for (uint8_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></body>\r\n", CONFIG_ARCH);
+}
+
+#define HTTP_MAX_BODY_STR_SIZE 512
+static char html_body[HTTP_MAX_BODY_STR_SIZE];
+
+int http_write_header_fields(struct http_server_ctx *ctx)
+{
+ print_http_headers(ctx, html_body, HTTP_MAX_BODY_STR_SIZE);
+
+ return http_write(ctx, HTTP_STATUS_200_OK, HTML_STR_HEADER,
+ html_body, HTML_STR_FOOTER);
+}
+
+int http_write_it_works(struct http_server_ctx *ctx)
+{
+ return http_write(ctx, HTTP_STATUS_200_OK, HTML_STR_HEADER,
+ "<body><h1><center>It Works!</center></h1>"
+ "</body>\r\n", HTML_STR_FOOTER);
+}
+
+int http_write_400_bad_request(struct http_server_ctx *ctx)
+{
+ return http_write(ctx, HTTP_STATUS_400_BR, NULL, NULL, NULL);
+}
+
+int http_write_403_forbidden(struct http_server_ctx *ctx)
+{
+ return http_write(ctx, HTTP_STATUS_403_FBD, NULL, NULL, NULL);
+}
+
+int http_write_404_not_found(struct http_server_ctx *ctx)
+{
+ return http_write(ctx, HTTP_STATUS_404_NF, NULL, NULL, NULL);
+}
+
+int http_write_soft_404_not_found(struct http_server_ctx *ctx)
+{
+ return http_write(ctx, HTTP_STATUS_200_OK, HTML_STR_HEADER,
+ "<body><h1><center>404 Not Found</center></h1>"
+ "</body>\r\n", HTML_STR_FOOTER);
+}
diff --git a/samples/net/http_server/src/http_write_utils.h b/samples/net/http_server/src/http_write_utils.h
new file mode 100644
index 000000000..0c53e3b28
--- /dev/null
+++ b/samples/net/http_server/src/http_write_utils.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _HTTP_WRITE_UTILS_H
+#define _HTTP_WRITE_UTILS_H
+
+#include "http_types.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_STATUS_400_BR "HTTP/1.1 400 Bad Request\r\n" \
+ "\r\n"
+
+#define HTTP_STATUS_403_FBD "HTTP/1.1 403 Forbidden\r\n" \
+ "\r\n"
+
+#define HTTP_STATUS_404_NF "HTTP/1.1 404 Not Found\r\n" \
+ "\r\n"
+
+#define HTML_STR_HEADER "<html><head>\r\n" \
+ "<title>Zephyr HTTP Server</title>\r\n" \
+ "</head>\r\n"
+
+#define HTML_STR_FOOTER "</html>\r\n"
+
+uint16_t http_strlen(const char *str);
+
+/* Adds the HTTP header to the tx buffer */
+int http_add_header(struct net_buf *tx, const char *str);
+
+/* Writes a generic data chunk to the tx buffer, could be HTML */
+int http_add_chunk(struct net_buf *tx, const char *str);
+
+/* Writes an HTTP response with an HTML structure to the client */
+int http_write(struct http_server_ctx *ctx, const char *http_header,
+ const char *html_header, const char *html_body,
+ const char *html_footer);
+
+/* Writes the received HTTP header fields to the client */
+int http_write_header_fields(struct http_server_ctx *ctx);
+
+/* Writes an elementary It Works! HTML web page to the client */
+int http_write_it_works(struct http_server_ctx *ctx);
+
+/* Writes a 400 Bad Request HTTP status code to the client */
+int http_write_400_bad_request(struct http_server_ctx *ctx);
+
+/* Writes a 403 forbidden HTTP status code to the client */
+int http_write_403_forbidden(struct http_server_ctx *ctx);
+
+/* Writes a 404 Not Found HTTP status code to the client */
+int http_write_404_not_found(struct http_server_ctx *ctx);
+
+/* Writes a 200 OK HTTP status code with a 404 Not Found HTML msg */
+int http_write_soft_404_not_found(struct http_server_ctx *ctx);
+
+#endif
diff --git a/samples/net/http_server/src/main.c b/samples/net/http_server/src/main.c
new file mode 100644
index 000000000..c01d789bb
--- /dev/null
+++ b/samples/net/http_server/src/main.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr.h>
+#include <net/net_context.h>
+
+#include <misc/printk.h>
+
+#include "http_types.h"
+#include "http_server.h"
+#include "http_utils.h"
+#include "http_write_utils.h"
+#include "config.h"
+
+static void network_setup(void);
+
+void main(void)
+{
+ http_ctx_init();
+
+ 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);
+
+ network_setup();
+}
+
+/**
+ * @brief connection_cb Callback executed when a connection is accepted
+ * @param ctx Network context
+ * @param addr Client's address
+ * @param addr_len Address length
+ * @param status Status code, 0 on success, < 0 otherwise
+ * @param data User-provided data
+ */
+void connection_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) {
+ printk("Status code: %d\n", status);
+ net_context_put(net_ctx);
+ return;
+ }
+
+ print_client_banner(addr);
+
+ http_ctx = http_ctx_get();
+ if (!http_ctx) {
+ printk("Unable to get free HTTP context\n");
+ net_context_put(net_ctx);
+ return;
+ }
+
+ http_ctx_set(http_ctx, net_ctx);
+
+ net_context_recv(net_ctx, http_rx_tx, 0, http_ctx);
+}
+
+/**
+ * @brief network_setup This routine configures the HTTP server.
+ * It puts the application in listening mode and
+ * installs the accept connection callback.
+ */
+static void network_setup(void)
+{
+ struct net_context *ctx = NULL;
+ struct sockaddr local_sock;
+ void *ptr;
+ int rc;
+
+#ifdef CONFIG_NET_IPV6
+ net_sin6(&local_sock)->sin6_port = htons(ZEPHYR_PORT);
+ sock_addr.family = AF_INET6;
+ ptr = &(net_sin6(&local_sock)->sin6_addr);
+ rc = net_addr_pton(AF_INET6, ZEPHYR_ADDR, ptr);
+#else
+ net_sin(&local_sock)->sin_port = htons(ZEPHYR_PORT);
+ local_sock.family = AF_INET;
+ ptr = &(net_sin(&local_sock)->sin_addr);
+ rc = net_addr_pton(AF_INET, ZEPHYR_ADDR, ptr);
+#endif
+
+ if (rc) {
+ printk("Invalid IP address/Port: %s, %d\n",
+ ZEPHYR_ADDR, ZEPHYR_PORT);
+ }
+
+#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
+ print_server_banner(&local_sock);
+
+#if defined(CONFIG_NET_IPV6)
+ rc = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, &ctx);
+#else
+ rc = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, &ctx);
+#endif
+ if (rc != 0) {
+ printk("net_context_get error\n");
+ return;
+ }
+
+ rc = net_context_bind(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(ctx, 0);
+ if (rc != 0) {
+ printk("[%s:%d] net_context_listen %d, <%s>\n",
+ __func__, __LINE__, rc, RC_STR(rc));
+ goto lb_error;
+ }
+
+ rc = net_context_accept(ctx, connection_cb, 0, NULL);
+ if (rc != 0) {
+ printk("[%s:%d] net_context_accept %d, <%s>\n",
+ __func__, __LINE__, rc, RC_STR(rc));
+ goto lb_error;
+ }
+
+ return;
+
+lb_error:
+ net_context_put(ctx);
+}
diff --git a/samples/net/http_server/testcase.ini b/samples/net/http_server/testcase.ini
new file mode 100644
index 000000000..1c14f3d70
--- /dev/null
+++ b/samples/net/http_server/testcase.ini
@@ -0,0 +1,4 @@
+[test]
+tags = net http
+build_only = true
+platform_whitelist = frdm_k64f