Files
ESP32_Serial_Swiss_Army_Knife/src/web_serial_transport.h
T

151 lines
5.4 KiB
C

/* SPDX-License-Identifier: GPL-3.0-only */
/* Authenticated, bounded WebSocket transport for the serial session broker. */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "esp_http_server.h"
#include "session_broker.h"
#include "user_database.h"
#include "web_session_store.h"
#ifdef __cplusplus
extern "C" {
#endif
#define WEB_SERIAL_TRANSPORT_MAX_SESSIONS 2U
#define WEB_SERIAL_TRANSPORT_MAX_TICKETS 4U
#define WEB_SERIAL_TRANSPORT_TICKET_LENGTH 32U
#define WEB_SERIAL_TRANSPORT_TICKET_CAPACITY \
(WEB_SERIAL_TRANSPORT_TICKET_LENGTH + 1U)
#define WEB_SERIAL_TRANSPORT_TICKET_LIFETIME_SECONDS 30U
#define WEB_SERIAL_TRANSPORT_MAX_RX_PAYLOAD 1024U
#define WEB_SERIAL_TRANSPORT_TX_PAYLOAD_SIZE 512U
#define WEB_SERIAL_TRANSPORT_TICKET_URI "/api/ws-ticket"
#define WEB_SERIAL_TRANSPORT_WS_URI "/ws/serial"
#define WEB_SERIAL_TRANSPORT_TICKET_QUERY_KEY "ticket"
typedef struct {
uint64_t tickets_issued;
uint64_t tickets_consumed;
uint64_t tickets_rejected;
uint64_t tickets_expired;
uint64_t connections;
uint64_t connection_failures;
uint64_t disconnections;
uint64_t service_start_failures;
uint64_t broker_failures;
uint64_t writer_requests;
uint64_t writer_grants;
uint64_t writer_denials;
uint64_t writer_releases;
uint64_t writer_revocations;
uint64_t rx_ws_frames_accepted;
uint64_t rx_ws_frames_rejected;
uint64_t rx_ws_bytes_accepted;
uint64_t rx_ws_bytes_rejected;
uint64_t tx_binary_frames;
uint64_t tx_binary_bytes;
uint64_t tx_control_frames;
uint64_t tx_control_bytes;
uint64_t send_failures;
uint64_t queue_failures;
uint64_t protocol_errors;
uint64_t close_requests;
} web_serial_transport_counters_t;
typedef struct {
bool active;
bool principal_valid;
bool writer;
bool tx_pending;
bool close_requested;
int socket_fd;
uint32_t generation;
session_broker_client_id_t broker_client_id;
user_role_t user_role;
user_auth_method_t auth_method;
char username[USER_DATABASE_USERNAME_CAPACITY + 1U];
} web_serial_transport_session_snapshot_t;
typedef struct {
bool initialized;
bool server_attached;
uint32_t active_sessions;
uint32_t active_tickets;
web_serial_transport_session_snapshot_t
sessions[WEB_SERIAL_TRANSPORT_MAX_SESSIONS];
web_serial_transport_counters_t counters;
} web_serial_transport_snapshot_t;
/*
* Allocate no per-session heap objects and start the permanent transport task.
* CONFIG_HTTPD_WS_SUPPORT must be enabled. CONFIG_HTTPD_QUEUE_WORK_BLOCKING must
* be disabled because that IDF mode can wait forever inside httpd_queue_work().
*/
esp_err_t web_serial_transport_init(void);
/* Attach after httpd start; detach as part of stopping that same server. */
esp_err_t web_serial_transport_attach_server(httpd_handle_t server);
esp_err_t web_serial_transport_detach_server(httpd_handle_t server);
/*
* Mint a one-time bearer ticket bound to a current authenticated principal and
* originating web-session ID (zero only for the shipped Basic path). The
* principal is copied; the output is exactly 32 Base64URL characters plus a
* terminator and expires after 30 monotonic seconds. Never log or persist it.
*/
esp_err_t web_serial_transport_mint_ticket(const user_principal_t *principal,
web_session_id_t web_session_id,
char *ticket, size_t capacity);
/*
* Convenience POST response helper for /api/ws-ticket. Authentication is
* intentionally outside this module: pass the principal returned by successful
* authentication, and its session ID (zero for Basic). Cookie callers must also
* enforce CSRF/Origin policy. Register as HTTP_POST, not as a public handler.
*/
esp_err_t web_serial_transport_handle_authenticated_ticket_request(
httpd_req_t *request, const user_principal_t *principal,
web_session_id_t web_session_id);
/*
* Handler for /ws/serial. Register as HTTP_GET with is_websocket=true and
* handle_ws_control_frames=false. The initial upgraded GET authenticates the
* ticket; later invocations process one complete data frame.
*/
esp_err_t web_serial_transport_ws_handler(httpd_req_t *request);
/* Trusted future cookie-authorized upgrade caller; must validate cookie/Origin
* first. Zero identifies only the shipped Basic path, never a cookie fallback. */
esp_err_t web_serial_transport_session_ws_handler(httpd_req_t *request,
web_session_id_t web_session_id);
/* Invalidates the store first, then marks only matching tickets/slots for owner
* cleanup. Safe to repeat after either store or transport slot reuse. */
esp_err_t web_serial_transport_revoke_web_session(web_session_id_t id);
esp_err_t web_serial_transport_get_snapshot(
web_serial_transport_snapshot_t *snapshot);
/* Clearing counters does not alter tickets, sessions, ownership, or queued data. */
esp_err_t web_serial_transport_clear_counters(void);
/* Invalidate cookie records and tickets/sockets for one username (also after
* deletion), or all accounts. Store invalidation occurs even if serial init
* failed; these do not touch the HTTPD-owned Basic cache, which rechecks DB. */
esp_err_t web_serial_transport_revoke_user(const uint8_t *username,
size_t username_length);
esp_err_t web_serial_transport_revoke_sessions(void);
#ifdef __cplusplus
}
#endif