Browse Source
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
11 changed files with 1975 additions and 6808 deletions
@ -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 */ |
@ -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; |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue