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>
This commit is contained in:
parent
8dd852dc92
commit
d1675bf3e6
11 changed files with 2061 additions and 6894 deletions
1082
include/net/http.h
1082
include/net/http.h
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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)
|
|||
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[])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|||
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
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue