Add request timeouts

This commit is contained in:
2025-12-12 22:36:27 +01:00
parent b361125bc3
commit 1f9411119a
12 changed files with 393 additions and 40 deletions
+164 -15
View File
@@ -1715,6 +1715,51 @@ int chttp_parse_set_cookie(CHTTP_String str, CHTTP_SetCookie *out)
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////
// src/time.c
////////////////////////////////////////////////////////////////////////////////////////
Time get_current_time(void)
{
#ifdef _WIN32
{
int64_t count;
int64_t freq;
int ok;
ok = QueryPerformanceCounter((LARGE_INTEGER*) &count);
if (!ok) return INVALID_TIME;
ok = QueryPerformanceFrequency((LARGE_INTEGER*) &freq);
if (!ok) return INVALID_TIME;
uint64_t res = 1000 * (double) count / freq;
return res;
}
#else
{
struct timespec time;
if (clock_gettime(CLOCK_REALTIME, &time))
return INVALID_TIME;
uint64_t res;
uint64_t sec = time.tv_sec;
if (sec > UINT64_MAX / 1000)
return INVALID_TIME;
res = sec * 1000;
uint64_t nsec = time.tv_nsec;
if (res > UINT64_MAX - nsec / 1000000)
return INVALID_TIME;
res += nsec / 1000000;
return res;
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////
// src/secure_context.c
////////////////////////////////////////////////////////////////////////////////////////
@@ -2125,6 +2170,9 @@ static void close_socket_pair(NATIVE_SOCKET a, NATIVE_SOCKET b)
int socket_manager_init(SocketManager *sm, Socket *socks,
int num_socks)
{
sm->creation_timeout = 60000;
sm->recv_timeout = 3000;
sm->plain_sock = NATIVE_SOCKET_INVALID;
sm->secure_sock = NATIVE_SOCKET_INVALID;
@@ -2169,6 +2217,16 @@ void socket_manager_free(SocketManager *sm)
#endif
}
void socket_manager_set_creation_timeout(SocketManager *sm, int timeout)
{
sm->creation_timeout = (timeout < 0) ? INVALID_TIME : (Time) timeout;
}
void socket_manager_set_recv_timeout(SocketManager *sm, int timeout)
{
sm->recv_timeout = (timeout < 0) ? INVALID_TIME : (Time) timeout;
}
int socket_manager_listen_tcp(SocketManager *sm,
CHTTP_String addr, Port port, int backlog,
bool reuse_addr)
@@ -2670,6 +2728,8 @@ void socket_manager_register_events(
// is ready to be processed exists, return an empty
// event registration list so that those entries can
// be processed immediately.
// TODO: comment about deadline
Time deadline = INVALID_TIME;
for (int i = 0, j = 0; j < sm->num_used; i++) {
Socket *s = &sm->sockets[i];
if (s->state == SOCKET_STATE_FREE)
@@ -2679,11 +2739,23 @@ void socket_manager_register_events(
if (s->silent)
continue;
if (s->creation_timeout != INVALID_TIME) {
Time creation_deadline = s->creation_time + s->creation_timeout;
if (deadline == INVALID_TIME || creation_deadline < deadline)
deadline = creation_deadline;
}
if (s->recv_timeout != INVALID_TIME) {
Time recv_deadline = s->last_recv_time + s->recv_timeout;
if (deadline == INVALID_TIME || recv_deadline < deadline)
deadline = recv_deadline;
}
// If at least one socket can be processed, return an
// empty list.
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) {
reg->num_polled = 0;
return;
if (s->state == SOCKET_STATE_DIED ||
s->state == SOCKET_STATE_ESTABLISHED_READY) {
deadline = 0;
}
if (s->events) {
@@ -2694,6 +2766,20 @@ void socket_manager_register_events(
reg->num_polled++;
}
}
if (deadline == INVALID_TIME) {
reg->timeout = -1;
} else {
Time current_time = get_current_time();
if (current_time == INVALID_TIME) {
reg->timeout = 1000;
} else if (deadline < current_time) {
reg->timeout = 0;
} else {
reg->timeout = deadline - current_time;
}
}
}
static SocketHandle
@@ -2717,6 +2803,8 @@ int socket_manager_translate_events(
SocketManager *sm, SocketEvent *events,
EventRegister reg)
{
Time current_time = get_current_time();
int num_events = 0;
for (int i = 0; i < reg.num_polled; i++) {
@@ -2758,6 +2846,10 @@ int socket_manager_translate_events(
s->events = 0;
s->user = NULL;
s->silent = false;
s->creation_time = current_time;
s->last_recv_time = current_time;
s->creation_timeout = sm->creation_timeout;
s->recv_timeout = sm->recv_timeout;
#ifdef HTTPS_ENABLED
// Determine whether the event came from
// the encrypted listener or not.
@@ -2810,7 +2902,31 @@ int socket_manager_translate_events(
if (s->silent)
continue;
if (s->state == SOCKET_STATE_DIED) {
if (s->creation_timeout != INVALID_TIME
&& current_time != INVALID_TIME
&& current_time > s->creation_time + s->creation_timeout) {
s->creation_time = INVALID_TIME;
events[num_events++] = (SocketEvent) {
SOCKET_EVENT_CREATION_TIMEOUT,
socket_to_handle(sm, s),
s->user
};
} else if (s->recv_timeout != INVALID_TIME
&& current_time != INVALID_TIME
&& current_time > s->last_recv_time + s->recv_timeout) {
s->recv_timeout = INVALID_TIME;
events[num_events++] = (SocketEvent) {
SOCKET_EVENT_RECV_TIMEOUT,
socket_to_handle(sm, s),
s->user
};
} else if (s->state == SOCKET_STATE_DIED) {
events[num_events++] = (SocketEvent) {
SOCKET_EVENT_DISCONNECT,
@@ -2830,6 +2946,7 @@ int socket_manager_translate_events(
sm->num_used--;
} else if (s->state == SOCKET_STATE_ESTABLISHED_READY) {
events[num_events++] = (SocketEvent) {
SOCKET_EVENT_READY,
socket_to_handle(sm, s),
@@ -2955,6 +3072,10 @@ int socket_connect(SocketManager *sm, int num_targets,
ConnectTarget *targets, bool secure, bool dont_verify_cert,
void *user)
{
Time current_time = get_current_time();
if (current_time == INVALID_TIME)
return CHTTP_ERROR_UNSPECIFIED;
if (sm->num_used == sm->max_used)
return CHTTP_ERROR_UNSPECIFIED;
@@ -3000,6 +3121,10 @@ int socket_connect(SocketManager *sm, int num_targets,
s->sock = NATIVE_SOCKET_INVALID;
s->user = user;
s->silent = false;
s->creation_time = current_time;
s->last_recv_time = current_time;
s->creation_timeout = sm->creation_timeout;
s->recv_timeout = sm->recv_timeout;
#ifdef HTTPS_ENABLED
s->server_secure_context = NULL;
s->client_secure_context = NULL;
@@ -3050,8 +3175,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
return 0;
}
int ret;
if (!is_secure(s)) {
int ret = recv(s->sock, dst, max, 0);
ret = recv(s->sock, dst, max, 0);
if (ret == 0) {
UPDATE_STATE(s->state, SOCKET_STATE_DIED);
s->events = 0;
@@ -3065,10 +3191,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
}
ret = 0;
}
return ret;
} else {
#ifdef HTTPS_ENABLED
int ret = SSL_read(s->ssl, dst, max);
ret = SSL_read(s->ssl, dst, max);
if (ret <= 0) {
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
@@ -3083,12 +3208,18 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
}
ret = 0;
}
return ret;
#else
// Unreachable
return 0;
ret = 0;
#endif
}
if (ret > 0 && s->recv_timeout != INVALID_TIME) {
Time current_time = get_current_time();
if (current_time != INVALID_TIME)
s->last_recv_time = current_time;
}
return ret;
}
int socket_send(SocketManager *sm, SocketHandle handle,
@@ -4215,6 +4346,16 @@ void chttp_client_process_events(CHTTP_Client *client,
conn->state = CHTTP_CLIENT_CONN_COMPLETE;
conn->result = -1;
} else if (events[i].type == SOCKET_EVENT_CREATION_TIMEOUT) {
// TODO: This is too abrupt
socket_close(&client->sockets, events[i].handle);
} else if (events[i].type == SOCKET_EVENT_RECV_TIMEOUT) {
// TODO: This is too abrupt
socket_close(&client->sockets, events[i].handle);
} else if (events[i].type == SOCKET_EVENT_READY) {
// Store the handle if this is a new connection
@@ -4387,11 +4528,10 @@ void chttp_client_wait_response(CHTTP_Client *client,
void *ptrs[CHTTP_CLIENT_POLL_CAPACITY];
struct pollfd polled[CHTTP_CLIENT_POLL_CAPACITY];
EventRegister reg = { ptrs, polled, 0 };
EventRegister reg = { ptrs, polled, 0, -1 };
chttp_client_register_events(client, &reg);
if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1);
POLL(reg.polled, reg.num_polled, reg.timeout);
chttp_client_process_events(client, reg);
@@ -4703,6 +4843,16 @@ void chttp_server_process_events(CHTTP_Server *server,
chttp_server_conn_free(conn); // TODO: what if this was in the ready queue?
server->num_conns--;
} else if (events[i].type == SOCKET_EVENT_CREATION_TIMEOUT) {
// TODO: This is too abrupt
socket_close(&server->sockets, events[i].handle);
} else if (events[i].type == SOCKET_EVENT_RECV_TIMEOUT) {
// TODO: This is too abrupt
socket_close(&server->sockets, events[i].handle);
} else if (events[i].type == SOCKET_EVENT_READY) {
if (events[i].user == NULL) {
@@ -4758,11 +4908,10 @@ void chttp_server_wait_request(CHTTP_Server *server,
void *ptrs[CHTTP_SERVER_POLL_CAPACITY];
struct pollfd polled[CHTTP_SERVER_POLL_CAPACITY];
EventRegister reg = { ptrs, polled, 0 };
EventRegister reg = { ptrs, polled, 0, -1 };
chttp_server_register_events(server, &reg);
if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1);
POLL(reg.polled, reg.num_polled, reg.timeout);
chttp_server_process_events(server, reg);