Browse Source

net: http: Remove the old legacy API

There are no internal users for old HTTP API so removing it.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
pull/5332/head
Jukka Rissanen 8 years ago committed by Anas Nashif
parent
commit
d1675bf3e6
  1. 1082
      include/net/http.h
  2. 1092
      include/net/http_app.h
  3. 1244
      include/net/http_legacy.h
  4. 32
      subsys/net/ip/net_shell.c
  5. 11
      subsys/net/lib/http/CMakeLists.txt
  6. 60
      subsys/net/lib/http/Kconfig
  7. 0
      subsys/net/lib/http/http.c
  8. 733
      subsys/net/lib/http/http_app_client.c
  9. 922
      subsys/net/lib/http/http_app_server.c
  10. 1856
      subsys/net/lib/http/http_client.c
  11. 1751
      subsys/net/lib/http/http_server.c

1082
include/net/http.h

File diff suppressed because it is too large Load Diff

1092
include/net/http_app.h

File diff suppressed because it is too large Load Diff

1244
include/net/http_legacy.h

File diff suppressed because it is too large Load Diff

32
subsys/net/ip/net_shell.c

@ -1398,37 +1398,6 @@ static char *http_str_output(char *output, int outlen, const char *str, int len) @@ -1398,37 +1398,6 @@ static char *http_str_output(char *output, int outlen, const char *str, int len)
return output;
}
#if !defined(CONFIG_HTTP_APP)
static void http_server_cb(struct http_server_ctx *entry,
void *user_data)
{
int *count = user_data;
static char output[MAX_HTTP_OUTPUT_LEN];
/* +7 for []:port */
char addr_local[ADDR_LEN + 7];
char addr_remote[ADDR_LEN + 7] = "";
get_addresses(entry->req.net_ctx, addr_local, sizeof(addr_local),
addr_remote, sizeof(addr_remote));
if (*count == 0) {
printk(" HTTP ctx Local \t"
"Remote \tURL\n");
}
(*count)++;
printk("[%2d] %c%c %p %16s\t%16s\t%s\n",
*count, entry->enabled ? 'E' : 'D',
entry->is_https ? 'S' : ' ',
entry, addr_local, addr_remote,
http_str_output(output, sizeof(output) - 1,
entry->req.url, entry->req.url_len));
}
#endif
#if defined(CONFIG_HTTP_APP)
static void http_server_cb(struct http_ctx *entry, void *user_data)
{
int *count = user_data;
@ -1465,7 +1434,6 @@ static void http_server_cb(struct http_ctx *entry, void *user_data) @@ -1465,7 +1434,6 @@ static void http_server_cb(struct http_ctx *entry, void *user_data)
entry->http.url, entry->http.url_len));
}
}
#endif /* CONFIG_HTTP_APP */
#endif /* CONFIG_NET_DEBUG_HTTP_CONN && CONFIG_HTTP_SERVER */
int net_shell_cmd_http(int argc, char *argv[])

11
subsys/net/lib/http/CMakeLists.txt

@ -7,15 +7,10 @@ endif() @@ -7,15 +7,10 @@ endif()
zephyr_library_sources_if_kconfig(http_parser.c)
zephyr_library_sources_if_kconfig(http_parser_url.c)
if(CONFIG_HTTP_APP)
zephyr_library_sources(http_app.c)
zephyr_library_sources(http.c)
zephyr_library_sources_ifdef(CONFIG_HTTP_SERVER http_app_server.c)
zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_app_client.c)
else()
zephyr_library_sources_ifdef(CONFIG_HTTP_SERVER http_server.c)
zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_client.c)
endif()
zephyr_library_sources_ifdef(CONFIG_HTTP_SERVER http_server.c)
zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_client.c)
zephyr_link_interface_ifdef(CONFIG_MBEDTLS mbedTLS)
zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS)

60
subsys/net/lib/http/Kconfig

@ -11,12 +11,6 @@ config HTTP @@ -11,12 +11,6 @@ config HTTP
if HTTP
config HTTP_APP
bool "Use new HTTP API built on top of net-app API"
default y
if HTTP_APP
config HTTP_SERVER
bool "HTTP server support"
default n
@ -60,60 +54,6 @@ config HTTP_SERVER_CONNECTIONS @@ -60,60 +54,6 @@ config HTTP_SERVER_CONNECTIONS
served at a time, so set CONFIG_NET_APP_SERVER_NUM_CONN and
CONFIG_NET_TCP_BACKLOG_SIZE to 1 in that case.
endif # HTTP_APP
if !HTTP_APP
config HTTP_SERVER
bool "HTTP server support"
default n
select HTTP_PARSER
select HTTP_PARSER_URL
help
Enables HTTP server routines.
config HTTP_CLIENT
bool "HTTP client support"
default n
select HTTP_PARSER
select HTTP_PARSER_URL
help
Enables HTTP client routines.
config HTTP_HEADER_FIELD_ITEMS
int "HTTP header field max number of items"
depends on HTTP_SERVER
default 8
help
Number of HTTP header field items that an HTTP server
application will handle
config HTTPS
bool "HTTPS support"
default n
select 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 HTTP_SERVER_CONNECTIONS
int "Max number of concurrent 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. Note that only 1 HTTPS connection can be
served at a time.
endif # !HTTP_APP
config HTTP_SERVER_NUM_URLS
int "Max number of URLs that the HTTP server will handle"
default 8

0
subsys/net/lib/http/http_app.c → subsys/net/lib/http/http.c

733
subsys/net/lib/http/http_app_client.c

