fix(gateway): harden websocket streaming under client backpressure
This commit is contained in:
@@ -20,7 +20,7 @@ extern "C" {
|
||||
#define MAZE32_GATEWAY_OUTGOING_LIMIT 768U
|
||||
#define MAZE32_GATEWAY_RATE_LIMIT_PER_SECOND 20U
|
||||
#define MAZE32_GATEWAY_RATE_LIMIT_CLOSE_THRESHOLD 40U
|
||||
#define MAZE32_GATEWAY_PLAYER_LATENCY_BUDGET_MS 250U
|
||||
#define MAZE32_GATEWAY_PLAYER_LATENCY_BUDGET_MS 500U
|
||||
|
||||
typedef struct {
|
||||
int active;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "maze32_gateway.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -39,6 +41,30 @@ static unsigned int active_connection_count(const maze32_gateway_t* gateway) {
|
||||
return count;
|
||||
}
|
||||
|
||||
static int gateway_send_all(httpd_handle_t server, int socket_fd, const char* buffer, size_t length,
|
||||
int flags) {
|
||||
(void)server;
|
||||
if (buffer == NULL) {
|
||||
return HTTPD_SOCK_ERR_INVALID;
|
||||
}
|
||||
size_t offset = 0U;
|
||||
while (offset < length) {
|
||||
int sent = send(socket_fd, buffer + offset, length - offset, flags);
|
||||
if (sent > 0) {
|
||||
offset += (size_t)sent;
|
||||
continue;
|
||||
}
|
||||
if (sent < 0 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (sent < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
return HTTPD_SOCK_ERR_TIMEOUT;
|
||||
}
|
||||
return HTTPD_SOCK_ERR_FAIL;
|
||||
}
|
||||
return (int)offset;
|
||||
}
|
||||
|
||||
static void sample_resources(maze32_gateway_t* gateway, const char* label) {
|
||||
size_t free_bytes = internal_free_bytes();
|
||||
size_t largest_block = internal_largest_block_bytes();
|
||||
@@ -82,6 +108,10 @@ static maze32_gateway_connection_t* open_connection(maze32_gateway_t* gateway, i
|
||||
memset(connection, 0, sizeof(*connection));
|
||||
connection->active = 1;
|
||||
connection->socket_fd = socket_fd;
|
||||
if (httpd_sess_set_send_override(gateway->server, socket_fd, gateway_send_all) !=
|
||||
ESP_OK) {
|
||||
ESP_LOGW(TAG, "Could not install bounded send callback for fd=%d", socket_fd);
|
||||
}
|
||||
connection->rate_window_started_ms = now_ms();
|
||||
unsigned int active_count = active_connection_count(gateway);
|
||||
if (active_count > gateway->metrics.connection_high_water) {
|
||||
@@ -202,6 +232,7 @@ static int drain_queue(maze32_gateway_t* gateway) {
|
||||
.len = entry->length,
|
||||
};
|
||||
int ready = 1;
|
||||
int original_flags = -1;
|
||||
if (entry->drop_if_busy) {
|
||||
fd_set write_set;
|
||||
FD_ZERO(&write_set);
|
||||
@@ -209,6 +240,12 @@ static int drain_queue(maze32_gateway_t* gateway) {
|
||||
struct timeval timeout = {0};
|
||||
ready = select(entry->socket_fd + 1, NULL, &write_set, NULL, &timeout) > 0 &&
|
||||
FD_ISSET(entry->socket_fd, &write_set);
|
||||
if (ready) {
|
||||
original_flags = fcntl(entry->socket_fd, F_GETFL, 0);
|
||||
if (original_flags >= 0) {
|
||||
(void)fcntl(entry->socket_fd, F_SETFL, original_flags | O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ready) {
|
||||
gateway->metrics.messages_dropped++;
|
||||
@@ -229,6 +266,9 @@ static int drain_queue(maze32_gateway_t* gateway) {
|
||||
close_slow_connection(gateway, entry->socket_fd);
|
||||
}
|
||||
}
|
||||
if (original_flags >= 0) {
|
||||
(void)fcntl(entry->socket_fd, F_SETFL, original_flags);
|
||||
}
|
||||
gateway->outgoing_head = (gateway->outgoing_head + 1U) % MAZE32_GATEWAY_QUEUE_LIMIT;
|
||||
gateway->outgoing_count--;
|
||||
}
|
||||
@@ -561,7 +601,9 @@ static void countdown_finished_work(void* context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
drain_queue(gateway);
|
||||
broadcast_snapshot(gateway);
|
||||
drain_queue(gateway);
|
||||
broadcast_views(gateway, 1);
|
||||
drain_queue(gateway);
|
||||
ESP_LOGI(TAG, "Lobby became active at server time %" PRIu64 " ms",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "maze32_web.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
@@ -107,7 +108,7 @@ esp_err_t maze32_web_start(httpd_handle_t* server, maze32_health_t* health,
|
||||
configuration.max_open_sockets = 9;
|
||||
configuration.lru_purge_enable = false;
|
||||
configuration.recv_wait_timeout = 5;
|
||||
configuration.send_wait_timeout = 1;
|
||||
configuration.send_wait_timeout = 5;
|
||||
configuration.global_user_ctx = gateway;
|
||||
configuration.global_user_ctx_free_fn = preserve_static_context;
|
||||
configuration.close_fn = maze32_gateway_close_socket;
|
||||
|
||||
Reference in New Issue
Block a user