Add request timeouts
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
@@ -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
|
||||
|
||||
+17
-8
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
+12
-3
@@ -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);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
+12
-3
@@ -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);
|
||||
|
||||
|
||||
+95
-9
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
+41
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
typedef uint64_t Time;
|
||||
|
||||
#define INVALID_TIME ((Time) UINT64_MAX-1)
|
||||
|
||||
Time get_current_time(void);
|
||||
Reference in New Issue
Block a user