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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user