| /* |
| * 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); |
| } |