142 lines
4.3 KiB
C
142 lines
4.3 KiB
C
/**
|
|
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#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;
|
|
}
|