@ -1,733 +0,0 @@ @@ -1,733 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_NET_DEBUG_HTTP)
#if defined(CONFIG_HTTPS)
#define SYS_LOG_DOMAIN "https/client"
#else
#define SYS_LOG_DOMAIN "http/client"
#endif
#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#define NET_LOG_ENABLED 1
#endif
#include <zephyr.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdlib.h>
#include <version.h>
#include <net/net_core.h>
#include <net/net_ip.h>
#include <net/http.h>
#include "../../ip/net_private.h"
#define BUF_ALLOC_TIMEOUT 100
#define RC_STR(rc) (rc == 0 ? "OK" : "ERROR")
#define HTTP_EOF "\r\n\r\n"
#define HTTP_HOST "Host"
#define HTTP_CONTENT_TYPE "Content-Type"
#define HTTP_CONTENT_LEN "Content-Length"
#define HTTP_CONT_LEN_SIZE 6
/* Default network activity timeout in seconds */
#define HTTP_NETWORK_TIMEOUT K_SECONDS(CONFIG_HTTP_CLIENT_NETWORK_TIMEOUT)
int client_reset(struct http_ctx *ctx)
{
http_parser_init(&ctx->http.parser, HTTP_RESPONSE);
memset(ctx->http.rsp.http_status, 0,
sizeof(ctx->http.rsp.http_status));
ctx->http.rsp.cl_present = 0;
ctx->http.rsp.content_length = 0;
ctx->http.rsp.processed = 0;
ctx->http.rsp.body_found = 0;
ctx->http.rsp.message_complete = 0;
ctx->http.rsp.body_start = NULL;
memset(ctx->http.rsp.response_buf, 0, ctx->http.rsp.response_buf_len);
ctx->http.rsp.data_len = 0;
return 0;
}
int http_request(struct http_ctx *ctx, struct http_request *req, s32_t timeout,
void *user_data)
{
const char *method = http_method_str(req->method);
int ret;
if (ctx->pending) {
net_pkt_unref(ctx->pending);
ctx->pending = NULL;
}
ret = http_add_header(ctx, method, user_data);
if (ret < 0) {
goto out;
}
ret = http_add_header(ctx, " ", user_data);
if (ret < 0) {
goto out;
}
ret = http_add_header(ctx, req->url, user_data);
if (ret < 0) {
goto out;
}
ret = http_add_header(ctx, req->protocol, user_data);
if (ret < 0) {
goto out;
}
ret = http_add_header(ctx, HTTP_CRLF, user_data);
if (ret < 0) {
goto out;
}
if (req->host) {
ret = http_add_header_field(ctx, HTTP_HOST, req->host,
user_data);
if (ret < 0) {
goto out;
}
}
if (req->header_fields) {
ret = http_add_header(ctx, req->header_fields, user_data);
if (ret < 0) {
goto out;
}
}
if (req->content_type_value) {
ret = http_add_header_field(ctx, HTTP_CONTENT_TYPE,
req->content_type_value,
user_data);
if (ret < 0) {
goto out;
}
}
if (req->payload && req->payload_size) {
char content_len_str[HTTP_CONT_LEN_SIZE];
ret = snprintk(content_len_str, HTTP_CONT_LEN_SIZE,
"%u", req->payload_size);
if (ret <= 0 || ret >= HTTP_CONT_LEN_SIZE) {
ret = -ENOMEM;
goto out;
}
ret = http_add_header_field(ctx, HTTP_CONTENT_LEN,
content_len_str, user_data);
if (ret < 0) {
goto out;
}
ret = http_add_header(ctx, HTTP_CRLF, user_data);
if (ret < 0) {
goto out;
}
ret = http_prepare_and_send(ctx, req->payload,
req->payload_size, user_data);
if (ret < 0) {
goto out;
}
} else {
ret = http_add_header(ctx, HTTP_EOF, user_data);
if (ret < 0) {
goto out;
}
}
http_send_flush(ctx, user_data);
out:
if (ctx->pending) {
net_pkt_unref(ctx->pending);
ctx->pending = NULL;
}
return ret;
}
#if defined(CONFIG_NET_DEBUG_HTTP)
static void sprint_addr(char *buf, int len,
sa_family_t family,
struct sockaddr *addr)
{
if (family == AF_INET6) {
net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf, len);
} else if (family == AF_INET) {
net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf, len);
} else {
NET_DBG("Invalid protocol family");
}
}
#endif
static inline void print_info(struct http_ctx *ctx,
enum http_method method)
{
#if defined(CONFIG_NET_DEBUG_HTTP)
char local[NET_IPV6_ADDR_LEN];
char remote[NET_IPV6_ADDR_LEN];
sprint_addr(local, NET_IPV6_ADDR_LEN,
ctx->app_ctx.default_ctx->local.sa_family,
&ctx->app_ctx.default_ctx->local);
sprint_addr(remote, NET_IPV6_ADDR_LEN,
ctx->app_ctx.default_ctx->remote.sa_family,
&ctx->app_ctx.default_ctx->remote);
NET_DBG("HTTP %s (%s) %s -> %s port %d",
http_method_str(method), ctx->http.req.host, local, remote,
ntohs(net_sin(&ctx->app_ctx.default_ctx->remote)->sin_port));
#endif
}
int http_client_send_req(struct http_ctx *ctx,
struct http_request *req,
http_response_cb_t cb,
u8_t *response_buf,
size_t response_buf_len,
void *user_data,
s32_t timeout)
{
int ret;
if (!response_buf || response_buf_len == 0) {
return -EINVAL;
}
ctx->http.rsp.response_buf = response_buf;
ctx->http.rsp.response_buf_len = response_buf_len;
client_reset(ctx);
if (!req->host) {
req->host = ctx->server;
}
ctx->http.req.host = req->host;
ctx->http.req.method = req->method;
ctx->http.req.user_data = user_data;
ctx->http.rsp.cb = cb;
ret = net_app_connect(&ctx->app_ctx, HTTP_NETWORK_TIMEOUT);
if (ret < 0) {
NET_DBG("Cannot connect to server (%d)", ret);
return ret;
}
/* We might wait longer than timeout if the first connection
* establishment takes long time (like with HTTPS)
*/
if (k_sem_take(&ctx->http.connect_wait, HTTP_NETWORK_TIMEOUT)) {
NET_DBG("Connection timed out");
ret = -ETIMEDOUT;
goto out;
}
print_info(ctx, ctx->http.req.method);
ret = http_request(ctx, req, timeout, user_data);
if (ret < 0) {
NET_DBG("Send error (%d)", ret);
goto out;
}
if (timeout != 0 && k_sem_take(&ctx->http.req.wait, timeout)) {
ret = -ETIMEDOUT;
goto out;
}
if (timeout == 0) {
return -EINPROGRESS;
}
return 0;
out:
return ret;
}
static void print_header_field(size_t len, const char *str)
{
#if defined(CONFIG_NET_DEBUG_HTTP)
#define MAX_OUTPUT_LEN 128
char output[MAX_OUTPUT_LEN];
/* The value of len does not count \0 so we need to increase it
* by one.
*/
if ((len + 1) > sizeof(output)) {
len = sizeof(output) - 1;
}
snprintk(output, len + 1, "%s", str);
NET_DBG("[%zd] %s", len, output);
#endif
}
static int on_url(struct http_parser *parser, const char *at, size_t length)
{
ARG_UNUSED(parser);
print_header_field(length, at);
return 0;
}
static int on_status(struct http_parser *parser, const char *at, size_t length)
{
u16_t len;
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
len = min(length, sizeof(ctx->http.rsp.http_status) - 1);
memcpy(ctx->http.rsp.http_status, at, len);
ctx->http.rsp.http_status[len] = 0;
NET_DBG("HTTP response status %s", ctx->http.rsp.http_status);
return 0;
}
static int on_header_field(struct http_parser *parser, const char *at,
size_t length)
{
const char *content_len = HTTP_CONTENT_LEN;
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
u16_t len;
len = strlen(content_len);
if (length >= len && memcmp(at, content_len, len) == 0) {
ctx->http.rsp.cl_present = true;
}
print_header_field(length, at);
return 0;
}
#define MAX_NUM_DIGITS 16
static int on_header_value(struct http_parser *parser, const char *at,
size_t length)
{
char str[MAX_NUM_DIGITS];
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
if (ctx->http.rsp.cl_present) {
if (length <= MAX_NUM_DIGITS - 1) {
long int num;
memcpy(str, at, length);
str[length] = 0;
num = strtol(str, NULL, 10);
if (num == LONG_MIN || num == LONG_MAX) {
return -EINVAL;
}
ctx->http.rsp.content_length = num;
}
ctx->http.rsp.cl_present = false;
}
print_header_field(length, at);
return 0;
}
static int on_body(struct http_parser *parser, const char *at, size_t length)
{
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
ctx->http.rsp.body_found = 1;
ctx->http.rsp.processed += length;
NET_DBG("Processed %zd length %zd", ctx->http.rsp.processed, length);
if (!ctx->http.rsp.body_start &&
(u8_t *)at != (u8_t *)ctx->http.rsp.response_buf) {
ctx->http.rsp.body_start = (u8_t *)at;
}
if (ctx->http.rsp.cb) {
NET_DBG("Calling callback for partitioned %zd len data",
ctx->http.rsp.data_len);
ctx->http.rsp.cb(ctx,
ctx->http.rsp.response_buf,
ctx->http.rsp.response_buf_len,
ctx->http.rsp.data_len,
HTTP_DATA_MORE,
ctx->http.req.user_data);
/* Re-use the result buffer and start to fill it again */
ctx->http.rsp.data_len = 0;
ctx->http.rsp.body_start = NULL;
}
return 0;
}
static int on_headers_complete(struct http_parser *parser)
{
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
if (parser->status_code >= 500 && parser->status_code < 600) {
NET_DBG("Status %d, skipping body", parser->status_code);
return 1;
}
if ((ctx->http.req.method == HTTP_HEAD ||
ctx->http.req.method == HTTP_OPTIONS)
&& ctx->http.rsp.content_length > 0) {
NET_DBG("No body expected");
return 1;
}
NET_DBG("Headers complete");
return 0;
}
static int on_message_begin(struct http_parser *parser)
{
#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2)
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
NET_DBG("-- HTTP %s response (headers) --",
http_method_str(ctx->http.req.method));
#else
ARG_UNUSED(parser);
#endif
return 0;
}
static int on_message_complete(struct http_parser *parser)
{
struct http_ctx *ctx = CONTAINER_OF(parser,
struct http_ctx,
http.parser);
NET_DBG("-- HTTP %s response (complete) --",
http_method_str(ctx->http.req.method));
if (ctx->http.rsp.cb) {
ctx->http.rsp.cb(ctx,
ctx->http.rsp.response_buf,
ctx->http.rsp.response_buf_len,
ctx->http.rsp.data_len,
HTTP_DATA_FINAL,
ctx->http.req.user_data);
}
ctx->http.rsp.message_complete = 1;
k_sem_give(&ctx->http.req.wait);
return 0;
}
static int on_chunk_header(struct http_parser *parser)
{
ARG_UNUSED(parser);
return 0;
}
static int on_chunk_complete(struct http_parser *parser)
{
ARG_UNUSED(parser);
return 0;
}
static void http_received(struct net_app_ctx *app_ctx,
struct net_pkt *pkt,
int status,
void *user_data)
{
struct http_ctx *ctx = user_data;
size_t start = ctx->http.rsp.data_len;
u16_t len = 0;
struct net_buf *frag, *prev_frag = NULL;
size_t recv_len;
size_t pkt_len;
recv_len = net_pkt_appdatalen(pkt);
if (recv_len == 0) {
/* don't print info about zero-length app data buffers */
goto quit;
}
if (status) {
NET_DBG("[%p] Status %d <%s>", ctx, status, RC_STR(status));
goto out;
}
/* Get rid of possible IP headers in the first fragment. */
frag = pkt->frags;
pkt_len = net_pkt_get_len(pkt);
if (recv_len < pkt_len) {
net_buf_pull(frag, pkt_len - recv_len);
net_pkt_set_appdata(pkt, frag->data);
}
NET_DBG("[%p] Received %zd bytes http data", ctx, recv_len);
while (frag) {
/* If this fragment cannot be copied to result buf,
* then parse what we have which will cause the callback to be
* called in function on_body(), and continue copying.
*/
if ((ctx->http.rsp.data_len + frag->len) >
ctx->http.rsp.response_buf_len) {
/* If the caller has not supplied a callback, then
* we cannot really continue if the request buffer
* overflows. Set the data_len to mark how many bytes
* should be needed in the response_buf.
*/
if (!ctx->cb.recv) {
ctx->http.rsp.data_len = recv_len;
goto out;
}
http_parser_execute(&ctx->http.parser,
&ctx->http.parser_settings,
ctx->http.rsp.response_buf + start,
len);
ctx->http.rsp.data_len = 0;
len = 0;
start = 0;
}
memcpy(ctx->http.rsp.response_buf + ctx->http.rsp.data_len,
frag->data, frag->len);
ctx->http.rsp.data_len += frag->len;
len += frag->len;
prev_frag = frag;
frag = frag->frags;
pkt->frags = frag;
prev_frag->frags = NULL;
net_pkt_frag_unref(prev_frag);
}
out:
http_parser_execute(&ctx->http.parser,
&ctx->http.parser_settings,
ctx->http.rsp.response_buf + start,
len);
net_pkt_unref(pkt);
return;
quit:
http_parser_init(&ctx->http.parser, HTTP_RESPONSE);
ctx->http.rsp.data_len = 0;
net_pkt_unref(pkt);
}
static void http_data_sent(struct net_app_ctx *app_ctx,
int status,
void *user_data_send,
void *user_data)
{
struct http_ctx *ctx = user_data;
if (!user_data_send) {
/* This is the token field in the net_context_send().
* If this is not set, then it is TCP ACK messages
* that are generated by the stack. We just ignore those.
*/
return;
}
if (ctx->cb.send) {
ctx->cb.send(ctx, status, user_data_send, ctx->user_data);
}
}
static void http_connected(struct net_app_ctx *app_ctx,
int status,
void *user_data)
{
struct http_ctx *ctx = user_data;
if (status < 0) {
return;
}
if (ctx->cb.connect) {
ctx->cb.connect(ctx, HTTP_CONNECTION, ctx->user_data);
}
if (ctx->is_connected) {
return;
}
ctx->is_connected = true;
k_sem_give(&ctx->http.connect_wait);
}
static void http_closed(struct net_app_ctx *app_ctx,
int status,
void *user_data)
{
struct http_ctx *ctx = user_data;
ARG_UNUSED(app_ctx);
ARG_UNUSED(status);
NET_DBG("[%p] connection closed", ctx);
ctx->is_connected = false;
if (ctx->cb.close) {
ctx->cb.close(ctx, 0, ctx->user_data);
}
}
int http_client_init(struct http_ctx *ctx,
const char *server,
u16_t server_port,
struct sockaddr *server_addr,
s32_t timeout)
{
int ret;
memset(ctx, 0, sizeof(*ctx));
ret = net_app_init_tcp_client(&ctx->app_ctx,
NULL, /* use any local address */
server_addr,
server,
server_port,
timeout,
ctx);
if (ret < 0) {
NET_DBG("Cannot init HTTP client (%d)", ret);
return ret;
}
ret = net_app_set_cb(&ctx->app_ctx, http_connected, http_received,
http_data_sent, http_closed);
if (ret < 0) {
NET_ERR("Cannot set callbacks (%d)", ret);
return ret;
}
ctx->http.parser_settings.on_body = on_body;
ctx->http.parser_settings.on_chunk_complete = on_chunk_complete;
ctx->http.parser_settings.on_chunk_header = on_chunk_header;
ctx->http.parser_settings.on_headers_complete = on_headers_complete;
ctx->http.parser_settings.on_header_field = on_header_field;
ctx->http.parser_settings.on_header_value = on_header_value;
ctx->http.parser_settings.on_message_begin = on_message_begin;
ctx->http.parser_settings.on_message_complete = on_message_complete;
ctx->http.parser_settings.on_status = on_status;
ctx->http.parser_settings.on_url = on_url;
k_sem_init(&ctx->http.req.wait, 0, 1);
k_sem_init(&ctx->http.connect_wait, 0, 1);
ctx->server = server;
ctx->is_init = true;
ctx->is_client = true;
return 0;
}
int http_request_cancel(struct http_ctx *ctx)
{
if (!ctx->is_init) {
return -EINVAL;
}
if (!ctx->is_client) {
return -EINVAL;
}
client_reset(ctx);
return 0;
}
#if defined(CONFIG_HTTPS)
int http_client_set_tls(struct http_ctx *ctx,
u8_t *request_buf,
size_t request_buf_len,
u8_t *personalization_data,
size_t personalization_data_len,
net_app_ca_cert_cb_t cert_cb,
const char *cert_host,
net_app_entropy_src_cb_t entropy_src_cb,
struct k_mem_pool *pool,
k_thread_stack_t *https_stack,
size_t https_stack_size)
{
int ret;
ret = net_app_client_tls(&ctx->app_ctx,
request_buf,
request_buf_len,
personalization_data,
personalization_data_len,
cert_cb,
cert_host,
entropy_src_cb,
pool,
https_stack,
https_stack_size);
if (ret < 0) {
NET_DBG("Cannot init TLS (%d)", ret);
return ret;
}
ctx->is_tls = true;
return 0;
}
#endif /* CONFIG_HTTPS */

