291 lines
5.4 KiB
C
291 lines
5.4 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define LOG_MODULE_NAME net_http
|
|
#define NET_LOG_LEVEL CONFIG_HTTP_LOG_LEVEL
|
|
|
|
#include <zephyr.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <version.h>
|
|
#include <misc/printk.h>
|
|
|
|
#include <net/net_core.h>
|
|
#include <net/net_ip.h>
|
|
#include <net/http.h>
|
|
|
|
int http_set_cb(struct http_ctx *ctx,
|
|
http_connect_cb_t connect_cb,
|
|
http_recv_cb_t recv_cb,
|
|
http_send_cb_t send_cb,
|
|
http_close_cb_t close_cb)
|
|
{
|
|
if (!ctx) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ctx->is_init) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
ctx->cb.connect = connect_cb;
|
|
ctx->cb.recv = recv_cb;
|
|
ctx->cb.send = send_cb;
|
|
ctx->cb.close = close_cb;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int http_close(struct http_ctx *ctx)
|
|
{
|
|
if (!ctx) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ctx->is_init) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
http_send_flush(ctx, NULL);
|
|
if (ctx->pending) {
|
|
net_pkt_unref(ctx->pending);
|
|
ctx->pending = NULL;
|
|
}
|
|
|
|
#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_NET_DEBUG_HTTP_CONN)
|
|
if (!ctx->is_client) {
|
|
http_server_conn_del(ctx);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_WEBSOCKET)
|
|
if (ctx->websocket.pending) {
|
|
net_pkt_unref(ctx->websocket.pending);
|
|
ctx->websocket.pending = NULL;
|
|
}
|
|
|
|
ctx->websocket.data_waiting = 0;
|
|
#endif
|
|
|
|
return net_app_close(&ctx->app_ctx);
|
|
}
|
|
|
|
int http_release(struct http_ctx *ctx)
|
|
{
|
|
if (!ctx) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ctx->is_init) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
ctx->is_tls = false;
|
|
|
|
#if defined(CONFIG_HTTP_SERVER) && defined(CONFIG_NET_DEBUG_HTTP_CONN)
|
|
if (!ctx->is_client) {
|
|
http_server_conn_del(ctx);
|
|
http_server_disable(ctx);
|
|
}
|
|
#endif
|
|
|
|
if (ctx->pending) {
|
|
net_pkt_unref(ctx->pending);
|
|
ctx->pending = NULL;
|
|
}
|
|
|
|
ctx->is_init = false;
|
|
|
|
return net_app_release(&ctx->app_ctx);
|
|
}
|
|
|
|
int http_send_msg_raw(struct http_ctx *ctx, struct net_pkt *pkt,
|
|
void *user_send_data)
|
|
{
|
|
int ret;
|
|
|
|
NET_DBG("[%p] Sending %zd bytes data", ctx, net_pkt_get_len(pkt));
|
|
|
|
ret = net_app_send_pkt(&ctx->app_ctx, pkt, NULL, 0, 0,
|
|
user_send_data);
|
|
if (!ret) {
|
|
/* We must let the system to send the packet, otherwise TCP
|
|
* might timeout before the packet is actually sent. This is
|
|
* easily seen if the application calls this functions many
|
|
* times in a row.
|
|
*/
|
|
k_yield();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline struct net_pkt *get_net_pkt(struct http_ctx *ctx,
|
|
const struct sockaddr *dst)
|
|
{
|
|
if (!dst) {
|
|
return net_app_get_net_pkt(&ctx->app_ctx, AF_UNSPEC,
|
|
ctx->timeout);
|
|
}
|
|
|
|
return net_app_get_net_pkt_with_dst(&ctx->app_ctx, dst, ctx->timeout);
|
|
}
|
|
|
|
int http_prepare_and_send(struct http_ctx *ctx,
|
|
const char *payload,
|
|
size_t payload_len,
|
|
const struct sockaddr *dst,
|
|
void *user_send_data)
|
|
{
|
|
size_t added;
|
|
int ret;
|
|
|
|
do {
|
|
if (!ctx->pending) {
|
|
ctx->pending = get_net_pkt(ctx, dst);
|
|
if (!ctx->pending) {
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
ret = net_pkt_append(ctx->pending, payload_len, payload,
|
|
ctx->timeout);
|
|
if (!ret || ret > payload_len) {
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
added = ret;
|
|
|
|
payload_len -= added;
|
|
if (payload_len) {
|
|
payload += added;
|
|
/* Not all data could be added, send what we have now
|
|
* and allocate new stuff to be sent.
|
|
*/
|
|
ret = http_send_flush(ctx, user_send_data);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
} while (payload_len);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (ctx->pending) {
|
|
net_pkt_unref(ctx->pending);
|
|
ctx->pending = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int http_send_flush(struct http_ctx *ctx, void *user_send_data)
|
|
{
|
|
int ret;
|
|
|
|
if (!ctx->pending) {
|
|
return 0;
|
|
}
|
|
|
|
ret = http_send_msg_raw(ctx, ctx->pending, user_send_data);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ctx->pending = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int http_send_chunk(struct http_ctx *ctx, const char *buf, size_t len,
|
|
const struct sockaddr *dst, void *user_send_data)
|
|
{
|
|
char chunk_header[16];
|
|
int ret;
|
|
|
|
if (!buf) {
|
|
len = 0;
|
|
}
|
|
|
|
snprintk(chunk_header, sizeof(chunk_header), "%x" HTTP_CRLF,
|
|
(unsigned int)len);
|
|
|
|
ret = http_prepare_and_send(ctx, chunk_header, strlen(chunk_header),
|
|
dst, user_send_data);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (len) {
|
|
ret = http_prepare_and_send(ctx, buf, len, dst, user_send_data);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = http_prepare_and_send(ctx, HTTP_CRLF, sizeof(HTTP_CRLF) - 1, dst,
|
|
user_send_data);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _http_add_header(struct http_ctx *ctx, s32_t timeout,
|
|
const char *name, const char *value,
|
|
const struct sockaddr *dst,
|
|
void *user_send_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = http_prepare_and_send(ctx, name, strlen(name), dst,
|
|
user_send_data);
|
|
if (value && ret >= 0) {
|
|
ret = http_prepare_and_send(ctx, ": ", strlen(": "), dst,
|
|
user_send_data);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = http_prepare_and_send(ctx, value, strlen(value), dst,
|
|
user_send_data);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = http_prepare_and_send(ctx, HTTP_CRLF, strlen(HTTP_CRLF),
|
|
dst, user_send_data);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int http_add_header(struct http_ctx *ctx, const char *field,
|
|
const struct sockaddr *dst,
|
|
void *user_send_data)
|
|
{
|
|
return _http_add_header(ctx, ctx->timeout, field, NULL, dst,
|
|
user_send_data);
|
|
}
|
|
|
|
int http_add_header_field(struct http_ctx *ctx, const char *name,
|
|
const char *value,
|
|
const struct sockaddr *dst,
|
|
void *user_send_data)
|
|
{
|
|
return _http_add_header(ctx, ctx->timeout, name, value, dst,
|
|
user_send_data);
|
|
}
|