189 lines
7.2 KiB
C
189 lines
7.2 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
|
|
* nonzero originating web-session ID. 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. 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);
|
|
|
|
/*
|
|
* Frame callback installed by the HTTPD adapter after authorized admission.
|
|
* Do not register directly: the initial HTTP GET must pass cookie/Origin policy
|
|
* and call the session handler below before any 101 response.
|
|
*/
|
|
esp_err_t web_serial_transport_ws_handler(httpd_req_t *request);
|
|
/* Trusted cookie-authorized upgrade caller; validate cookie/Origin first.
|
|
* Zero is invalid for initial admission; no Basic fallback exists. */
|
|
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);
|
|
|
|
typedef struct {
|
|
uint64_t count, sum_us, max_us;
|
|
} web_serial_performance_timing_t;
|
|
|
|
typedef struct {
|
|
bool active, pending, measured_pending, executing, saturated;
|
|
int socket_fd;
|
|
uint32_t generation;
|
|
session_broker_client_id_t broker_client_id;
|
|
uint64_t pending_age_us;
|
|
uint64_t queued_frames, queued_bytes, queue_errors;
|
|
uint64_t sent_frames, sent_bytes, send_errors, retired;
|
|
web_serial_performance_timing_t queue_wait, send_call;
|
|
web_serial_performance_timing_t completion_attempt, completion_nonempty;
|
|
web_serial_performance_timing_t completion_first_nonempty;
|
|
} web_serial_performance_session_t;
|
|
|
|
typedef struct {
|
|
bool enabled, epoch_exhausted;
|
|
uint32_t epoch;
|
|
web_serial_performance_session_t sessions[WEB_SERIAL_TRANSPORT_MAX_SESSIONS];
|
|
} web_serial_performance_snapshot_t;
|
|
|
|
/* Binary TX only; no broker/HTTPD queries, heap scans, or sensitive identities.
|
|
* queued_* counts transport reservations, including reported queue failures;
|
|
* sent_* counts successful send-call returns. send_errors includes owner-context
|
|
* rejection; send_call timings count only actual API calls. queue_wait includes
|
|
* owned callbacks retired without sending. Pending age is since reservation-path
|
|
* entry, including an executing send; unavailable epochs are explicitly marked.
|
|
* All timing endpoints are local monotonic estimates, never peer acknowledgments.
|
|
* Disable freezes aggregates; enable resumes them. Every toggle/clear fences
|
|
* in-flight samples. Clear preserves enabled state. Epoch exhaustion fails closed.
|
|
* Nonempty completion intervals include idle; first_nonempty requires the first
|
|
* subsequent read to return data (not proof of backlog at send completion). */
|
|
esp_err_t web_serial_performance_enable(bool enabled);
|
|
esp_err_t web_serial_performance_clear(void);
|
|
esp_err_t web_serial_performance_snapshot(web_serial_performance_snapshot_t *out);
|
|
|
|
/* 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. Authoritative store/principal checks supplement notifications. */
|
|
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
|