blob: 0d3ae4daa3921fa942423517e5e0d7ff22ab9e86 [file] [log] [blame]
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#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 <zephyr.h>
#include <stdio.h>
/* For Basic auth, we need base64 decoder which can be found
* in mbedtls library.
*/
#include <mbedtls/base64.h>
#include <net/net_context.h>
#include <net/http.h>
#include <net_sample_app.h>
#include "config.h"
/* Sets the network parameters */
#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 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, 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)
{
/* Let the peer close the connection but if it does not do it
* close it ourselves after 1 sec.
*/
return http_response_wait(ctx, HTTP_STATUS_200_OK, HTML_HEADER
"<body><h2><center>It Works!</center></h2>"
HTML_FOOTER, K_SECONDS(1));
}
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);
}
static int http_response_auth(struct http_server_ctx *ctx)
{
return http_response(ctx, HTTP_STATUS_200_OK, HTML_HEADER
"<h2><center>user: "HTTP_AUTH_USERNAME
", password: "HTTP_AUTH_PASSWORD
"</center></h2>" HTML_FOOTER);
}
int http_response_401(struct http_server_ctx *ctx, s32_t timeout)
{
return http_response_wait(ctx, HTTP_401_STATUS_US, NULL, timeout);
}
static int http_basic_auth(struct http_server_ctx *ctx)
{
const char auth_str[] = HTTP_CRLF "Authorization: Basic ";
char *ptr;
ptr = strstr(ctx->req.field_values[0].key, auth_str);
if (ptr) {
char output[sizeof(HTTP_AUTH_USERNAME) +
sizeof(":") +
sizeof(HTTP_AUTH_PASSWORD)];
size_t olen, ilen, alen;
char *end, *colon;
int ret;
memset(output, 0, sizeof(output));
end = strstr(ptr + 2, HTTP_CRLF);
if (!end) {
return http_response_401(ctx, K_NO_WAIT);
}
alen = sizeof(auth_str) - 1;
ilen = end - (ptr + alen);
ret = mbedtls_base64_decode(output, sizeof(output) - 1,
&olen, ptr + alen, ilen);
if (ret) {
return http_response_401(ctx, K_NO_WAIT);
}
colon = memchr(output, ':', olen);
if (colon && colon > output && colon < (output + olen) &&
memcmp(output, HTTP_AUTH_USERNAME, colon - output) == 0 &&
memcmp(colon + 1, HTTP_AUTH_PASSWORD,
output + olen - colon) == 0) {
return http_response_auth(ctx);
}
return http_response_401(ctx, K_NO_WAIT);
}
/* Wait 2 secs for the reply with proper credentials */
return http_response_401(ctx, K_SECONDS(2));
}
#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)
{
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)
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
server_addr = NULL;
ARG_UNUSED(addr);
#endif
http_server_add_default(&http_urls, http_response_soft_404);
http_server_add_url(&http_urls, HTTP_AUTH_URL, HTTP_URL_STANDARD,
http_basic_auth);
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,
K_THREAD_STACK_SIZEOF(https_stack));
if (ret < 0) {
NET_ERR("Cannot initialize HTTPS server (%d)", ret);
panic(NULL);
}
http_server_enable(&https_ctx);
#endif
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);
}
/*
* 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);
}