diff --git a/chttp.c b/chttp.c index dd3c8d7..acee618 100644 --- a/chttp.c +++ b/chttp.c @@ -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, ®); - 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, ®); - 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); diff --git a/chttp.h b/chttp.h index 2b14cf4..eb8c80c 100644 --- a/chttp.h +++ b/chttp.h @@ -22,6 +22,7 @@ #include #include #else +#include #include #include #include @@ -284,6 +285,16 @@ typedef struct { // Returns 0 on success, -1 on error 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 //////////////////////////////////////////////////////////////////////////////////////// @@ -398,6 +409,8 @@ typedef uint16_t Port; typedef enum { SOCKET_EVENT_READY, + SOCKET_EVENT_CREATION_TIMEOUT, + SOCKET_EVENT_RECV_TIMEOUT, SOCKET_EVENT_DISCONNECT, } SocketEventType; @@ -495,6 +508,12 @@ typedef struct { // User-provided context pointer void *user; + Time creation_time; + Time last_recv_time; + + Time recv_timeout; + Time creation_timeout; + // A single connect operation may involve // trying to establish a connection towards // one of a set of addresses. @@ -519,6 +538,10 @@ typedef struct { // header. typedef struct { + // TODO: comment + Time recv_timeout; + Time creation_timeout; + // TCP listener sockets. The first is intended // for plaintext, while the second is for TLS. // The socket manager will accept and add new @@ -567,6 +590,9 @@ int socket_manager_init(SocketManager *sm, Socket *socks, // Deinitialize a socket manager 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 // the specified interface for TCP connections. // Incoming connections will be automatically @@ -608,6 +634,7 @@ typedef struct { void **ptrs; struct pollfd *polled; int num_polled; + int timeout; } EventRegister; // Resets the event register with the list of descriptors diff --git a/examples/002_proxy.c b/examples/002_proxy.c index c4e2535..1b2fff6 100644 --- a/examples/002_proxy.c +++ b/examples/002_proxy.c @@ -9,6 +9,13 @@ #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 ret; @@ -55,23 +62,25 @@ int main() EventRegister server_reg = { ptrs, polled, - 0 + 0, + -1 }; chttp_server_register_events(&server, &server_reg); EventRegister client_reg = { ptrs + server_reg.num_polled, polled + server_reg.num_polled, - 0 + 0, + -1 }; chttp_client_register_events(&client, &client_reg); - if (server_reg.num_polled > 0 || - client_reg.num_polled > 0) { - int num_polled = server_reg.num_polled - + client_reg.num_polled; - POLL(polled, num_polled, -1); - } + 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, timeout); int result; void *user; diff --git a/misc/TODO.txt b/misc/TODO.txt index a5ec270..c632eef 100644 --- a/misc/TODO.txt +++ b/misc/TODO.txt @@ -1,4 +1,2 @@ -- Add connection timeouts - 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 diff --git a/misc/amalg.py b/misc/amalg.py index ae7af48..212dd3c 100644 --- a/misc/amalg.py +++ b/misc/amalg.py @@ -59,6 +59,7 @@ header.append_text(desc) header.append_file("src/includes.h") header.append_file("src/basic.h") header.append_file("src/parse.h") +header.append_file("src/time.h") header.append_file("src/secure_context.h") header.append_file("src/socket.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_file("src/basic.c") source.append_file("src/parse.c") +source.append_file("src/time.c") source.append_file("src/secure_context.c") source.append_file("src/socket.c") source.append_file("src/byte_queue.c") diff --git a/src/client.c b/src/client.c index 8008f10..cda7bfb 100644 --- a/src/client.c +++ b/src/client.c @@ -558,6 +558,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 @@ -730,11 +740,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, ®); - 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); diff --git a/src/includes.h b/src/includes.h index 05f4beb..f8e561b 100644 --- a/src/includes.h +++ b/src/includes.h @@ -12,6 +12,7 @@ #include #include #else +#include #include #include #include diff --git a/src/server.c b/src/server.c index 801736e..c99aea2 100644 --- a/src/server.c +++ b/src/server.c @@ -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? 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) { @@ -290,11 +300,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, ®); - 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); diff --git a/src/socket.c b/src/socket.c index 0b6efea..32ccac6 100644 --- a/src/socket.c +++ b/src/socket.c @@ -193,6 +193,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; @@ -237,6 +240,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) @@ -738,6 +751,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) @@ -747,11 +762,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) { @@ -762,6 +789,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 @@ -785,6 +826,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++) { @@ -826,6 +869,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. @@ -878,7 +925,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, @@ -898,6 +969,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), @@ -1023,6 +1095,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; @@ -1068,6 +1144,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; @@ -1118,8 +1198,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; @@ -1133,10 +1214,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) { @@ -1151,12 +1231,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, diff --git a/src/socket.h b/src/socket.h index ea4d76c..9e5ff42 100644 --- a/src/socket.h +++ b/src/socket.h @@ -65,6 +65,8 @@ typedef uint16_t Port; typedef enum { SOCKET_EVENT_READY, + SOCKET_EVENT_CREATION_TIMEOUT, + SOCKET_EVENT_RECV_TIMEOUT, SOCKET_EVENT_DISCONNECT, } SocketEventType; @@ -162,6 +164,12 @@ typedef struct { // User-provided context pointer void *user; + Time creation_time; + Time last_recv_time; + + Time recv_timeout; + Time creation_timeout; + // A single connect operation may involve // trying to establish a connection towards // one of a set of addresses. @@ -186,6 +194,10 @@ typedef struct { // header. typedef struct { + // TODO: comment + Time recv_timeout; + Time creation_timeout; + // TCP listener sockets. The first is intended // for plaintext, while the second is for TLS. // The socket manager will accept and add new @@ -234,6 +246,9 @@ int socket_manager_init(SocketManager *sm, Socket *socks, // Deinitialize a socket manager 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 // the specified interface for TCP connections. // Incoming connections will be automatically @@ -275,6 +290,7 @@ typedef struct { void **ptrs; struct pollfd *polled; int num_polled; + int timeout; } EventRegister; // Resets the event register with the list of descriptors diff --git a/src/time.c b/src/time.c new file mode 100644 index 0000000..3cd581e --- /dev/null +++ b/src/time.c @@ -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 +} \ No newline at end of file diff --git a/src/time.h b/src/time.h new file mode 100644 index 0000000..1e656cb --- /dev/null +++ b/src/time.h @@ -0,0 +1,6 @@ + +typedef uint64_t Time; + +#define INVALID_TIME ((Time) UINT64_MAX-1) + +Time get_current_time(void); \ No newline at end of file