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