922
subsys/net/lib/http/http_app_server.c

@ -1,922 +0,0 @@ @@ -1,922 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_NET_DEBUG_HTTP)
#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 <string.h>
#include <strings.h>
#include <errno.h>
#include <stdlib.h>
#include <version.h>
#include <net/net_core.h>
#include <net/net_ip.h>
#include <net/http.h>
#define BUF_ALLOC_TIMEOUT 100
#define HTTP_DEFAULT_PORT 80
#define HTTPS_DEFAULT_PORT 443
#define RC_STR(rc) (rc == 0 ? "OK" : "ERROR")
/* Max length of the error description in HTTP error reply */
#define MAX_DESCRIPTION_LEN 20
#define HTTP_STATUS_400_BR "Bad Request"
#if defined(CONFIG_NET_DEBUG_HTTP_CONN)
/** List of http connections */
static sys_slist_t http_conn;
static http_server_cb_t ctx_mon;
static void *mon_user_data;
void http_server_conn_add(struct http_ctx *ctx)
{
sys_slist_prepend(&http_conn, &ctx->node);
if (ctx_mon) {
ctx_mon(ctx, mon_user_data);
}
}
void http_server_conn_del(struct http_ctx *ctx)
{
sys_slist_find_and_remove(&http_conn, &ctx->node);
}
void http_server_conn_foreach(http_server_cb_t cb, void *user_data)
{
struct http_ctx *ctx;
SYS_SLIST_FOR_EACH_CONTAINER(&http_conn, ctx, node) {
cb(ctx, user_data);
}
}
void http_server_conn_monitor(http_server_cb_t cb, void *user_data)
{
ctx_mon = cb;
mon_user_data = user_data;
}
#endif /* CONFIG_NET_DEBUG_HTTP_CONN */
const char * const http_state_str(enum http_state state)
{
#if defined(CONFIG_NET_DEBUG_HTTP)
switch (state) {
case HTTP_STATE_CLOSED:
return "CLOSED";
case HTTP_STATE_WAITING_HEADER:
return "WAITING_HEADER";
case HTTP_STATE_RECEIVING_HEADER:
return "RECEIVING HEADER";
case HTTP_STATE_HEADER_RECEIVED:
return "HEADER_RECEIVED";
case HTTP_STATE_OPEN:
return "OPEN";
}
#else /* CONFIG_NET_DEBUG_HTTP */
ARG_UNUSED(state);
#endif /* CONFIG_NET_DEBUG_HTTP */
return "";
}
#if defined(CONFIG_NET_DEBUG_HTTP)
static void validate_state_transition(struct http_ctx *ctx,
enum http_state current,
enum http_state new)
{
static const u16_t valid_transitions[] = {
[HTTP_STATE_CLOSED] = 1 << HTTP_STATE_WAITING_HEADER,
[HTTP_STATE_WAITING_HEADER] =
1 << HTTP_STATE_RECEIVING_HEADER |
1 << HTTP_STATE_CLOSED,
[HTTP_STATE_RECEIVING_HEADER] =
1 << HTTP_STATE_HEADER_RECEIVED |
1 << HTTP_STATE_CLOSED |
1 << HTTP_STATE_OPEN,
[HTTP_STATE_HEADER_RECEIVED] =
1 << HTTP_STATE_OPEN |
1 << HTTP_STATE_CLOSED,
[HTTP_STATE_OPEN] = 1 << HTTP_STATE_CLOSED,
};
if (!(valid_transitions[current] & 1 << new)) {
NET_DBG("[%p] Invalid state transition: %s (%d) => %s (%d)",
ctx, http_state_str(current), current,
http_state_str(new), new);
}
}
#endif /* CONFIG_NET_DEBUG_HTTP */
void _http_change_state(struct http_ctx *ctx,
enum http_state new_state,
const char *func, int line)
{
if (ctx->state == new_state) {
return;
}
NET_ASSERT(new_state >= HTTP_STATE_CLOSED &&
new_state <= HTTP_STATE_OPEN);
NET_DBG("[%p] state %s (%d) => %s (%d) [%s():%d]",
ctx, http_state_str(ctx->state), ctx->state,
http_state_str(new_state), new_state,
func, line);
#if defined(CONFIG_NET_DEBUG_HTTP)
validate_state_transition(ctx, ctx->state, new_state);
#endif /* CONFIG_NET_DEBUG_HTTP */
ctx->state = new_state;
}
static void http_data_sent(struct net_app_ctx *app_ctx,
int status,
void *user_data_send,
void *user_data)
{
struct http_ctx *ctx = user_data;
if (!user_data_send) {
/* This is the token field in the net_context_send().
* If this is not set, then it is TCP ACK messages
* that are generated by the stack. We just ignore those.
*/
return;
}
if (ctx->state == HTTP_STATE_OPEN && ctx->cb.send) {
ctx->cb.send(ctx, status, user_data_send, ctx->user_data);
}
}
int http_send_error(struct http_ctx *ctx, int code, const char *description,
u8_t *html_payload, size_t html_len)
{
char msg[sizeof(HTTP_PROTOCOL " xxx " HTTP_CRLF HTTP_CRLF) +
MAX_DESCRIPTION_LEN];
int ret;
if (ctx->pending) {
net_pkt_unref(ctx->pending);
ctx->pending = NULL;
}
if (code < 100 || code > 999) {
return -EINVAL;
}
snprintk(msg, sizeof(msg), "%s %d %s%s%s", HTTP_PROTOCOL, code,
description, HTTP_CRLF, HTTP_CRLF);
ret = http_add_header(ctx, msg, NULL);
if (ret < 0) {
goto quit;
}
if (html_payload) {
ret = http_prepare_and_send(ctx, html_payload, html_len, NULL);
if (ret < 0) {
goto quit;
}
}
ret = http_send_flush(ctx, NULL);
quit:
if (ret < 0) {
net_pkt_unref(ctx->pending);
ctx->pending = NULL;
}
return ret;
}
#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2)
static char *sprint_ipaddr(char *buf, int buflen, const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
char ipaddr[NET_IPV6_ADDR_LEN];
net_addr_ntop(addr->sa_family,
&net_sin6(addr)->sin6_addr,
ipaddr, sizeof(ipaddr));
snprintk(buf, buflen, "[%s]:%u", ipaddr,
ntohs(net_sin6(addr)->sin6_port));
#endif
} else if (addr->sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
char ipaddr[NET_IPV4_ADDR_LEN];
net_addr_ntop(addr->sa_family,
&net_sin(addr)->sin_addr,
ipaddr, sizeof(ipaddr));
snprintk(buf, buflen, "%s:%u", ipaddr,
ntohs(net_sin(addr)->sin_port));
#endif
} else {
snprintk(buf, buflen, "<AF_UNSPEC %d>", addr->sa_family);
}
return buf;
}
static struct net_context *get_server_ctx(struct net_app_ctx *ctx,
const struct sockaddr *dst)
{
int i;
if (!dst) {
return NULL;
}
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
struct net_context *tmp;
u16_t port, rport;
if (!ctx->server.net_ctxs[i]) {
continue;
}
tmp = ctx->server.net_ctxs[i];
if (IS_ENABLED(CONFIG_NET_IPV4) &&
tmp->remote.sa_family == AF_INET &&
dst->sa_family == AF_INET) {
struct in_addr *addr4 = &net_sin(dst)->sin_addr;
struct in_addr *remote4;
remote4 = &net_sin(&tmp->remote)->sin_addr;
rport = net_sin(&tmp->remote)->sin_port;
port = net_sin(dst)->sin_port;
if (net_ipv4_addr_cmp(addr4, remote4) &&
port == rport) {
return tmp;
}
} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
tmp->remote.sa_family == AF_INET6 &&
dst->sa_family == AF_INET6) {
struct in6_addr *addr6 = &net_sin6(dst)->sin6_addr;
struct in6_addr *remote6;
remote6 = &net_sin6(&tmp->remote)->sin6_addr;
rport = net_sin6(&tmp->remote)->sin6_port;
port = net_sin6(dst)->sin6_port;
if (net_ipv6_addr_cmp(addr6, remote6) &&
port == rport) {
return tmp;
}
}
}
return NULL;
}
#endif /* CONFIG_NET_DEBUG_HTTP */
static inline void new_client(struct http_ctx *ctx,
enum http_connection_type type,
struct net_app_ctx *app_ctx)
{
#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2)
#if defined(CONFIG_NET_IPV6)
#define PORT_LEN sizeof("[]:xxxxx")
#define ADDR_LEN NET_IPV6_ADDR_LEN
#elif defined(CONFIG_NET_IPV4)
#define PORT_LEN sizeof(":xxxxx")
#define ADDR_LEN NET_IPV4_ADDR_LEN
#endif
char buf[ADDR_LEN + PORT_LEN];
struct net_context *net_ctx;
const char *type_str = "HTTP";
net_ctx = get_server_ctx(app_ctx, ctx->addr);
if (net_ctx) {
NET_INFO("[%p] %s connection from %s (%p)", ctx, type_str,
sprint_ipaddr(buf, sizeof(buf), &net_ctx->remote),
net_ctx);
} else {
NET_INFO("[%p] %s connection", ctx, type_str);
}
#endif /* CONFIG_NET_DEBUG_HTTP */
}
static void url_connected(struct http_ctx *ctx,
enum http_connection_type type)
{
new_client(ctx, type, &ctx->app_ctx);
if (ctx->cb.connect) {
ctx->cb.connect(ctx, type, ctx->user_data);
}
}
struct http_root_url *http_server_add_url(struct http_server_urls *my,
const char *url, u8_t flags)
{
int i;
for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) {
if (my->urls[i].is_used) {
continue;
}
my->urls[i].is_used = true;
my->urls[i].root = url;
/* This will speed-up some future operations */
my->urls[i].root_len = strlen(url);
my->urls[i].flags = flags;
NET_DBG("[%d] %s URL %s", i,
flags == HTTP_URL_STANDARD ? "HTTP" :
(flags == HTTP_URL_WEBSOCKET ? "WS" : "<unknown>"),
url);
return &my->urls[i];
}
return NULL;
}
int http_server_del_url(struct http_server_urls *my, const char *url)
{
int i;
for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) {
if (!my->urls[i].is_used) {
continue;
}
if (strncmp(my->urls[i].root, url, my->urls[i].root_len)) {
continue;
}
my->urls[i].is_used = false;
my->urls[i].root = NULL;
return 0;
}
return -ENOENT;
}
struct http_root_url *http_server_add_default(struct http_server_urls *my,
http_url_cb_t cb)
{
if (my->default_url.is_used) {
return NULL;
}
my->default_url.is_used = true;
my->default_url.root = NULL;
my->default_url.root_len = 0;
my->default_url.flags = 0;
my->default_cb = cb;
return &my->default_url;
}
int http_server_del_default(struct http_server_urls *my)
{
if (!my->default_url.is_used) {
return -ENOENT;
}
my->default_url.is_used = false;
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 evaluate 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) {
/* Do not match root_url = / and url = /foobar */
if (root_url_len > 1 &&
root_url[root_url_len - 1] == '/') {
return 0;
}
if (url[root_url_len] == '/') {
return 0;
}
}
}
return -EINVAL;
}
struct http_root_url *http_url_find(struct http_ctx *ctx,
enum http_url_flags flags)
{
u16_t url_len = ctx->http.url_len;
const char *url = ctx->http.url;
struct http_root_url *root_url;
u8_t i;
int ret;
for (i = 0; i < CONFIG_HTTP_SERVER_NUM_URLS; i++) {
if (!ctx->http.urls) {
continue;
}
root_url = &ctx->http.urls->urls[i];
if (!root_url || !root_url->is_used) {
continue;
}
ret = http_url_cmp(url, url_len,
root_url->root, root_url->root_len);
if (!ret && flags == root_url->flags) {
return root_url;
}
}
return NULL;
}
static int http_process_recv(struct http_ctx *ctx)
{
struct http_root_url *root_url;
int ret;
root_url = http_url_find(ctx, HTTP_URL_STANDARD);
if (!root_url) {
if (!ctx->http.urls) {
NET_DBG("[%p] No URL handlers found", ctx);
ret = -ENOENT;
goto out;
}
root_url = &ctx->http.urls->default_url;
if (!root_url || !root_url->is_used) {
NET_DBG("[%p] No default handler found", ctx);
ret = -ENOENT;
goto out;
}
if (ctx->http.urls->default_cb) {
ret = ctx->http.urls->default_cb(ctx,
HTTP_CONNECTION);
if (ret != HTTP_VERDICT_ACCEPT) {
ret = -ECONNREFUSED;
goto out;
}
}
}
http_change_state(ctx, HTTP_STATE_OPEN);
url_connected(ctx, HTTP_CONNECTION);
ret = 0;
out:
return ret;
}
static void http_closed(struct net_app_ctx *app_ctx,
int status,
void *user_data)
{
struct http_ctx *ctx = user_data;
ARG_UNUSED(app_ctx);
ARG_UNUSED(status);
http_change_state(ctx, HTTP_STATE_CLOSED);
NET_DBG("[%p] http closed", ctx);
http_server_conn_del(ctx);
if (ctx->cb.close) {
ctx->cb.close(ctx, 0, ctx->user_data);
}
}
static void http_received(struct net_app_ctx *app_ctx,
struct net_pkt *pkt,
int status,
void *user_data)
{
struct http_ctx *ctx = user_data;
size_t start = ctx->http.data_len;
u16_t len = 0;
struct net_buf *frag;
int parsed_len;
size_t recv_len;
size_t pkt_len;
recv_len = net_pkt_appdatalen(pkt);
if (recv_len == 0) {
/* don't print info about zero-length app data buffers */
goto quit;
}
if (status) {
NET_DBG("[%p] Status %d <%s>", ctx, status, RC_STR(status));
goto out;
}
/* Get rid of possible IP headers in the first fragment. */
frag = pkt->frags;
pkt_len = net_pkt_get_len(pkt);
if (recv_len < pkt_len) {
net_buf_pull(frag, pkt_len - recv_len);
net_pkt_set_appdata(pkt, frag->data);
}
NET_DBG("[%p] Received %zd bytes http data", ctx, recv_len);
if (ctx->state == HTTP_STATE_OPEN) {
/* We have active websocket session and there is no longer
* any HTTP traffic in the connection. Give the data to
* application to send.
*/
goto http_only;
}
while (frag) {
/* If this fragment cannot be copied to result buf,
* then parse what we have which will cause the callback to be
* called in function on_body(), and continue copying.
*/
if ((ctx->http.data_len + frag->len) >
ctx->http.request_buf_len) {
if (ctx->state == HTTP_STATE_HEADER_RECEIVED) {
goto http_ready;
}
/* If the caller has not supplied a callback, then
* we cannot really continue if the request buffer
* overflows. Set the data_len to mark how many bytes
* should be needed in the response_buf.
*/
if (http_process_recv(ctx) < 0) {
ctx->http.data_len = recv_len;
goto out;
}
parsed_len =
http_parser_execute(&ctx->http.parser,
&ctx->http.parser_settings,
ctx->http.request_buf +
start,
len);
if (parsed_len <= 0) {
goto fail;
}
ctx->http.data_len = 0;
len = 0;
start = 0;
}
memcpy(ctx->http.request_buf + ctx->http.data_len,
frag->data, frag->len);
ctx->http.data_len += frag->len;
len += frag->len;
frag = frag->frags;
}
out:
parsed_len = http_parser_execute(&ctx->http.parser,
&ctx->http.parser_settings,
ctx->http.request_buf + start,
len);
if (parsed_len < 0) {
fail:
NET_DBG("[%p] Received %zd bytes, only parsed %d "
"bytes (%s %s)",
ctx, recv_len, parsed_len,
http_errno_name(ctx->http.parser.http_errno),
http_errno_description(
ctx->http.parser.http_errno));
}
if (ctx->http.parser.http_errno != HPE_OK) {
http_send_error(ctx, 400, HTTP_STATUS_400_BR, NULL, 0);
} else {
if (ctx->state == HTTP_STATE_HEADER_RECEIVED) {
goto http_ready;
}
http_process_recv(ctx);
}
quit:
http_parser_init(&ctx->http.parser, HTTP_REQUEST);
ctx->http.data_len = 0;
ctx->http.field_values_ctr = 0;
net_pkt_unref(pkt);
return;
http_only:
if (ctx->cb.recv) {
ctx->cb.recv(ctx, pkt, 0, 0, ctx->user_data);
}
return;
http_ready:
http_change_state(ctx, HTTP_STATE_OPEN);
url_connected(ctx, HTTP_CONNECT);
net_pkt_unref(pkt);
}
#if defined(CONFIG_HTTPS)
int http_server_set_tls(struct http_ctx *ctx,
const char *server_banner,
u8_t *personalization_data,
size_t personalization_data_len,
net_app_cert_cb_t cert_cb,
net_app_entropy_src_cb_t entropy_src_cb,
struct k_mem_pool *pool,
k_thread_stack_t *stack,
size_t stack_len)
{
int ret;
if (!ctx->is_tls) {
/* Change the default port if user did not set it */
if (!ctx->server_addr) {
net_sin(&ctx->local)->sin_port =
htons(HTTPS_DEFAULT_PORT);
#if defined(CONFIG_NET_IPV6)
net_sin6(&ctx->app_ctx.ipv6.local)->sin6_port =
htons(HTTPS_DEFAULT_PORT);
#endif
#if defined(CONFIG_NET_IPV4)
net_sin(&ctx->app_ctx.ipv4.local)->sin_port =
htons(HTTPS_DEFAULT_PORT);
#endif
}
ret = net_app_server_tls(&ctx->app_ctx,
ctx->http.request_buf,
ctx->http.request_buf_len,
server_banner,
personalization_data,
personalization_data_len,
cert_cb,
entropy_src_cb,
pool,
stack,
stack_len);
if (ret < 0) {
NET_ERR("Cannot init TLS (%d)", ret);
goto quit;
}
ctx->is_tls = true;
return 0;
}
return -EALREADY;
quit:
net_app_release(&ctx->app_ctx);
return ret;
}
#endif
static int on_header_field(struct http_parser *parser,
const char *at, size_t length)
{
struct http_ctx *ctx = parser->data;
if (ctx->http.field_values_ctr >= CONFIG_HTTP_HEADERS) {
return 0;
}
http_change_state(ctx, HTTP_STATE_RECEIVING_HEADER);
ctx->http.field_values[ctx->http.field_values_ctr].key = at;
ctx->http.field_values[ctx->http.field_values_ctr].key_len = length;
return 0;
}
static int on_header_value(struct http_parser *parser,
const char *at, size_t length)
{
struct http_ctx *ctx = parser->data;
if (ctx->http.field_values_ctr >= CONFIG_HTTP_HEADERS) {
return 0;
}
ctx->http.field_values[ctx->http.field_values_ctr].value = at;
ctx->http.field_values[ctx->http.field_values_ctr].value_len = length;
ctx->http.field_values_ctr++;
return 0;
}
static int on_url(struct http_parser *parser, const char *at, size_t length)
{
struct http_ctx *ctx = parser->data;
ctx->http.url = at;
ctx->http.url_len = length;
http_change_state(ctx, HTTP_STATE_WAITING_HEADER);
http_server_conn_add(ctx);
return 0;
}
static int on_headers_complete(struct http_parser *parser)
{
ARG_UNUSED(parser);
return 0;
}
static int init_http_parser(struct http_ctx *ctx)
{
memset(ctx->http.field_values, 0, sizeof(ctx->http.field_values));
ctx->http.parser_settings.on_header_field = on_header_field;
ctx->http.parser_settings.on_header_value = on_header_value;
ctx->http.parser_settings.on_url = on_url;
ctx->http.parser_settings.on_headers_complete = on_headers_complete;
http_parser_init(&ctx->http.parser, HTTP_REQUEST);
ctx->http.parser.data = ctx;
return 0;
}
static inline void new_server(struct http_ctx *ctx,
const char *server_banner,
const struct sockaddr *addr)
{
#if defined(CONFIG_NET_DEBUG_HTTP) && (CONFIG_SYS_LOG_NET_LEVEL > 2)
#if defined(CONFIG_NET_IPV6)
#define PORT_STR sizeof("[]:xxxxx")
char buf[NET_IPV6_ADDR_LEN + PORT_STR];
#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
#define PORT_STR sizeof(":xxxxx")
char buf[NET_IPV4_ADDR_LEN + PORT_STR];
#endif
if (addr) {
NET_INFO("%s %s (%p)", server_banner,
sprint_ipaddr(buf, sizeof(buf), addr), ctx);
} else {
NET_INFO("%s (%p)", server_banner, ctx);
}
#endif /* CONFIG_NET_DEBUG_HTTP */
}
static void init_net(struct http_ctx *ctx,
struct sockaddr *server_addr,
u16_t port)
{
memset(&ctx->local, 0, sizeof(ctx->local));
if (server_addr) {
memcpy(&ctx->local, server_addr, sizeof(ctx->local));
} else {
ctx->local.sa_family = AF_UNSPEC;
net_sin(&ctx->local)->sin_port = htons(port);
}
}
int http_server_init(struct http_ctx *ctx,
struct http_server_urls *urls,
struct sockaddr *server_addr,
u8_t *request_buf,
size_t request_buf_len,
const char *server_banner,
void *user_data)
{
int ret = 0;
if (!ctx) {
return -EINVAL;
}
if (ctx->is_init) {
return -EALREADY;
}
if (!request_buf || request_buf_len == 0) {
NET_ERR("Request buf must be set");
return -EINVAL;
}
memset(ctx, 0, sizeof(*ctx));
init_net(ctx, server_addr, HTTP_DEFAULT_PORT);
if (server_banner) {
new_server(ctx, server_banner, server_addr);
}
/* Timeout for network buffer allocations */
ctx->timeout = BUF_ALLOC_TIMEOUT;
ctx->http.request_buf = request_buf;
ctx->http.request_buf_len = request_buf_len;
ctx->http.urls = urls;
ctx->http.data_len = 0;
ctx->user_data = user_data;
ctx->server_addr = server_addr;
ret = net_app_init_tcp_server(&ctx->app_ctx,
&ctx->local,
HTTP_DEFAULT_PORT,
ctx);
if (ret < 0) {
NET_ERR("Cannot create http server (%d)", ret);
return ret;
}
ret = net_app_set_cb(&ctx->app_ctx, NULL, http_received,
http_data_sent, http_closed);
if (ret < 0) {
NET_ERR("Cannot set callbacks (%d)", ret);
goto quit;
}
init_http_parser(ctx);
ctx->is_init = true;
return 0;
quit:
net_app_release(&ctx->app_ctx);
return ret;
}
int http_server_enable(struct http_ctx *ctx)
{
int ret;
NET_ASSERT(ctx);
net_app_server_enable(&ctx->app_ctx);
ret = net_app_listen(&ctx->app_ctx);
if (ret < 0) {
NET_ERR("Cannot wait connection (%d)", ret);
return false;
}
return 0;
}
int http_server_disable(struct http_ctx *ctx)
{
NET_ASSERT(ctx);
net_app_server_disable(&ctx->app_ctx);
return 0;
}

1856
subsys/net/lib/http/http_client.c

File diff suppressed because it is too large Load Diff

1751
subsys/net/lib/http/http_server.c

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save