From ed366c9638fb2ef3b301d36fb48582d0406a23d5 Mon Sep 17 00:00:00 2001 From: PoliEcho Date: Mon, 8 Sep 2025 19:58:58 +0200 Subject: [PATCH] add http client from pico example --- http_client_util.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 http_client_util.c diff --git a/http_client_util.c b/http_client_util.c new file mode 100644 index 0000000..d67f598 --- /dev/null +++ b/http_client_util.c @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/async_context.h" +#include "lwip/altcp.h" +#include "lwip/altcp_tls.h" +#include "example_http_client_util.h" + +#ifndef HTTP_INFO +#define HTTP_INFO printf +#endif + +#ifndef HTTP_INFOC +#define HTTP_INFOC putchar +#endif + +#ifndef HTTP_INFOC +#define HTTP_INFOC putchar +#endif + +#ifndef HTTP_DEBUG +#ifdef NDEBUG +#define HTTP_DEBUG +#else +#define HTTP_DEBUG printf +#endif +#endif + +#ifndef HTTP_ERROR +#define HTTP_ERROR printf +#endif + +// Print headers to stdout +err_t http_client_header_print_fn(__unused httpc_state_t *connection, __unused void *arg, struct pbuf *hdr, u16_t hdr_len, __unused u32_t content_len) { + HTTP_INFO("\nheaders %u\n", hdr_len); + u16_t offset = 0; + while (offset < hdr->tot_len && offset < hdr_len) { + char c = (char)pbuf_get_at(hdr, offset++); + HTTP_INFOC(c); + } + return ERR_OK; +} + +// Print body to stdout +err_t http_client_receive_print_fn(__unused void *arg, __unused struct altcp_pcb *conn, struct pbuf *p, err_t err) { + HTTP_INFO("\ncontent err %d\n", err); + u16_t offset = 0; + while (offset < p->tot_len) { + char c = (char)pbuf_get_at(p, offset++); + HTTP_INFOC(c); + } + return ERR_OK; +} + + +static err_t internal_header_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len) { + assert(arg); + EXAMPLE_HTTP_REQUEST_T *req = (EXAMPLE_HTTP_REQUEST_T*)arg; + if (req->headers_fn) { + return req->headers_fn(connection, req->callback_arg, hdr, hdr_len, content_len); + } + return ERR_OK; +} + +static err_t internal_recv_fn(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err) { + assert(arg); + EXAMPLE_HTTP_REQUEST_T *req = (EXAMPLE_HTTP_REQUEST_T*)arg; + if (req->recv_fn) { + return req->recv_fn(req->callback_arg, conn, p, err); + } + return ERR_OK; +} + +static void internal_result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err) { + assert(arg); + EXAMPLE_HTTP_REQUEST_T *req = (EXAMPLE_HTTP_REQUEST_T*)arg; + HTTP_DEBUG("result %d len %u server_response %u err %d\n", httpc_result, rx_content_len, srv_res, err); + req->complete = true; + req->result = httpc_result; + if (req->result_fn) { + req->result_fn(req->callback_arg, httpc_result, rx_content_len, srv_res, err); + } +} + +// Override altcp_tls_alloc to set sni +static struct altcp_pcb *altcp_tls_alloc_sni(void *arg, u8_t ip_type) { + assert(arg); + EXAMPLE_HTTP_REQUEST_T *req = (EXAMPLE_HTTP_REQUEST_T*)arg; + struct altcp_pcb *pcb = altcp_tls_alloc(req->tls_config, ip_type); + if (!pcb) { + HTTP_ERROR("Failed to allocate PCB\n"); + return NULL; + } + mbedtls_ssl_set_hostname(altcp_tls_context(pcb), req->hostname); + return pcb; +} + +// Make a http request, complete when req->complete returns true +int http_client_request_async(async_context_t *context, EXAMPLE_HTTP_REQUEST_T *req) { +#if LWIP_ALTCP + const uint16_t default_port = req->tls_config ? 443 : 80; + if (req->tls_config) { + if (!req->tls_allocator.alloc) { + req->tls_allocator.alloc = altcp_tls_alloc_sni; + req->tls_allocator.arg = req; + } + req->settings.altcp_allocator = &req->tls_allocator; + } +#else + const uint16_t default_port = 80; +#endif + req->complete = false; + req->settings.headers_done_fn = req->headers_fn ? internal_header_fn : NULL; + req->settings.result_fn = internal_result_fn; + async_context_acquire_lock_blocking(context); + err_t ret = httpc_get_file_dns(req->hostname, req->port ? req->port : default_port, req->url, &req->settings, internal_recv_fn, req, NULL); + async_context_release_lock(context); + if (ret != ERR_OK) { + HTTP_ERROR("http request failed: %d", ret); + } + return ret; +} + +// Make a http request and only return when it has completed. Returns true on success +int http_client_request_sync(async_context_t *context, EXAMPLE_HTTP_REQUEST_T *req) { + assert(req); + int ret = http_client_request_async(context, req); + if (ret != 0) { + return ret; + } + while(!req->complete) { + async_context_poll(context); + async_context_wait_for_work_ms(context, 1000); + } + return req->result; +}