Files
tactility/Modules/http-module/include/http/download.h
Ken Van Hoeylandt bcbf18e363 Implement http-module (#637)
Create http-module and implement it in App Hub and App Hub Details apps.
2026-08-28 23:09:59 +02:00

144 lines
6.5 KiB
C

// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <tactility/concurrent/task_event_group.h>
#include <tactility/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Identifies how a download finished, as delivered to http_download_poll(). */
enum HttpDownloadEventType {
HTTP_DOWNLOAD_EVENT_SUCCESS, // no data
HTTP_DOWNLOAD_EVENT_ERROR, // struct HttpDownloadErrorEvent
HTTP_DOWNLOAD_EVENT_CANCELLED, // no data - see http_download_cancel()
};
#define HTTP_DOWNLOAD_ERROR_MESSAGE_MAX_LEN 64
/** Data for HTTP_DOWNLOAD_EVENT_ERROR. */
struct HttpDownloadErrorEvent {
char message[HTTP_DOWNLOAD_ERROR_MESSAGE_MAX_LEN];
};
/** Event delivered through http_download_poll() once a download's task finishes. */
struct HttpDownloadEvent {
enum HttpDownloadEventType type;
/** Microseconds since boot, from get_micros_since_boot(). */
uint64_t timestamp;
/** The server's HTTP response status code, or 0 if no response was ever received
* (e.g. failed to open the connection, or platform doesn't support downloads). */
int32_t status_code;
/** Valid only when type == HTTP_DOWNLOAD_EVENT_ERROR. */
struct HttpDownloadErrorEvent error;
};
/** Internal, refcounted state shared between a subscription and its download task - lets
* http_download_unsubscribe() sever the link safely at any time, even mid-download (see its
* own doc). Defined only in download.cpp; opaque here like Module::internal (tactility/module.h). */
struct HttpDownloadLink;
/**
* Caller-owned subscription node for one download's outcome, registered with
* http_download_subscribe() and polled with http_download_poll() - same subscribe/await/poll
* shape as TactilityKernel's system_event and app-module's app_event. Unlike those (which
* multiplex many emitters/subscribers by type or app instance), one subscription always belongs
* to exactly one download and receives exactly one terminal event.
* @warning Fields other than `bit` are for internal use only; do not read or write them directly.
*/
struct HttpDownloadSubscription {
/** Set by http_download_subscribe(). Read-only for the caller: OR it into a
* task_event_group_wait() mask (alongside other subscriptions sharing the same
* `internal.event_group`) to block on this subscription and other event sources with one
* call. */
uint32_t bit;
struct {
/** Caller-owned, borrowed; set by http_download_subscribe(). */
struct TaskEventGroup* event_group;
struct HttpDownloadEvent event;
bool pending;
struct HttpDownloadLink* link;
} internal;
};
/**
* Register a poll subscription for one download's outcome.
* @warning Does not work in ISR context.
* @warning Must be called before http_download_start(), so the download task can never finish
* before a subscriber exists to notify.
* @param[in,out] sub subscription to register; caller owns the storage and must keep it alive
* (and stationary) until http_download_unsubscribe()
* @param[in] event_group caller-owned group to wait on; must outlive @a sub. To block for the
* outcome, call task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit
* into the mask, or use _wait_any() to include every subscription sharing it), then poll with
* http_download_poll().
* @retval ERROR_NONE on success
* @retval ERROR_RESOURCE @a event_group has no free bits left to claim; @a sub was not registered
* @retval ERROR_OUT_OF_MEMORY failed to allocate @a sub's internal link; @a sub was not registered
*/
error_t http_download_subscribe(struct HttpDownloadSubscription* sub, struct TaskEventGroup* event_group);
/**
* Remove a previously registered subscription. Safe to call at any time, including while the
* download is still in flight (e.g. right after http_download_cancel()) - the download task
* never touches @a sub or its event_group again once this returns, so both may be destructed
* immediately afterward without waiting for the download to actually finish.
* @warning Does not work in ISR context.
* @return ERROR_NONE on success, ERROR_NOT_FOUND if @a sub isn't currently subscribed
*/
error_t http_download_unsubscribe(struct HttpDownloadSubscription* sub);
/**
* Non-blocking: check whether the download's terminal event has arrived.
* @warning Never blocks. To wait, block in task_event_group_wait()/task_event_group_wait_any()
* on @a sub's bit first (see http_download_subscribe()), then call this.
* @param[in,out] sub subscription to poll, as passed to http_download_subscribe()
* @param[out] out_event set to the download's outcome
* @retval ERROR_NONE the terminal event arrived - it is copied into @a out_event
* @retval ERROR_TIMEOUT the download hasn't finished yet
*/
error_t http_download_poll(struct HttpDownloadSubscription* sub, struct HttpDownloadEvent* out_event);
/**
* Starts a download on its own dedicated task: issues a GET request for @a url, verifying the
* server against the PEM certificate at @a cert_path, and writes the response body to
* @a target_path.
* @param[in] url the URL to download
* @param[in] cert_path path to a PEM certificate file used to verify the server
* @param[in] target_path path to write the downloaded file to
* @param[in] sub subscription to notify on completion, already registered via
* http_download_subscribe(); must stay alive (and stationary) at least until
* http_download_unsubscribe() is called
* @retval ERROR_NONE the download task was started
* @retval ERROR_INVALID_ARGUMENT @a sub is not currently subscribed
* @retval ERROR_OUT_OF_MEMORY failed to allocate the download task
*/
error_t http_download_start(
const char* url,
const char* cert_path,
const char* target_path,
struct HttpDownloadSubscription* sub
);
/**
* Request cancellation of @a sub's in-flight download. Best-effort and asynchronous: the
* download task only checks for this periodically (between I/O steps), so it keeps running for
* a short while after this returns. If you still want the outcome, poll as usual - it finishes
* with HTTP_DOWNLOAD_EVENT_CANCELLED. If you don't, http_download_unsubscribe() may be called
* right away instead of waiting for that - see its own doc.
* @param[in,out] sub subscription for the download to cancel, as passed to
* http_download_subscribe()
* @retval ERROR_NONE the cancellation request was recorded
* @retval ERROR_INVALID_STATE @a sub is not currently subscribed
*/
error_t http_download_cancel(struct HttpDownloadSubscription* sub);
#ifdef __cplusplus
}
#endif