blob: c075aba1a95f1373857a323b6cde53a14a8f8909 [file] [log] [blame]
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_NET_DEBUG_HTTP)
#define SYS_LOG_DOMAIN "http"
#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 <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
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;
}
int http_prepare_and_send(struct http_ctx *ctx,
const char *payload,
size_t payload_len,
void *user_send_data)
{
size_t added;
int ret;
do {
if (!ctx->pending) {
ctx->pending = net_app_get_net_pkt(&ctx->app_ctx,
AF_UNSPEC,
ctx->timeout);
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,
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),
user_send_data);
if (ret < 0) {
return ret;
}
if (len) {
ret = http_prepare_and_send(ctx, buf, len, user_send_data);
if (ret < 0) {
return ret;
}
}
ret = http_prepare_and_send(ctx, HTTP_CRLF, sizeof(HTTP_CRLF),
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,
void *user_send_data)
{
int ret;
ret = http_prepare_and_send(ctx, name, strlen(name), user_send_data);
if (value && ret >= 0) {
ret = http_prepare_and_send(ctx, ": ", strlen(": "),
user_send_data);
if (ret < 0) {
goto out;
}
ret = http_prepare_and_send(ctx, value, strlen(value),
user_send_data);
if (ret < 0) {
goto out;
}
ret = http_prepare_and_send(ctx, HTTP_CRLF, strlen(HTTP_CRLF),
user_send_data);
if (ret < 0) {
goto out;
}
}
out:
return ret;
}
int http_add_header(struct http_ctx *ctx, const char *field,
void *user_send_data)
{
return _http_add_header(ctx, ctx->timeout, field, NULL, user_send_data);
}
int http_add_header_field(struct http_ctx *ctx, const char *name,
const char *value, void *user_send_data)
{
return _http_add_header(ctx, ctx->timeout, name, value, user_send_data);
}