-
Notifications
You must be signed in to change notification settings - Fork 979
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented using the lwip http client. Fixes #1386
- Loading branch information
1 parent
9f45e3c
commit aeae442
Showing
3 changed files
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/** | ||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd. | ||
* | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <string.h> | ||
#include "pico/http_client_util.h" | ||
#include "pico/async_context.h" | ||
#include "lwip/altcp.h" | ||
#include "lwip/altcp_tls.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); | ||
PICO_HTTP_REQUEST_T *req = (PICO_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); | ||
PICO_HTTP_REQUEST_T *req = (PICO_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); | ||
PICO_HTTP_REQUEST_T *req = (PICO_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); | ||
PICO_HTTP_REQUEST_T *req = (PICO_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, PICO_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, PICO_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; | ||
} |
126 changes: 126 additions & 0 deletions
126
src/rp2_common/pico_lwip/include/pico/http_client_util.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/** | ||
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd. | ||
* | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
#ifndef PICO_HTTP_CLIENT_UTIL_H | ||
#define PICO_HTTP_CLIENT_UTIL_H | ||
|
||
#include "lwip/apps/http_client.h" | ||
|
||
/*! \brief Parameters used to make HTTP request | ||
* \ingroup pico_lwip | ||
*/ | ||
typedef struct PICO_HTTP_REQUEST { | ||
/*! | ||
* The name of the host, e.g. www.raspberrypi.com | ||
*/ | ||
const char *hostname; | ||
/*! | ||
* The url to request, e.g. /favicon.ico | ||
*/ | ||
const char *url; | ||
/*! | ||
* Function to callback with headers, can be null | ||
* @see httpc_headers_done_fn | ||
*/ | ||
httpc_headers_done_fn headers_fn; | ||
/*! | ||
* Function to callback with results from the server, can be null | ||
* @see altcp_recv_fn | ||
*/ | ||
altcp_recv_fn recv_fn; | ||
/*! | ||
* Function to callback with final results of the request, can be null | ||
* @see httpc_result_fn | ||
*/ | ||
httpc_result_fn result_fn; | ||
/*! | ||
* Callback to pass to calback functions | ||
*/ | ||
void *callback_arg; | ||
/*! | ||
* The port to use. A default port is chosen if this is set to zero | ||
*/ | ||
uint16_t port; | ||
#if LWIP_ALTCP && LWIP_ALTCP_TLS | ||
/*! | ||
* TLS configuration, can be null or set to a correctly configured tls configuration. | ||
* e.g altcp_tls_create_config_client(NULL, 0) would use https without a certificate | ||
*/ | ||
struct altcp_tls_config *tls_config; | ||
/*! | ||
* TLS allocator, used internall for setting TLS server name indication | ||
*/ | ||
altcp_allocator_t tls_allocator; | ||
#endif | ||
/*! | ||
* LwIP HTTP client settings | ||
*/ | ||
httpc_connection_t settings; | ||
/*! | ||
* Flag to indicate when the request is complete | ||
*/ | ||
int complete; | ||
/*! | ||
* Overall result of http request, only valid when complete is set | ||
*/ | ||
httpc_result_t result; | ||
|
||
} PICO_HTTP_REQUEST_T; | ||
|
||
struct async_context; | ||
|
||
/*! \brief Perform a http request asynchronously | ||
* \ingroup pico_lwip | ||
* | ||
* Perform the http request asynchronously | ||
* | ||
* @param context async context | ||
* @param req HTTP request parameters. As a minimum this should be initialised to zero with hostname and url set to valid values | ||
* @return If zero is returned the request has been made and is complete when \em req->complete is true or the result callback has been called. | ||
* A non-zero return value indicates an error. | ||
* | ||
* @see async_context | ||
*/ | ||
int http_client_request_async(struct async_context *context, PICO_HTTP_REQUEST_T *req); | ||
|
||
/*! \brief Perform a http request synchronously | ||
* \ingroup pico_lwip | ||
* | ||
* Perform the http request synchronously | ||
* | ||
* @param context async context | ||
* @param req HTTP request parameters. As a minimum this should be initialised to zero with hostname and url set to valid values | ||
* @param result Returns the overall result of the http request when complete. Zero indicates success. | ||
*/ | ||
int http_client_request_sync(struct async_context *context, PICO_HTTP_REQUEST_T *req); | ||
|
||
/*! \brief A http header callback that can be passed to \em http_client_init or \em http_client_init_secure | ||
* \ingroup pico_http_client | ||
* | ||
* An implementation of the http header callback which just prints headers to stdout | ||
* | ||
* @param arg argument specified on initialisation | ||
* @param hdr header pbuf(s) (may contain data also) | ||
* @param hdr_len length of the headers in 'hdr' | ||
* @param content_len content length as received in the headers (-1 if not received) | ||
* @return if != zero is returned, the connection is aborted | ||
*/ | ||
err_t http_client_header_print_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len); | ||
|
||
/*! \brief A http recv callback that can be passed to http_client_init or http_client_init_secure | ||
* \ingroup pico_http_client | ||
* | ||
* An implementation of the http recv callback which just prints the http body to stdout | ||
* | ||
* @param arg argument specified on initialisation | ||
* @param conn http client connection | ||
* @param p body pbuf(s) | ||
* @param err Error code in the case of an error | ||
* @return if != zero is returned, the connection is aborted | ||
*/ | ||
err_t http_client_receive_print_fn(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err); | ||
|
||
#endif |