Files
cHTTP/src/socket_pool.c
T

328 lines
8.3 KiB
C

#include <assert.h>
#include <stdlib.h>
#ifdef __linux__
#include <errno.h>
#include <sys/socket.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "socket_pool.h"
#endif
#define SOCKET_HARD_LIMIT (1<<10)
#define MAX_CERTS 10
struct SocketPool {
SecureContext sec;
RAW_SOCKET listen_sock;
RAW_SOCKET secure_sock;
int num_socks;
int max_socks;
Socket socks[];
};
void socket_pool_global_init(void)
{
}
void socket_pool_global_free(void)
{
}
SocketPool *socket_pool_init(HTTP_String addr,
uint16_t port, uint16_t secure_port, int max_socks,
bool reuse_addr, int backlog, HTTP_String cert_file,
HTTP_String key_file)
{
if (max_socks > SOCKET_HARD_LIMIT)
return NULL;
SocketPool *pool = malloc(sizeof(SocketPool) + max_socks * sizeof(Socket));
if (pool == NULL)
return NULL;
pool->num_socks = 0;
pool->max_socks = max_socks;
for (int i = 0; i < pool->max_socks; i++)
pool->socks[i].state = SOCKET_STATE_FREE;
if (port == 0)
pool->listen_sock = BAD_SOCKET;
else {
pool->listen_sock = listen_socket(addr, port, reuse_addr, backlog);
if (pool->listen_sock == BAD_SOCKET) {
free(pool);
return NULL;
}
}
if (secure_port == 0)
pool->secure_sock = BAD_SOCKET;
else {
#ifndef HTTPS_ENABLED
(void) cert_file;
(void) key_file;
if (pool->listen_sock != BAD_SOCKET)
CLOSE_SOCKET(pool->listen_sock);
free(pool);
return NULL;
#else
if (secure_context_init_as_server(&pool->sec, cert_file, key_file) < 0) {
if (pool->listen_sock != BAD_SOCKET)
CLOSE_SOCKET(pool->listen_sock);
free(pool);
return NULL;
}
pool->secure_sock = listen_socket(addr, secure_port, reuse_addr, backlog);
if (pool->secure_sock == BAD_SOCKET) {
if (pool->listen_sock != BAD_SOCKET) CLOSE_SOCKET(pool->listen_sock);
free(pool);
return NULL;
}
#endif
}
return pool;
}
void socket_pool_free(SocketPool *pool)
{
for (int i = 0, j = 0; j < pool->num_socks; i++) {
Socket *sock = &pool->socks[i];
if (sock->state == SOCKET_STATE_FREE)
continue;
j++;
socket_free(sock);
}
secure_context_free(&pool->sec);
if (pool->secure_sock != BAD_SOCKET) CLOSE_SOCKET(pool->secure_sock);
if (pool->listen_sock != BAD_SOCKET) CLOSE_SOCKET(pool->listen_sock);
}
int socket_pool_add_cert(SocketPool *pool, char *domain, int domain_len,
char *cert_file, int cert_file_len, char *key_file, int key_file_len)
{
return secure_context_add_cert(&pool->sec, domain, domain_len, cert_file, cert_file_len, key_file, key_file_len);
}
void socket_pool_set_user_data(SocketPool *pool, SocketHandle handle, void *user_data)
{
Socket *sock = &pool->socks[handle];
socket_set_user_data(sock, user_data);
}
void socket_pool_close(SocketPool *pool, SocketHandle handle)
{
Socket *sock = &pool->socks[handle];
socket_close(sock);
}
static Socket *find_free_socket(SocketPool *pool)
{
if (pool->num_socks == pool->max_socks)
return NULL;
int i = 0;
while (pool->socks[i].state != SOCKET_STATE_FREE)
i++;
return &pool->socks[i];
}
int socket_pool_connect(SocketPool *pool, bool secure,
HTTP_String addr, uint16_t port, void *user_data)
{
Socket *sock = find_free_socket(pool);
if (sock == NULL)
return -1;
socket_connect(sock, secure ? &pool->sec : NULL, addr, port, user_data);
if (socket_died(sock)) {
socket_free(sock);
return -1;
}
pool->num_socks++;
return 0;
}
int socket_pool_connect_ipv4(SocketPool *pool, bool secure,
HTTP_IPv4 addr, uint16_t port, void *user_data)
{
Socket *sock = find_free_socket(pool);
if (sock == NULL)
return -1;
socket_connect_ipv4(sock, secure ? &pool->sec : NULL, addr, port, user_data);
if (socket_died(sock)) {
socket_free(sock);
return -1;
}
pool->num_socks++;
return 0;
}
int socket_pool_connect_ipv6(SocketPool *pool, bool secure,
HTTP_IPv6 addr, uint16_t port, void *user_data)
{
Socket *sock = find_free_socket(pool);
if (sock == NULL)
return -1;
socket_connect_ipv6(sock, secure ? &pool->sec : NULL, addr, port, user_data);
if (socket_died(sock)) {
socket_free(sock);
return -1;
}
pool->num_socks++;
return 0;
}
SocketEvent socket_pool_wait(SocketPool *pool)
{
for (;;) {
// First, iterate over all sockets to find one that
// died or is ready.
for (int i = 0, j = 0; j < pool->num_socks; i++) {
Socket *sock = &pool->socks[i];
if (sock->state == SOCKET_STATE_FREE)
continue;
j++;
if (socket_died(sock)) {
void *user_data = socket_get_user_data(sock);
socket_free(sock);
return (SocketEvent) { SOCKET_EVENT_DIED, -1, user_data };
}
if (socket_ready(sock))
return (SocketEvent) { SOCKET_EVENT_READY, i, socket_get_user_data(sock) };
assert(sock->events);
}
// If we reached this point, we either have no sockets
// or all sockets need to wait for some event. Waiting
// when no sockets are available is only allowed when
// the pool is in server mode.
int indices[SOCKET_HARD_LIMIT+2];
struct pollfd polled[SOCKET_HARD_LIMIT+2];
int num_polled = 0;
if (pool->num_socks < pool->max_socks) {
if (pool->listen_sock != BAD_SOCKET) {
indices[num_polled] = -1;
polled[num_polled].fd = pool->listen_sock;
polled[num_polled].events = POLLIN;
polled[num_polled].revents = 0;
num_polled++;
}
if (pool->secure_sock != BAD_SOCKET) {
indices[num_polled] = -1;
polled[num_polled].fd = pool->secure_sock;
polled[num_polled].events = POLLIN;
polled[num_polled].revents = 0;
num_polled++;
}
}
for (int i = 0, j = 0; j < pool->num_socks; i++) {
Socket *sock = &pool->socks[i];
if (sock->state == SOCKET_STATE_FREE)
continue;
j++;
indices[num_polled] = i;
polled[num_polled].fd = sock->raw;
polled[num_polled].events = sock->events;
polled[num_polled].revents = 0;
num_polled++;
}
if (num_polled == 0)
return (SocketEvent) { SOCKET_EVENT_ERROR, -1, NULL };
int ret = POLL(polled, num_polled, -1);
if (ret < 0) {
if (errno == EINTR)
return (SocketEvent) { SOCKET_EVENT_SIGNAL, -1, NULL };
return (SocketEvent) { SOCKET_EVENT_ERROR, -1, NULL };
}
for (int i = 0; i < num_polled; i++) {
Socket *sock;
if (polled[i].fd == pool->listen_sock || polled[i].fd == pool->secure_sock) {
bool secure = false;
if (polled[i].fd == pool->secure_sock)
secure = true;
Socket *sock = find_free_socket(pool);
if (sock == NULL)
continue;
RAW_SOCKET raw = accept(polled[i].fd, NULL, NULL);
if (raw == BAD_SOCKET)
continue;
socket_accept(sock, secure ? &pool->sec : NULL, raw);
if (socket_died(sock)) {
socket_free(sock);
continue;
}
pool->num_socks++;
} else {
int j = indices[i];
sock = &pool->socks[j];
if (polled[i].revents)
socket_update(sock);
}
}
}
// This branch is unreachable
return (SocketEvent) { SOCKET_EVENT_ERROR, -1, NULL };
}
int socket_pool_read(SocketPool *pool, SocketHandle handle, char *dst, int len)
{
return socket_read(&pool->socks[handle], dst, len);
}
int socket_pool_write(SocketPool *pool, SocketHandle handle, char *src, int len)
{
return socket_write(&pool->socks[handle], src, len);
}