Add authenticated WebSocket serial terminal - dirty commit with front-

and backend issues
This commit is contained in:
2026-08-24 19:45:36 +02:00
parent 8b5417881c
commit 5f7ea5b79d
22 changed files with 8845 additions and 45 deletions
+84 -5
View File
@@ -10,6 +10,7 @@
#include "esp_console.h"
#include "secure_random.h"
#include "web_security.h"
#include "web_serial_transport.h"
#include "web_server.h"
static void print_usage(void)
@@ -57,7 +58,37 @@ static int show_status(void)
printf("Authentication material unavailable: %s; use 'web reset --force' to replace it.\n",
esp_err_to_name(security_error));
}
printf("Endpoints: GET / and GET /api/status (authentication required)\n");
printf("Endpoints: GET /, GET /api/status, POST /api/ws-ticket, WSS /ws/serial\n");
web_serial_transport_snapshot_t transport;
esp_err_t transport_error = web_serial_transport_get_snapshot(&transport);
if (transport_error != ESP_OK) {
printf("WebSocket serial transport unavailable: %s\n",
esp_err_to_name(snapshot.serial_transport_error));
return 0;
}
printf("WebSocket serial: attached=%s sessions=%" PRIu32 "/%u tickets=%" PRIu32 "\n",
transport.server_attached ? "yes" : "no",
transport.active_sessions,
WEB_SERIAL_TRANSPORT_MAX_SESSIONS,
transport.active_tickets);
for (size_t index = 0U; index < WEB_SERIAL_TRANSPORT_MAX_SESSIONS; ++index) {
const web_serial_transport_session_snapshot_t *session =
&transport.sessions[index];
if (!session->active) {
continue;
}
printf(" slot=%u fd=%d generation=%" PRIu32 " broker=%" PRIu32
" role=%s tx-pending=%s closing=%s\n",
(unsigned int)index,
session->socket_fd,
session->generation,
session->broker_client_id,
session->writer ? "writer" : "observer",
session->tx_pending ? "yes" : "no",
session->close_requested ? "yes" : "no");
}
return 0;
}
@@ -76,10 +107,50 @@ static int show_counters(void)
counter->starts, counter->start_failures, counter->stops);
printf("Requests: total=%" PRIu64 " authenticated=%" PRIu64
" auth-failures=%" PRIu64 " root=%" PRIu64
" status=%" PRIu64 " response-errors=%" PRIu64 "\n",
" status=%" PRIu64 " tickets=%" PRIu64 " assets=%" PRIu64
" response-errors=%" PRIu64 "\n",
counter->requests, counter->authenticated_requests,
counter->authentication_failures, counter->root_requests,
counter->status_requests, counter->response_errors);
counter->status_requests, counter->ticket_requests,
counter->asset_requests, counter->response_errors);
web_serial_transport_snapshot_t transport;
error = web_serial_transport_get_snapshot(&transport);
if (error != ESP_OK) {
printf("WebSocket serial counters unavailable: %s\n", esp_err_to_name(error));
return 0;
}
const web_serial_transport_counters_t *websocket = &transport.counters;
printf("Tickets: issued=%" PRIu64 " consumed=%" PRIu64
" rejected=%" PRIu64 " expired=%" PRIu64 "\n",
websocket->tickets_issued, websocket->tickets_consumed,
websocket->tickets_rejected, websocket->tickets_expired);
printf("WebSocket sessions: connect=%" PRIu64 " failures=%" PRIu64
" disconnect=%" PRIu64 " service-start-failures=%" PRIu64
" broker-failures=%" PRIu64 "\n",
websocket->connections, websocket->connection_failures,
websocket->disconnections, websocket->service_start_failures,
websocket->broker_failures);
printf("WebSocket RX: frames-ok=%" PRIu64 " frames-rejected=%" PRIu64
" bytes-ok=%" PRIu64 " bytes-rejected=%" PRIu64 "\n",
websocket->rx_ws_frames_accepted,
websocket->rx_ws_frames_rejected,
websocket->rx_ws_bytes_accepted,
websocket->rx_ws_bytes_rejected);
printf("WebSocket TX: binary-frames=%" PRIu64 " binary-bytes=%" PRIu64
" control-frames=%" PRIu64 " control-bytes=%" PRIu64 "\n",
websocket->tx_binary_frames, websocket->tx_binary_bytes,
websocket->tx_control_frames, websocket->tx_control_bytes);
printf("WebSocket control: writer-requests=%" PRIu64
" grants=%" PRIu64 " denials=%" PRIu64
" releases=%" PRIu64 " revocations=%" PRIu64 "\n",
websocket->writer_requests, websocket->writer_grants,
websocket->writer_denials, websocket->writer_releases,
websocket->writer_revocations);
printf("WebSocket failures: send=%" PRIu64 " queue=%" PRIu64
" protocol=%" PRIu64 " closes=%" PRIu64 "\n",
websocket->send_failures, websocket->queue_failures,
websocket->protocol_errors, websocket->close_requests);
return 0;
}
@@ -160,7 +231,12 @@ static int rotate_credentials(void)
return 1;
}
esp_err_t revoke_error = web_serial_transport_revoke_sessions();
printf("Web credentials rotated and persisted. Existing Basic credentials are now invalid.\n");
if (revoke_error != ESP_OK && revoke_error != ESP_ERR_INVALID_STATE) {
printf("Warning: existing WebSocket sessions could not be revoked: %s\n",
esp_err_to_name(revoke_error));
}
printf("Username: %.*s\nPassword: %.*s\n",
(int)credentials.username_length, credentials.username,
(int)credentials.password_length, credentials.password);
@@ -247,11 +323,14 @@ static int command_web(int argc, char **argv)
}
if (argc == 2 && strcmp(argv[1], "clear-counters") == 0) {
esp_err_t error = web_server_clear_counters();
if (error == ESP_OK) {
error = web_serial_transport_clear_counters();
}
if (error != ESP_OK) {
printf("Could not clear HTTPS counters: %s\n", esp_err_to_name(error));
printf("Could not clear web counters: %s\n", esp_err_to_name(error));
return 1;
}
printf("HTTPS counters cleared.\n");
printf("HTTPS and WebSocket counters cleared.\n");
return 0;
}
if (argc == 3 && strcmp(argv[1], "credentials") == 0 &&