1. tcp_write infinite loop: When the output byte queue enters an error
state (buffer full), tcp_write would loop forever because
byte_queue_write_buf returns {NULL,0} without advancing. Break out
of the loop when the write buffer is empty.
2. Proxy ignores async return values: toastyfs_async_get/put/delete
can fail (return -1) but the proxy unconditionally set the operation
to STARTED, causing it to wait forever for a result that never comes.
Now checks return values and responds with 500 on failure.
3. Use-after-free on closed connections: When a client disconnects while
its request is queued as PENDING, the HTTP_Conn is freed but the proxy
still holds stale pointers. Added http_response_builder_is_valid() to
detect and discard operations for dead connections.
4. Wrong HTTP status codes: The proxy returned 500 for all errors. Now
returns 507 (Insufficient Storage) for FULL, 404 for NOT_FOUND, and
503 (Service Unavailable) when the operation queue is full.
5. TCP connection leak (root cause of proxy hang): When a peer closes a
connection, process_conn_events closes the socket and sets HUP, but
tcp_conn_free_maybe immediately frees the TCP_Conn before
tcp_next_event can deliver the HUP event. The HTTP server never learns
about the disconnect, so HTTP_Conn entries accumulate until max_conns
is reached and the proxy stops accepting new connections. Fixed by
only calling tcp_conn_free_maybe when the CLOSED flag is already set
(meaning the user called tcp_close), preserving the connection for
HUP event delivery otherwise.
Also adds wrk-based stress test suite (stress-test.sh + lua scripts).
https://claude.ai/code/session_01EHoWWnVgyCFxah5WaNV4kS
69 lines
1.9 KiB
C
69 lines
1.9 KiB
C
#ifndef HTTP_SERVER_INCLUDED
|
|
#define HTTP_SERVER_INCLUDED
|
|
|
|
#include "tcp.h"
|
|
#include "http_parse.h"
|
|
|
|
typedef CHTTP_Request HTTP_Request;
|
|
|
|
typedef enum {
|
|
HTTP_CONN_STATE_FREE,
|
|
HTTP_CONN_STATE_IDLE,
|
|
HTTP_CONN_STATE_STATUS,
|
|
HTTP_CONN_STATE_HEADER,
|
|
HTTP_CONN_STATE_CONTENT,
|
|
} HTTP_ConnState;
|
|
|
|
typedef struct {
|
|
HTTP_ConnState state;
|
|
uint16_t gen;
|
|
bool ready;
|
|
int num_served;
|
|
int request_len;
|
|
bool keep_alive;
|
|
TCP_Handle handle;
|
|
HTTP_Request request;
|
|
TCP_Offset content_offset;
|
|
TCP_Offset content_length_header_offset;
|
|
TCP_Offset response_offset;
|
|
} HTTP_Conn;
|
|
|
|
typedef struct {
|
|
TCP *tcp;
|
|
int num_conns;
|
|
int max_conns;
|
|
HTTP_Conn *conns;
|
|
} HTTP_Server;
|
|
|
|
int http_server_init(HTTP_Server *server, int max_conns);
|
|
void http_server_free(HTTP_Server *server);
|
|
|
|
int http_server_listen_tcp(HTTP_Server *server, Address addr);
|
|
|
|
int http_server_listen_tls(HTTP_Server *server, Address addr,
|
|
string cert_file, string key_file);
|
|
|
|
int http_server_add_cert(HTTP_Server *server, string domain, string cert_file, string key_file);
|
|
|
|
void http_server_process_events(HTTP_Server *server,
|
|
void **ptrs, struct pollfd *arr, int cap);
|
|
|
|
int http_server_register_events(HTTP_Server *server,
|
|
void **ptrs, struct pollfd *arr, int cap);
|
|
|
|
typedef struct {
|
|
HTTP_Server *server;
|
|
int idx;
|
|
int gen;
|
|
} HTTP_ResponseBuilder;
|
|
|
|
bool http_server_next_request(HTTP_Server *server,
|
|
HTTP_Request **request, HTTP_ResponseBuilder *builder);
|
|
|
|
bool http_response_builder_is_valid(HTTP_ResponseBuilder builder);
|
|
void http_response_builder_status(HTTP_ResponseBuilder builder, int status);
|
|
void http_response_builder_header(HTTP_ResponseBuilder builder, string header);
|
|
void http_response_builder_content(HTTP_ResponseBuilder builder, string content);
|
|
void http_response_builder_submit(HTTP_ResponseBuilder builder);
|
|
|
|
#endif // HTTP_SERVER_INCLUDED
|