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; 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 // 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 socket_manager_init(SocketManager *sm, Socket *socks,
int num_socks) int num_socks)
{ {
sm->creation_timeout = 60000;
sm->recv_timeout = 3000;
sm->plain_sock = NATIVE_SOCKET_INVALID; sm->plain_sock = NATIVE_SOCKET_INVALID;
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
@@ -2169,6 +2217,16 @@ void socket_manager_free(SocketManager *sm)
#endif #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, int socket_manager_listen_tcp(SocketManager *sm,
CHTTP_String addr, Port port, int backlog, CHTTP_String addr, Port port, int backlog,
bool reuse_addr) bool reuse_addr)
@@ -2670,6 +2728,8 @@ void socket_manager_register_events(
// is ready to be processed exists, return an empty // is ready to be processed exists, return an empty
// event registration list so that those entries can // event registration list so that those entries can
// be processed immediately. // be processed immediately.
// TODO: comment about deadline
Time deadline = INVALID_TIME;
for (int i = 0, j = 0; j < sm->num_used; i++) { for (int i = 0, j = 0; j < sm->num_used; i++) {
Socket *s = &sm->sockets[i]; Socket *s = &sm->sockets[i];
if (s->state == SOCKET_STATE_FREE) if (s->state == SOCKET_STATE_FREE)
@@ -2679,11 +2739,23 @@ void socket_manager_register_events(
if (s->silent) if (s->silent)
continue; 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 // If at least one socket can be processed, return an
// empty list. // empty list.
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) { if (s->state == SOCKET_STATE_DIED ||
reg->num_polled = 0; s->state == SOCKET_STATE_ESTABLISHED_READY) {
return; deadline = 0;
} }
if (s->events) { if (s->events) {
@@ -2694,6 +2766,20 @@ void socket_manager_register_events(
reg->num_polled++; 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 static SocketHandle
@@ -2717,6 +2803,8 @@ int socket_manager_translate_events(
SocketManager *sm, SocketEvent *events, SocketManager *sm, SocketEvent *events,
EventRegister reg) EventRegister reg)
{ {
Time current_time = get_current_time();
int num_events = 0; int num_events = 0;
for (int i = 0; i < reg.num_polled; i++) { for (int i = 0; i < reg.num_polled; i++) {
@@ -2758,6 +2846,10 @@ int socket_manager_translate_events(
s->events = 0; s->events = 0;
s->user = NULL; s->user = NULL;
s->silent = false; 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 #ifdef HTTPS_ENABLED
// Determine whether the event came from // Determine whether the event came from
// the encrypted listener or not. // the encrypted listener or not.
@@ -2810,7 +2902,31 @@ int socket_manager_translate_events(
if (s->silent) if (s->silent)
continue; 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) { events[num_events++] = (SocketEvent) {
SOCKET_EVENT_DISCONNECT, SOCKET_EVENT_DISCONNECT,
@@ -2830,6 +2946,7 @@ int socket_manager_translate_events(
sm->num_used--; sm->num_used--;
} else if (s->state == SOCKET_STATE_ESTABLISHED_READY) { } else if (s->state == SOCKET_STATE_ESTABLISHED_READY) {
events[num_events++] = (SocketEvent) { events[num_events++] = (SocketEvent) {
SOCKET_EVENT_READY, SOCKET_EVENT_READY,
socket_to_handle(sm, s), 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, ConnectTarget *targets, bool secure, bool dont_verify_cert,
void *user) void *user)
{ {
Time current_time = get_current_time();
if (current_time == INVALID_TIME)
return CHTTP_ERROR_UNSPECIFIED;
if (sm->num_used == sm->max_used) if (sm->num_used == sm->max_used)
return CHTTP_ERROR_UNSPECIFIED; return CHTTP_ERROR_UNSPECIFIED;
@@ -3000,6 +3121,10 @@ int socket_connect(SocketManager *sm, int num_targets,
s->sock = NATIVE_SOCKET_INVALID; s->sock = NATIVE_SOCKET_INVALID;
s->user = user; s->user = user;
s->silent = false; 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 #ifdef HTTPS_ENABLED
s->server_secure_context = NULL; s->server_secure_context = NULL;
s->client_secure_context = NULL; s->client_secure_context = NULL;
@@ -3050,8 +3175,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
return 0; return 0;
} }
int ret;
if (!is_secure(s)) { if (!is_secure(s)) {
int ret = recv(s->sock, dst, max, 0); ret = recv(s->sock, dst, max, 0);
if (ret == 0) { if (ret == 0) {
UPDATE_STATE(s->state, SOCKET_STATE_DIED); UPDATE_STATE(s->state, SOCKET_STATE_DIED);
s->events = 0; s->events = 0;
@@ -3065,10 +3191,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
} }
ret = 0; ret = 0;
} }
return ret;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
int ret = SSL_read(s->ssl, dst, max); ret = SSL_read(s->ssl, dst, max);
if (ret <= 0) { if (ret <= 0) {
int err = SSL_get_error(s->ssl, ret); int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) { if (err == SSL_ERROR_WANT_READ) {
@@ -3083,12 +3208,18 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
} }
ret = 0; ret = 0;
} }
return ret;
#else #else
// Unreachable // Unreachable
return 0; ret = 0;
#endif #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, 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->state = CHTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; 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) { } else if (events[i].type == SOCKET_EVENT_READY) {
// Store the handle if this is a new connection // 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]; void *ptrs[CHTTP_CLIENT_POLL_CAPACITY];
struct pollfd polled[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); chttp_client_register_events(client, &reg);
if (reg.num_polled > 0) POLL(reg.polled, reg.num_polled, reg.timeout);
POLL(reg.polled, reg.num_polled, -1);
chttp_client_process_events(client, reg); 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? chttp_server_conn_free(conn); // TODO: what if this was in the ready queue?
server->num_conns--; 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) { } else if (events[i].type == SOCKET_EVENT_READY) {
if (events[i].user == NULL) { if (events[i].user == NULL) {
@@ -4758,11 +4908,10 @@ void chttp_server_wait_request(CHTTP_Server *server,
void *ptrs[CHTTP_SERVER_POLL_CAPACITY]; void *ptrs[CHTTP_SERVER_POLL_CAPACITY];
struct pollfd polled[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); chttp_server_register_events(server, &reg);
if (reg.num_polled > 0) POLL(reg.polled, reg.num_polled, reg.timeout);
POLL(reg.polled, reg.num_polled, -1);
chttp_server_process_events(server, reg); chttp_server_process_events(server, reg);
+27
View File
@@ -22,6 +22,7 @@
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#else #else
#include <time.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
@@ -284,6 +285,16 @@ typedef struct {
// Returns 0 on success, -1 on error // Returns 0 on success, -1 on error
int chttp_parse_set_cookie(CHTTP_String str, CHTTP_SetCookie *out); int chttp_parse_set_cookie(CHTTP_String str, CHTTP_SetCookie *out);
////////////////////////////////////////////////////////////////////////////////////////
// src/time.h
////////////////////////////////////////////////////////////////////////////////////////
typedef uint64_t Time;
#define INVALID_TIME ((Time) UINT64_MAX-1)
Time get_current_time(void);
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// src/secure_context.h // src/secure_context.h
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -398,6 +409,8 @@ typedef uint16_t Port;
typedef enum { typedef enum {
SOCKET_EVENT_READY, SOCKET_EVENT_READY,
SOCKET_EVENT_CREATION_TIMEOUT,
SOCKET_EVENT_RECV_TIMEOUT,
SOCKET_EVENT_DISCONNECT, SOCKET_EVENT_DISCONNECT,
} SocketEventType; } SocketEventType;
@@ -495,6 +508,12 @@ typedef struct {
// User-provided context pointer // User-provided context pointer
void *user; void *user;
Time creation_time;
Time last_recv_time;
Time recv_timeout;
Time creation_timeout;
// A single connect operation may involve // A single connect operation may involve
// trying to establish a connection towards // trying to establish a connection towards
// one of a set of addresses. // one of a set of addresses.
@@ -519,6 +538,10 @@ typedef struct {
// header. // header.
typedef struct { typedef struct {
// TODO: comment
Time recv_timeout;
Time creation_timeout;
// TCP listener sockets. The first is intended // TCP listener sockets. The first is intended
// for plaintext, while the second is for TLS. // for plaintext, while the second is for TLS.
// The socket manager will accept and add new // The socket manager will accept and add new
@@ -567,6 +590,9 @@ int socket_manager_init(SocketManager *sm, Socket *socks,
// Deinitialize a socket manager // Deinitialize a socket manager
void socket_manager_free(SocketManager *sm); void socket_manager_free(SocketManager *sm);
void socket_manager_set_creation_timeout(SocketManager *sm, int timeout);
void socket_manager_set_recv_timeout(SocketManager *sm, int timeout);
// Configure the socket manager to listen on // Configure the socket manager to listen on
// the specified interface for TCP connections. // the specified interface for TCP connections.
// Incoming connections will be automatically // Incoming connections will be automatically
@@ -608,6 +634,7 @@ typedef struct {
void **ptrs; void **ptrs;
struct pollfd *polled; struct pollfd *polled;
int num_polled; int num_polled;
int timeout;
} EventRegister; } EventRegister;
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
+17 -8
View File
@@ -9,6 +9,13 @@
#include "../chttp.h" #include "../chttp.h"
static int pick_timeout(int t1, int t2)
{
if (t1 < 0) return t2;
if (t2 < 0) return t1;
return (t1 < t2) ? t1 : t2;
}
int main() int main()
{ {
int ret; int ret;
@@ -55,23 +62,25 @@ int main()
EventRegister server_reg = { EventRegister server_reg = {
ptrs, ptrs,
polled, polled,
0 0,
-1
}; };
chttp_server_register_events(&server, &server_reg); chttp_server_register_events(&server, &server_reg);
EventRegister client_reg = { EventRegister client_reg = {
ptrs + server_reg.num_polled, ptrs + server_reg.num_polled,
polled + server_reg.num_polled, polled + server_reg.num_polled,
0 0,
-1
}; };
chttp_client_register_events(&client, &client_reg); chttp_client_register_events(&client, &client_reg);
if (server_reg.num_polled > 0 || int num_polled = server_reg.num_polled
client_reg.num_polled > 0) { + client_reg.num_polled;
int num_polled = server_reg.num_polled
+ client_reg.num_polled; int timeout = pick_timeout(server_reg.timeout, client_reg.timeout);
POLL(polled, num_polled, -1);
} POLL(polled, num_polled, timeout);
int result; int result;
void *user; void *user;
-2
View File
@@ -1,4 +1,2 @@
- Add connection timeouts
- Per RFC 6265, cookie values can be quoted. This parser would include the quotes in the value. - Per RFC 6265, cookie values can be quoted. This parser would include the quotes in the value.
- Make sure that the proxy example doesn't stall
- Return an appropriate error code when the user waits on a client with no pending requests - Return an appropriate error code when the user waits on a client with no pending requests
+2
View File
@@ -59,6 +59,7 @@ header.append_text(desc)
header.append_file("src/includes.h") header.append_file("src/includes.h")
header.append_file("src/basic.h") header.append_file("src/basic.h")
header.append_file("src/parse.h") header.append_file("src/parse.h")
header.append_file("src/time.h")
header.append_file("src/secure_context.h") header.append_file("src/secure_context.h")
header.append_file("src/socket.h") header.append_file("src/socket.h")
header.append_file("src/byte_queue.h") header.append_file("src/byte_queue.h")
@@ -77,6 +78,7 @@ source.append_text('#include "chttp.h"\n')
source.append_text("#endif\n") source.append_text("#endif\n")
source.append_file("src/basic.c") source.append_file("src/basic.c")
source.append_file("src/parse.c") source.append_file("src/parse.c")
source.append_file("src/time.c")
source.append_file("src/secure_context.c") source.append_file("src/secure_context.c")
source.append_file("src/socket.c") source.append_file("src/socket.c")
source.append_file("src/byte_queue.c") source.append_file("src/byte_queue.c")
+12 -3
View File
@@ -558,6 +558,16 @@ void chttp_client_process_events(CHTTP_Client *client,
conn->state = CHTTP_CLIENT_CONN_COMPLETE; conn->state = CHTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; 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) { } else if (events[i].type == SOCKET_EVENT_READY) {
// Store the handle if this is a new connection // Store the handle if this is a new connection
@@ -730,11 +740,10 @@ void chttp_client_wait_response(CHTTP_Client *client,
void *ptrs[CHTTP_CLIENT_POLL_CAPACITY]; void *ptrs[CHTTP_CLIENT_POLL_CAPACITY];
struct pollfd polled[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); chttp_client_register_events(client, &reg);
if (reg.num_polled > 0) POLL(reg.polled, reg.num_polled, reg.timeout);
POLL(reg.polled, reg.num_polled, -1);
chttp_client_process_events(client, reg); chttp_client_process_events(client, reg);
+1
View File
@@ -12,6 +12,7 @@
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#else #else
#include <time.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
+12 -3
View File
@@ -235,6 +235,16 @@ void chttp_server_process_events(CHTTP_Server *server,
chttp_server_conn_free(conn); // TODO: what if this was in the ready queue? chttp_server_conn_free(conn); // TODO: what if this was in the ready queue?
server->num_conns--; 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) { } else if (events[i].type == SOCKET_EVENT_READY) {
if (events[i].user == NULL) { if (events[i].user == NULL) {
@@ -290,11 +300,10 @@ void chttp_server_wait_request(CHTTP_Server *server,
void *ptrs[CHTTP_SERVER_POLL_CAPACITY]; void *ptrs[CHTTP_SERVER_POLL_CAPACITY];
struct pollfd polled[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); chttp_server_register_events(server, &reg);
if (reg.num_polled > 0) POLL(reg.polled, reg.num_polled, reg.timeout);
POLL(reg.polled, reg.num_polled, -1);
chttp_server_process_events(server, reg); chttp_server_process_events(server, reg);
+95 -9
View File
@@ -193,6 +193,9 @@ static void close_socket_pair(NATIVE_SOCKET a, NATIVE_SOCKET b)
int socket_manager_init(SocketManager *sm, Socket *socks, int socket_manager_init(SocketManager *sm, Socket *socks,
int num_socks) int num_socks)
{ {
sm->creation_timeout = 60000;
sm->recv_timeout = 3000;
sm->plain_sock = NATIVE_SOCKET_INVALID; sm->plain_sock = NATIVE_SOCKET_INVALID;
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
@@ -237,6 +240,16 @@ void socket_manager_free(SocketManager *sm)
#endif #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, int socket_manager_listen_tcp(SocketManager *sm,
CHTTP_String addr, Port port, int backlog, CHTTP_String addr, Port port, int backlog,
bool reuse_addr) bool reuse_addr)
@@ -738,6 +751,8 @@ void socket_manager_register_events(
// is ready to be processed exists, return an empty // is ready to be processed exists, return an empty
// event registration list so that those entries can // event registration list so that those entries can
// be processed immediately. // be processed immediately.
// TODO: comment about deadline
Time deadline = INVALID_TIME;
for (int i = 0, j = 0; j < sm->num_used; i++) { for (int i = 0, j = 0; j < sm->num_used; i++) {
Socket *s = &sm->sockets[i]; Socket *s = &sm->sockets[i];
if (s->state == SOCKET_STATE_FREE) if (s->state == SOCKET_STATE_FREE)
@@ -747,11 +762,23 @@ void socket_manager_register_events(
if (s->silent) if (s->silent)
continue; 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 // If at least one socket can be processed, return an
// empty list. // empty list.
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) { if (s->state == SOCKET_STATE_DIED ||
reg->num_polled = 0; s->state == SOCKET_STATE_ESTABLISHED_READY) {
return; deadline = 0;
} }
if (s->events) { if (s->events) {
@@ -762,6 +789,20 @@ void socket_manager_register_events(
reg->num_polled++; 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 static SocketHandle
@@ -785,6 +826,8 @@ int socket_manager_translate_events(
SocketManager *sm, SocketEvent *events, SocketManager *sm, SocketEvent *events,
EventRegister reg) EventRegister reg)
{ {
Time current_time = get_current_time();
int num_events = 0; int num_events = 0;
for (int i = 0; i < reg.num_polled; i++) { for (int i = 0; i < reg.num_polled; i++) {
@@ -826,6 +869,10 @@ int socket_manager_translate_events(
s->events = 0; s->events = 0;
s->user = NULL; s->user = NULL;
s->silent = false; 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 #ifdef HTTPS_ENABLED
// Determine whether the event came from // Determine whether the event came from
// the encrypted listener or not. // the encrypted listener or not.
@@ -878,7 +925,31 @@ int socket_manager_translate_events(
if (s->silent) if (s->silent)
continue; 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) { events[num_events++] = (SocketEvent) {
SOCKET_EVENT_DISCONNECT, SOCKET_EVENT_DISCONNECT,
@@ -898,6 +969,7 @@ int socket_manager_translate_events(
sm->num_used--; sm->num_used--;
} else if (s->state == SOCKET_STATE_ESTABLISHED_READY) { } else if (s->state == SOCKET_STATE_ESTABLISHED_READY) {
events[num_events++] = (SocketEvent) { events[num_events++] = (SocketEvent) {
SOCKET_EVENT_READY, SOCKET_EVENT_READY,
socket_to_handle(sm, s), socket_to_handle(sm, s),
@@ -1023,6 +1095,10 @@ int socket_connect(SocketManager *sm, int num_targets,
ConnectTarget *targets, bool secure, bool dont_verify_cert, ConnectTarget *targets, bool secure, bool dont_verify_cert,
void *user) void *user)
{ {
Time current_time = get_current_time();
if (current_time == INVALID_TIME)
return CHTTP_ERROR_UNSPECIFIED;
if (sm->num_used == sm->max_used) if (sm->num_used == sm->max_used)
return CHTTP_ERROR_UNSPECIFIED; return CHTTP_ERROR_UNSPECIFIED;
@@ -1068,6 +1144,10 @@ int socket_connect(SocketManager *sm, int num_targets,
s->sock = NATIVE_SOCKET_INVALID; s->sock = NATIVE_SOCKET_INVALID;
s->user = user; s->user = user;
s->silent = false; 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 #ifdef HTTPS_ENABLED
s->server_secure_context = NULL; s->server_secure_context = NULL;
s->client_secure_context = NULL; s->client_secure_context = NULL;
@@ -1118,8 +1198,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
return 0; return 0;
} }
int ret;
if (!is_secure(s)) { if (!is_secure(s)) {
int ret = recv(s->sock, dst, max, 0); ret = recv(s->sock, dst, max, 0);
if (ret == 0) { if (ret == 0) {
UPDATE_STATE(s->state, SOCKET_STATE_DIED); UPDATE_STATE(s->state, SOCKET_STATE_DIED);
s->events = 0; s->events = 0;
@@ -1133,10 +1214,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
} }
ret = 0; ret = 0;
} }
return ret;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
int ret = SSL_read(s->ssl, dst, max); ret = SSL_read(s->ssl, dst, max);
if (ret <= 0) { if (ret <= 0) {
int err = SSL_get_error(s->ssl, ret); int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) { if (err == SSL_ERROR_WANT_READ) {
@@ -1151,12 +1231,18 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
} }
ret = 0; ret = 0;
} }
return ret;
#else #else
// Unreachable // Unreachable
return 0; ret = 0;
#endif #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, int socket_send(SocketManager *sm, SocketHandle handle,
+16
View File
@@ -65,6 +65,8 @@ typedef uint16_t Port;
typedef enum { typedef enum {
SOCKET_EVENT_READY, SOCKET_EVENT_READY,
SOCKET_EVENT_CREATION_TIMEOUT,
SOCKET_EVENT_RECV_TIMEOUT,
SOCKET_EVENT_DISCONNECT, SOCKET_EVENT_DISCONNECT,
} SocketEventType; } SocketEventType;
@@ -162,6 +164,12 @@ typedef struct {
// User-provided context pointer // User-provided context pointer
void *user; void *user;
Time creation_time;
Time last_recv_time;
Time recv_timeout;
Time creation_timeout;
// A single connect operation may involve // A single connect operation may involve
// trying to establish a connection towards // trying to establish a connection towards
// one of a set of addresses. // one of a set of addresses.
@@ -186,6 +194,10 @@ typedef struct {
// header. // header.
typedef struct { typedef struct {
// TODO: comment
Time recv_timeout;
Time creation_timeout;
// TCP listener sockets. The first is intended // TCP listener sockets. The first is intended
// for plaintext, while the second is for TLS. // for plaintext, while the second is for TLS.
// The socket manager will accept and add new // The socket manager will accept and add new
@@ -234,6 +246,9 @@ int socket_manager_init(SocketManager *sm, Socket *socks,
// Deinitialize a socket manager // Deinitialize a socket manager
void socket_manager_free(SocketManager *sm); void socket_manager_free(SocketManager *sm);
void socket_manager_set_creation_timeout(SocketManager *sm, int timeout);
void socket_manager_set_recv_timeout(SocketManager *sm, int timeout);
// Configure the socket manager to listen on // Configure the socket manager to listen on
// the specified interface for TCP connections. // the specified interface for TCP connections.
// Incoming connections will be automatically // Incoming connections will be automatically
@@ -275,6 +290,7 @@ typedef struct {
void **ptrs; void **ptrs;
struct pollfd *polled; struct pollfd *polled;
int num_polled; int num_polled;
int timeout;
} EventRegister; } EventRegister;
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
+41
View File
@@ -0,0 +1,41 @@
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
}
+6
View File
@@ -0,0 +1,6 @@
typedef uint64_t Time;
#define INVALID_TIME ((Time) UINT64_MAX-1)
Time get_current_time(void);