Add error codes

This commit is contained in:
2025-11-26 22:38:37 +01:00
parent a58dc241ef
commit c4c41653f4
16 changed files with 483 additions and 713 deletions
+172 -297
View File
@@ -107,6 +107,20 @@ void print_bytes(HTTP_String prefix, HTTP_String src)
putc('\n', stream); putc('\n', stream);
} }
char *http_strerror(int code)
{
switch (code) {
case HTTP_OK: return "No error";
case HTTP_ERROR_UNSPECIFIED: return "Unspecified error";
case HTTP_ERROR_OOM: return "Out of memory";
case HTTP_ERROR_BADURL: return "Invalid URL";
case HTTP_ERROR_REQLIMIT: return "Parallel request limit reached";
case HTTP_ERROR_BADHANDLE: return "Invalid handle";
case HTTP_ERROR_NOTLS: return "TLS support not built-in";
}
return "???";
}
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// src/parse.c // src/parse.c
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -1702,58 +1716,6 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
return 0; return 0;
} }
////////////////////////////////////////////////////////////////////////////////////////
// src/thread.c
////////////////////////////////////////////////////////////////////////////////////////
int mutex_init(Mutex *mutex)
{
#ifdef _WIN32
InitializeCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_init(mutex, NULL))
return -1;
return 0;
#endif
}
int mutex_free(Mutex *mutex)
{
#ifdef _WIN32
DeleteCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_destroy(mutex))
return -1;
return 0;
#endif
}
int mutex_lock(Mutex *mutex)
{
#ifdef _WIN32
EnterCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_lock(mutex))
return -1;
return 0;
#endif
}
int mutex_unlock(Mutex *mutex)
{
#ifdef _WIN32
LeaveCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_unlock(mutex))
return -1;
return 0;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// src/secure_context.c // src/secure_context.c
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -2008,7 +1970,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
WSADATA wsaData; WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2); WORD wVersionRequested = MAKEWORD(2, 2);
if (WSAStartup(wVersionRequested, &wsaData)) if (WSAStartup(wVersionRequested, &wsaData))
return -1; return HTTP_ERROR_UNSPECIFIED;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET && *global_cleanup) if (sock == INVALID_SOCKET && *global_cleanup)
@@ -2018,7 +1980,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
// Bind to loopback address with port 0 (dynamic port assignment) // Bind to loopback address with port 0 (dynamic port assignment)
@@ -2033,21 +1995,21 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) {
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
// Optional: Set socket to non-blocking mode // Optional: Set socket to non-blocking mode
@@ -2057,20 +2019,20 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
*a = sock; *a = sock;
*b = sock; *b = sock;
return 0; return HTTP_OK;
#else #else
*global_cleanup = false; *global_cleanup = false;
int fds[2]; int fds[2];
if (pipe(fds) < 0) if (pipe(fds) < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
*a = fds[0]; *a = fds[0];
*b = fds[1]; *b = fds[1];
return 0; return HTTP_OK;
#endif #endif
} }
@@ -2079,20 +2041,20 @@ static int set_socket_blocking(NATIVE_SOCKET sock, bool value)
#ifdef _WIN32 #ifdef _WIN32
u_long mode = !value; u_long mode = !value;
if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR)
return -1; return HTTP_ERROR_UNSPECIFIED;
return HTTP_OK;
#endif #endif
#ifdef __linux__ #ifdef __linux__
int flags = fcntl(sock, F_GETFL, 0); int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) if (flags < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
if (value) flags &= ~O_NONBLOCK; if (value) flags &= ~O_NONBLOCK;
else flags |= O_NONBLOCK; else flags |= O_NONBLOCK;
if (fcntl(sock, F_SETFL, flags) < 0) if (fcntl(sock, F_SETFL, flags) < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
return HTTP_OK;
#endif #endif
return 0;
} }
static NATIVE_SOCKET create_listen_socket(HTTP_String addr, static NATIVE_SOCKET create_listen_socket(HTTP_String addr,
@@ -2162,13 +2124,15 @@ 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)
{ {
if (mutex_init(&sm->mutex) < 0)
return -1;
sm->plain_sock = NATIVE_SOCKET_INVALID; sm->plain_sock = NATIVE_SOCKET_INVALID;
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
if (create_socket_pair(&sm->wait_sock, &sm->signal_sock,
&sm->global_cleanup) < 0) int ret = create_socket_pair(
return -1; &sm->wait_sock,
&sm->signal_sock,
&sm->global_cleanup);
if (ret < 0) return ret;
sm->at_least_one_secure_connect = false; sm->at_least_one_secure_connect = false;
sm->num_used = 0; sm->num_used = 0;
@@ -2179,7 +2143,7 @@ int socket_manager_init(SocketManager *sm, Socket *socks,
socks[i].state = SOCKET_STATE_FREE; socks[i].state = SOCKET_STATE_FREE;
socks[i].gen = 1; socks[i].gen = 1;
} }
return 0; return HTTP_OK;
} }
void socket_manager_free(SocketManager *sm) void socket_manager_free(SocketManager *sm)
@@ -2202,8 +2166,6 @@ void socket_manager_free(SocketManager *sm)
if (sm->global_cleanup) if (sm->global_cleanup)
WSACleanup(); WSACleanup();
#endif #endif
mutex_free(&sm->mutex);
} }
int socket_manager_listen_tcp(SocketManager *sm, int socket_manager_listen_tcp(SocketManager *sm,
@@ -2211,13 +2173,13 @@ int socket_manager_listen_tcp(SocketManager *sm,
bool reuse_addr) bool reuse_addr)
{ {
if (sm->plain_sock != NATIVE_SOCKET_INVALID) if (sm->plain_sock != NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog); sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
if (sm->plain_sock == NATIVE_SOCKET_INVALID) if (sm->plain_sock == NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
return 0; return HTTP_OK;
} }
int socket_manager_listen_tls(SocketManager *sm, int socket_manager_listen_tls(SocketManager *sm,
@@ -2225,35 +2187,39 @@ int socket_manager_listen_tls(SocketManager *sm,
bool reuse_addr, HTTP_String cert_file, bool reuse_addr, HTTP_String cert_file,
HTTP_String key_file) HTTP_String key_file)
{ {
#ifndef HTTPS_ENABLED
return HTTP_ERROR_NOTLS;
#endif
if (sm->secure_sock != NATIVE_SOCKET_INVALID) if (sm->secure_sock != NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog); sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
if (sm->secure_sock == NATIVE_SOCKET_INVALID) if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
if (server_secure_context_init(&sm->server_secure_context, if (server_secure_context_init(&sm->server_secure_context,
cert_file, key_file) < 0) { cert_file, key_file) < 0) {
CLOSE_NATIVE_SOCKET(sm->secure_sock); CLOSE_NATIVE_SOCKET(sm->secure_sock);
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
return 0; return HTTP_OK;
} }
int socket_manager_add_certificate(SocketManager *sm, int socket_manager_add_certificate(SocketManager *sm,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
{ {
if (sm->secure_sock == NATIVE_SOCKET_INVALID) if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
int ret = server_secure_context_add_certificate( int ret = server_secure_context_add_certificate(
&sm->server_secure_context, domain, cert_file, key_file); &sm->server_secure_context, domain, cert_file, key_file);
if (ret < 0) if (ret < 0)
return -1; return ret;
return 0; return HTTP_OK;
} }
static bool is_secure(Socket *s) static bool is_secure(Socket *s)
@@ -2649,27 +2615,21 @@ static void socket_update(Socket *s)
int socket_manager_wakeup(SocketManager *sm) int socket_manager_wakeup(SocketManager *sm)
{ {
if (mutex_lock(&sm->mutex) < 0) // NOTE: It's assumed send/write operate atomically
return -1; // on The descriptor.
// Send a byte through the signal socket to wake up any thread
// blocked on poll() with the wait socket
char byte = 1; char byte = 1;
int ret = 0; int ret = 0;
#ifdef _WIN32 #ifdef _WIN32
if (send(sm->signal_sock, &byte, 1, 0) < 0) if (send(sm->signal_sock, &byte, 1, 0) < 0)
ret = -1; return HTTP_ERROR_UNSPECIFIED;
#else #else
if (write(sm->signal_sock, &byte, 1) < 0) if (write(sm->signal_sock, &byte, 1) < 0)
ret = -1; return HTTP_ERROR_UNSPECIFIED;
#endif #endif
return HTTP_OK;
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
static void socket_manager_register_events_nolock( void socket_manager_register_events(
SocketManager *sm, EventRegister *reg) SocketManager *sm, EventRegister *reg)
{ {
reg->num_polled = 0; reg->num_polled = 0;
@@ -2729,19 +2689,6 @@ static void socket_manager_register_events_nolock(
} }
} }
int socket_manager_register_events(SocketManager *sm,
EventRegister *reg)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
socket_manager_register_events_nolock(sm, reg);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return 0;
}
static SocketHandle static SocketHandle
socket_to_handle(SocketManager *sm, Socket *s) socket_to_handle(SocketManager *sm, Socket *s)
{ {
@@ -2759,7 +2706,7 @@ static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle)
return &sm->sockets[idx]; return &sm->sockets[idx];
} }
static int socket_manager_translate_events_nolock( int socket_manager_translate_events(
SocketManager *sm, SocketEvent *events, SocketManager *sm, SocketEvent *events,
EventRegister *reg) EventRegister *reg)
{ {
@@ -2882,19 +2829,6 @@ static int socket_manager_translate_events_nolock(
return num_events; return num_events;
} }
int socket_manager_translate_events(SocketManager *sm,
SocketEvent *events, EventRegister *reg)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret = socket_manager_translate_events_nolock(sm, events, reg);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
}
static int resolve_connect_targets(ConnectTarget *targets, static int resolve_connect_targets(ConnectTarget *targets,
int num_targets, AddressAndPort *resolved, int max_resolved) int num_targets, AddressAndPort *resolved, int max_resolved)
{ {
@@ -2915,7 +2849,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1); RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
if (name == NULL) { if (name == NULL) {
free_addr_list(resolved, num_resolved); free_addr_list(resolved, num_resolved);
return -1; return HTTP_ERROR_OOM;
} }
name->refs = 0; name->refs = 0;
memcpy(name->data, targets[i].name.ptr, targets[i].name.len); memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
@@ -2925,7 +2859,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
// 512 bytes is more than enough for a DNS hostname (max 253 chars) // 512 bytes is more than enough for a DNS hostname (max 253 chars)
char hostname[1<<9]; char hostname[1<<9];
if (targets[i].name.len >= (int) sizeof(hostname)) if (targets[i].name.len >= (int) sizeof(hostname))
return -1; return HTTP_ERROR_OOM;
memcpy(hostname, targets[i].name.ptr, targets[i].name.len); memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
hostname[targets[i].name.len] = '\0'; hostname[targets[i].name.len] = '\0';
#endif #endif
@@ -2937,7 +2871,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
free(name); free(name);
#endif #endif
free_addr_list(resolved, num_resolved); free_addr_list(resolved, num_resolved);
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
@@ -3009,17 +2943,17 @@ int socket_connect(SocketManager *sm, int num_targets,
ConnectTarget *targets, bool secure, void *user) ConnectTarget *targets, bool secure, void *user)
{ {
if (sm->num_used == sm->max_used) if (sm->num_used == sm->max_used)
return -1; return HTTP_ERROR_UNSPECIFIED;
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
if (!sm->at_least_one_secure_connect) { if (!sm->at_least_one_secure_connect) {
if (client_secure_context_init(&sm->client_secure_context) < 0) if (client_secure_context_init(&sm->client_secure_context) < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
sm->at_least_one_secure_connect = true; sm->at_least_one_secure_connect = true;
} }
#else #else
if (secure) if (secure)
return -1; return HTTP_ERROR_NOTLS;
#endif #endif
AddressAndPort resolved[MAX_CONNECT_TARGETS]; AddressAndPort resolved[MAX_CONNECT_TARGETS];
@@ -3027,7 +2961,7 @@ int socket_connect(SocketManager *sm, int num_targets,
targets, num_targets, resolved, MAX_CONNECT_TARGETS); targets, num_targets, resolved, MAX_CONNECT_TARGETS);
if (num_resolved <= 0) if (num_resolved <= 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
Socket *s = sm->sockets; Socket *s = sm->sockets;
while (s->state != SOCKET_STATE_FREE) { while (s->state != SOCKET_STATE_FREE) {
@@ -3044,7 +2978,7 @@ int socket_connect(SocketManager *sm, int num_targets,
s->next_addr = 0; s->next_addr = 0;
s->addrs = malloc(num_resolved * sizeof(AddressAndPort)); s->addrs = malloc(num_resolved * sizeof(AddressAndPort));
if (s->addrs == NULL) if (s->addrs == NULL)
return -1; return HTTP_ERROR_OOM;
for (int i = 0; i < num_resolved; i++) for (int i = 0; i < num_resolved; i++)
s->addrs[i] = resolved[i]; s->addrs[i] = resolved[i];
} }
@@ -3062,7 +2996,7 @@ int socket_connect(SocketManager *sm, int num_targets,
sm->num_used++; sm->num_used++;
socket_update(s); socket_update(s);
return 0; return HTTP_OK;
} }
static bool would_block(void) static bool would_block(void)
@@ -3084,7 +3018,7 @@ static bool interrupted(void)
#endif #endif
} }
static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, int socket_recv(SocketManager *sm, SocketHandle handle,
char *dst, int max) char *dst, int max)
{ {
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
@@ -3135,20 +3069,7 @@ static int socket_recv_nolock(SocketManager *sm, SocketHandle handle,
} }
} }
int socket_recv(SocketManager *sm, SocketHandle handle, int socket_send(SocketManager *sm, SocketHandle handle,
char *dst, int max)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret = socket_recv_nolock(sm, handle, dst, max);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
}
static int socket_send_nolock(SocketManager *sm, SocketHandle handle,
char *src, int len) char *src, int len)
{ {
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
@@ -3196,95 +3117,48 @@ static int socket_send_nolock(SocketManager *sm, SocketHandle handle,
} }
} }
int socket_send(SocketManager *sm, SocketHandle handle, void socket_close(SocketManager *sm, SocketHandle handle)
char *src, int len)
{ {
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret = socket_send_nolock(sm, handle, src, len);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
}
int socket_close(SocketManager *sm, SocketHandle handle)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret; int ret;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
if (s == NULL) if (s == NULL)
ret = -1; return;
else {
// Only transition to SHUTDOWN if socket is not already DIED
if (s->state != SOCKET_STATE_DIED) { if (s->state != SOCKET_STATE_DIED) {
UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN); UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN);
s->events = 0; s->events = 0;
socket_update(s); socket_update(s);
} }
}
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
int socket_is_secure(SocketManager *sm, SocketHandle handle) bool socket_is_secure(SocketManager *sm, SocketHandle handle)
{ {
if (mutex_lock(&sm->mutex) < 0)
return -1;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
int ret;
if (s == NULL) if (s == NULL)
ret = -1; return false;
else return is_secure(s);
ret = is_secure(s);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
int socket_set_user(SocketManager *sm, SocketHandle handle, void *user) void socket_set_user(SocketManager *sm, SocketHandle handle, void *user)
{ {
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
if (s == NULL) if (s == NULL)
ret = -1; return;
else {
s->user = user; s->user = user;
ret = 0;
}
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
bool socket_ready(SocketManager *sm, SocketHandle handle) bool socket_ready(SocketManager *sm, SocketHandle handle)
{ {
if (mutex_lock(&sm->mutex) < 0) {
assert(0); // TODO
}
bool ready = false; bool ready = false;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
if (s && s->events == 0 && s->state != SOCKET_STATE_DIED) { if (s == NULL)
ready = true; return false;
}
if (mutex_unlock(&sm->mutex) < 0) { if (s->events == 0 && s->state != SOCKET_STATE_DIED)
assert(0); // TODO return true;
}
return ready; return false;
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -3772,10 +3646,8 @@ int http_client_init(HTTP_Client *client)
client->num_ready = 0; client->num_ready = 0;
client->ready_head = 0; client->ready_head = 0;
if (socket_manager_init(&client->sockets, return socket_manager_init(&client->sockets,
client->socket_pool, HTTP_CLIENT_CAPACITY) < 0) client->socket_pool, HTTP_CLIENT_CAPACITY);
return -1;
return 0;
} }
void http_client_free(HTTP_Client *client) void http_client_free(HTTP_Client *client)
@@ -3807,9 +3679,7 @@ void http_client_set_output_limit(HTTP_Client *client, uint32_t limit)
int http_client_wakeup(HTTP_Client *client) int http_client_wakeup(HTTP_Client *client)
{ {
if (socket_manager_wakeup(&client->sockets) < 0) return socket_manager_wakeup(&client->sockets);
return -1;
return 0;
} }
// Get a connection pointer from a request builder. // Get a connection pointer from a request builder.
@@ -3985,7 +3855,7 @@ void http_request_builder_target(HTTP_RequestBuilder builder,
char *url_copy = malloc(url.len); char *url_copy = malloc(url.len);
if (url_copy == NULL) { if (url_copy == NULL) {
conn->state = HTTP_CLIENT_CONN_COMPLETE; conn->state = HTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; conn->result = HTTP_ERROR_OOM;
return; return;
} }
memcpy(url_copy, url.ptr, url.len); memcpy(url_copy, url.ptr, url.len);
@@ -3996,14 +3866,14 @@ void http_request_builder_target(HTTP_RequestBuilder builder,
// Parse the copied URL (all url.* pointers will reference url_buffer) // Parse the copied URL (all url.* pointers will reference url_buffer)
if (http_parse_url(conn->url_buffer.ptr, conn->url_buffer.len, &conn->url) < 0) { if (http_parse_url(conn->url_buffer.ptr, conn->url_buffer.len, &conn->url) < 0) {
conn->state = HTTP_CLIENT_CONN_COMPLETE; conn->state = HTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; conn->result = HTTP_ERROR_BADURL;
return; return;
} }
if (!http_streq(conn->url.scheme, HTTP_STR("http")) && if (!http_streq(conn->url.scheme, HTTP_STR("http")) &&
!http_streq(conn->url.scheme, HTTP_STR("https"))) { !http_streq(conn->url.scheme, HTTP_STR("https"))) {
conn->state = HTTP_CLIENT_CONN_COMPLETE; conn->state = HTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; conn->result = HTTP_ERROR_BADURL;
return; return;
} }
@@ -4011,7 +3881,9 @@ void http_request_builder_target(HTTP_RequestBuilder builder,
if (conn->url.path.len == 0) if (conn->url.path.len == 0)
byte_queue_write(&conn->output, "/", 1); byte_queue_write(&conn->output, "/", 1);
else else
byte_queue_write(&conn->output, conn->url.path.ptr, conn->url.path.len); byte_queue_write(&conn->output,
conn->url.path.ptr,
conn->url.path.len);
// Write query string // Write query string
HTTP_String query = conn->url.query; HTTP_String query = conn->url.query;
@@ -4103,47 +3975,45 @@ void http_request_builder_body(HTTP_RequestBuilder builder,
byte_queue_write(&conn->output, str.ptr, str.len); byte_queue_write(&conn->output, str.ptr, str.len);
} }
static int static ConnectTarget url_to_connect_target(HTTP_URL url)
url_to_connect_target(HTTP_URL url,
ConnectTarget *target)
{ {
HTTP_Authority authority = url.authority; HTTP_Authority authority = url.authority;
ConnectTarget target;
if (authority.port < 1) { if (authority.port < 1) {
if (http_streq(url.scheme, HTTP_STR("https"))) if (http_streq(url.scheme, HTTP_STR("https")))
target->port = 443; target.port = 443;
else else
target->port = 80; target.port = 80;
} else { } else {
target->port = authority.port; target.port = authority.port;
} }
// Set up target based on host type
if (authority.host.mode == HTTP_HOST_MODE_NAME) { if (authority.host.mode == HTTP_HOST_MODE_NAME) {
target->type = CONNECT_TARGET_NAME; target.type = CONNECT_TARGET_NAME;
target->name = authority.host.name; target.name = authority.host.name;
} else if (authority.host.mode == HTTP_HOST_MODE_IPV4) { } else if (authority.host.mode == HTTP_HOST_MODE_IPV4) {
target->type = CONNECT_TARGET_IPV4; target.type = CONNECT_TARGET_IPV4;
target->ipv4 = authority.host.ipv4; target.ipv4 = authority.host.ipv4;
} else if (authority.host.mode == HTTP_HOST_MODE_IPV6) { } else if (authority.host.mode == HTTP_HOST_MODE_IPV6) {
target->type = CONNECT_TARGET_IPV6; target.type = CONNECT_TARGET_IPV6;
target->ipv6 = authority.host.ipv6; target.ipv6 = authority.host.ipv6;
} else { } else {
return -1; HTTP_UNREACHABLE;
} }
return 0; return target;
} }
int http_request_builder_send(HTTP_RequestBuilder builder) int http_request_builder_send(HTTP_RequestBuilder builder)
{ {
HTTP_Client *client = builder.client; HTTP_Client *client = builder.client;
if (client == NULL) if (client == NULL)
return -1; return HTTP_ERROR_REQLIMIT;
HTTP_ClientConn *conn = request_builder_to_conn(builder); HTTP_ClientConn *conn = request_builder_to_conn(builder);
if (conn == NULL) if (conn == NULL)
return -1; return HTTP_ERROR_BADHANDLE;
if (conn->state == HTTP_CLIENT_CONN_COMPLETE) if (conn->state == HTTP_CLIENT_CONN_COMPLETE)
goto error; // Early completion due to an error goto error; // Early completion due to an error
@@ -4160,17 +4030,14 @@ int http_request_builder_send(HTTP_RequestBuilder builder)
if (byte_queue_error(&conn->output)) if (byte_queue_error(&conn->output))
goto error; goto error;
ConnectTarget target; ConnectTarget target = url_to_connect_target(conn->url);
if (url_to_connect_target(conn->url, &target) < 0)
goto error;
bool secure = http_streq(conn->url.scheme, HTTP_STR("https")); bool secure = http_streq(conn->url.scheme, HTTP_STR("https"));
if (socket_connect(&client->sockets, 1, &target, secure, conn) < 0) if (socket_connect(&client->sockets, 1, &target, secure, conn) < 0)
goto error; goto error;
conn->state = HTTP_CLIENT_CONN_FLUSHING; conn->state = HTTP_CLIENT_CONN_FLUSHING;
conn->gen++; conn->gen++;
return 0; return HTTP_OK;
error: error:
conn->state = HTTP_CLIENT_CONN_FREE; conn->state = HTTP_CLIENT_CONN_FREE;
@@ -4178,7 +4045,7 @@ error:
byte_queue_free(&conn->input); byte_queue_free(&conn->input);
byte_queue_free(&conn->output); byte_queue_free(&conn->output);
client->num_conns--; client->num_conns--;
return -1; return conn->result;
} }
static void save_one_cookie(HTTP_CookieJar *cookie_jar, static void save_one_cookie(HTTP_CookieJar *cookie_jar,
@@ -4251,22 +4118,20 @@ static void save_cookies(HTTP_CookieJar *cookie_jar,
save_one_cookie(cookie_jar, headers[i], domain, path); save_one_cookie(cookie_jar, headers[i], domain, path);
} }
int http_client_register_events(HTTP_Client *client, void http_client_register_events(HTTP_Client *client,
EventRegister *reg) EventRegister *reg)
{ {
if (socket_manager_register_events(&client->sockets, reg) < 0) socket_manager_register_events(&client->sockets, reg);
return -1;
return 0;
} }
int http_client_process_events(HTTP_Client *client, int http_client_process_events(HTTP_Client *client,
EventRegister *reg) EventRegister *reg)
{ {
SocketEvent events[HTTP_CLIENT_CAPACITY]; SocketEvent events[HTTP_CLIENT_CAPACITY];
int num_events = socket_manager_translate_events( int ret = socket_manager_translate_events(&client->sockets, events, reg);
&client->sockets, events, reg); if (ret < 0)
if (num_events < 0) return ret;
return -1; int num_events = ret;
for (int i = 0; i < num_events; i++) { for (int i = 0; i < num_events; i++) {
@@ -4389,11 +4254,11 @@ int http_client_process_events(HTTP_Client *client,
} }
} }
return 0; return HTTP_OK;
} }
bool http_client_next_response(HTTP_Client *client, bool http_client_next_response(HTTP_Client *client,
HTTP_Response **response, void **user) int *result, void **user, HTTP_Response **response)
{ {
if (client->num_ready == 0) if (client->num_ready == 0)
return false; return false;
@@ -4404,13 +4269,13 @@ bool http_client_next_response(HTTP_Client *client,
assert(conn->state == HTTP_CLIENT_CONN_COMPLETE); assert(conn->state == HTTP_CLIENT_CONN_COMPLETE);
if (conn->result == 0) { *result = conn->result;
*user = conn->user;
if (conn->result == HTTP_OK) {
*response = &conn->response; *response = &conn->response;
} else { } else {
assert(conn->result == -1);
*response = NULL; *response = NULL;
} }
*user = conn->user;
return true; return true;
} }
@@ -4446,32 +4311,29 @@ void http_free_response(HTTP_Response *response)
#define POLL poll #define POLL poll
#endif #endif
int http_client_wait_response(HTTP_Client *client, HTTP_Response **response, void **user) int http_client_wait_response(HTTP_Client *client,
int *result, void **user, HTTP_Response **response)
{ {
for (;;) { for (;;) {
void *ptrs[HTTP_CLIENT_POLL_CAPACITY]; void *ptrs[HTTP_CLIENT_POLL_CAPACITY];
struct pollfd polled[HTTP_CLIENT_POLL_CAPACITY]; struct pollfd polled[HTTP_CLIENT_POLL_CAPACITY];
EventRegister reg = { EventRegister reg = { ptrs, polled, 0 };
.ptrs=ptrs, http_client_register_events(client, &reg);
.polled=polled,
.num_polled=0,
.max_polled=HTTP_CLIENT_POLL_CAPACITY,
};
if (http_client_register_events(client, &reg) < 0)
return -1;
if (reg.num_polled > 0) if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1); POLL(reg.polled, reg.num_polled, -1);
if (http_client_process_events(client, &reg) < 0) int ret = http_client_process_events(client, &reg);
return -1; if (ret < 0)
return ret;
if (http_client_next_response(client, response, user)) if (http_client_next_response(client, result, user, response))
break; break;
} }
return 0; return HTTP_OK;
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -4513,10 +4375,8 @@ int http_server_init(HTTP_Server *server)
server->num_ready = 0; server->num_ready = 0;
server->ready_head = 0; server->ready_head = 0;
if (socket_manager_init(&server->sockets, return socket_manager_init(&server->sockets,
server->socket_pool, HTTP_SERVER_CAPACITY) < 0) server->socket_pool, HTTP_SERVER_CAPACITY);
return -1;
return 0;
} }
void http_server_free(HTTP_Server *server) void http_server_free(HTTP_Server *server)
@@ -4561,45 +4421,35 @@ void http_server_set_backlog(HTTP_Server *server, int backlog)
int http_server_listen_tcp(HTTP_Server *server, int http_server_listen_tcp(HTTP_Server *server,
HTTP_String addr, Port port) HTTP_String addr, Port port)
{ {
if (socket_manager_listen_tcp(&server->sockets, addr, return socket_manager_listen_tcp(&server->sockets,
port, server->backlog, server->reuse_addr) < 0) addr, port, server->backlog, server->reuse_addr);
return -1;
return 0;
} }
int http_server_listen_tls(HTTP_Server *server, int http_server_listen_tls(HTTP_Server *server,
HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String addr, Port port, HTTP_String cert_file_name,
HTTP_String key_file_name) HTTP_String key_file_name)
{ {
if (socket_manager_listen_tls(&server->sockets, addr, return socket_manager_listen_tls(&server->sockets,
port, server->backlog, server->reuse_addr, addr, port, server->backlog, server->reuse_addr,
cert_file_name, key_file_name) < 0) cert_file_name, key_file_name);
return -1;
return 0;
} }
int http_server_add_certificate(HTTP_Server *server, int http_server_add_certificate(HTTP_Server *server,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
{ {
if (socket_manager_add_certificate(&server->sockets, return socket_manager_add_certificate(&server->sockets,
domain, cert_file, key_file) < 0) domain, cert_file, key_file);
return -1;
return 0;
} }
int http_server_wakeup(HTTP_Server *server) int http_server_wakeup(HTTP_Server *server)
{ {
if (socket_manager_wakeup(&server->sockets) < 0) return socket_manager_wakeup(&server->sockets);
return -1;
return 0;
} }
int http_server_register_events(HTTP_Server *server, void http_server_register_events(HTTP_Server *server,
EventRegister *reg) EventRegister *reg)
{ {
if (socket_manager_register_events(&server->sockets, reg) < 0) socket_manager_register_events(&server->sockets, reg);
return -1;
return 0;
} }
// Look at the head of the input buffer to see if // Look at the head of the input buffer to see if
@@ -4712,9 +4562,10 @@ int http_server_process_events(HTTP_Server *server,
EventRegister *reg) EventRegister *reg)
{ {
SocketEvent events[HTTP_SERVER_CAPACITY]; SocketEvent events[HTTP_SERVER_CAPACITY];
int num_events = socket_manager_translate_events(&server->sockets, events, reg); int ret = socket_manager_translate_events(&server->sockets, events, reg);
if (num_events < 0) if (ret < 0)
return -1; return ret;
int num_events = ret;
for (int i = 0; i < num_events; i++) { for (int i = 0; i < num_events; i++) {
@@ -4756,7 +4607,7 @@ int http_server_process_events(HTTP_Server *server,
} }
} }
return 0; return HTTP_OK;
} }
bool http_server_next_request(HTTP_Server *server, bool http_server_next_request(HTTP_Server *server,
@@ -4775,6 +4626,30 @@ bool http_server_next_request(HTTP_Server *server,
return true; return true;
} }
int http_server_wait_request(HTTP_Server *server,
HTTP_Request **request, HTTP_ResponseBuilder *builder)
{
for (;;) {
void *ptrs[HTTP_SERVER_POLL_CAPACITY];
struct pollfd polled[HTTP_SERVER_POLL_CAPACITY];
EventRegister reg = { ptrs, polled, 0 };
http_server_register_events(server, &reg);
if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1);
int ret = http_server_process_events(server, &reg);
if (ret < 0)
return ret;
if (http_server_next_request(server, request, builder))
break;
}
return HTTP_OK;
}
// Get a connection pointer from a response builder. // Get a connection pointer from a response builder.
// If the builder is invalid, returns NULL. // If the builder is invalid, returns NULL.
// Note that only connections in the responding states // Note that only connections in the responding states
+45 -37
View File
@@ -23,7 +23,6 @@
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h>
#include <poll.h> #include <poll.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
@@ -41,6 +40,29 @@
// src/basic.h // src/basic.h
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
enum {
HTTP_OK = 0,
// A generic error occurred
HTTP_ERROR_UNSPECIFIED = -1,
// Out of memory
HTTP_ERROR_OOM = -2,
// Invalid URL
HTTP_ERROR_BADURL = -3,
// Parallel request limit reached
HTTP_ERROR_REQLIMIT = -4,
// Invalid handle
HTTP_ERROR_BADHANDLE = -5,
// TLS support not built-in
HTTP_ERROR_NOTLS = -6,
};
// String type used throughout cHTTP. // String type used throughout cHTTP.
typedef struct { typedef struct {
char *ptr; char *ptr;
@@ -65,6 +87,9 @@ HTTP_String http_trim(HTTP_String s);
// This is primarily used for debugging purposes. // This is primarily used for debugging purposes.
void print_bytes(HTTP_String prefix, HTTP_String src); void print_bytes(HTTP_String prefix, HTTP_String src);
// TODO: comment
char *http_strerror(int code);
// Macro to simplify converting string literals to // Macro to simplify converting string literals to
// HTTP_String. // HTTP_String.
// //
@@ -94,6 +119,9 @@ void print_bytes(HTTP_String prefix, HTTP_String src);
// Example: printf("%.*s", HTTP_UNPACK(str)); // Example: printf("%.*s", HTTP_UNPACK(str));
#define HTTP_UNPACK(X) (X).len, (X).ptr #define HTTP_UNPACK(X) (X).len, (X).ptr
// TODO: comment
#define HTTP_UNREACHABLE __builtin_trap()
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// src/parse.h // src/parse.h
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -253,21 +281,6 @@ typedef struct {
// Returns 0 on success, -1 on error // Returns 0 on success, -1 on error
int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out); int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out);
////////////////////////////////////////////////////////////////////////////////////////
// src/thread.h
////////////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
typedef CRITICAL_SECTION Mutex;
#else
typedef pthread_mutex_t Mutex;
#endif
int mutex_init(Mutex *mutex);
int mutex_free(Mutex *mutex);
int mutex_lock(Mutex *mutex);
int mutex_unlock(Mutex *mutex);
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// src/secure_context.h // src/secure_context.h
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -499,11 +512,6 @@ typedef struct {
// header. // header.
typedef struct { typedef struct {
// This guards access to the main thread using
// the manager from other threads calling the
// wakeup function.
Mutex mutex;
// 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
@@ -593,14 +601,11 @@ typedef struct {
void **ptrs; void **ptrs;
struct pollfd *polled; struct pollfd *polled;
int num_polled; int num_polled;
int max_polled;
} EventRegister; } EventRegister;
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
// the socket manager wants monitored. Returns 0 on // the socket manager wants monitored.
// success, -1 if the event register's capacity isn't void socket_manager_register_events(SocketManager *sm,
// large enough.
int socket_manager_register_events(SocketManager *sm,
EventRegister *reg); EventRegister *reg);
// After poll() is called on the previously registered // After poll() is called on the previously registered
@@ -645,15 +650,15 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
int socket_send(SocketManager *sm, SocketHandle handle, int socket_send(SocketManager *sm, SocketHandle handle,
char *src, int len); char *src, int len);
int socket_close(SocketManager *sm, SocketHandle handle); void socket_close(SocketManager *sm, SocketHandle handle);
// Returns -1 on error, 0 if the socket was accepted // Returns -1 on error, 0 if the socket was accepted
// from the plaintext listener, or 1 if it was accepted // from the plaintext listener, or 1 if it was accepted
// by the secure listener. // by the secure listener.
int socket_is_secure(SocketManager *sm, SocketHandle handle); bool socket_is_secure(SocketManager *sm, SocketHandle handle);
// Set the user pointer of a socket // Set the user pointer of a socket
int socket_set_user(SocketManager *sm, SocketHandle handle, void *user); void socket_set_user(SocketManager *sm, SocketHandle handle, void *user);
// Returns true iff the socket is ready for reading or // Returns true iff the socket is ready for reading or
// writing. // writing.
@@ -1025,9 +1030,8 @@ void http_request_builder_body(HTTP_RequestBuilder builder,
int http_request_builder_send(HTTP_RequestBuilder builder); int http_request_builder_send(HTTP_RequestBuilder builder);
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
// the client wants monitored. Returns 0 on success, -1 if // the client wants monitored.
// the event register's capacity isn't large enough. void http_client_register_events(HTTP_Client *client,
int http_client_register_events(HTTP_Client *client,
EventRegister *reg); EventRegister *reg);
// The caller has waited for poll() to return and some // The caller has waited for poll() to return and some
@@ -1044,10 +1048,11 @@ int http_client_process_events(HTTP_Client *client,
// http_free_response function. // http_free_response function.
// TODO: Better comment talking about output arguments // TODO: Better comment talking about output arguments
bool http_client_next_response(HTTP_Client *client, bool http_client_next_response(HTTP_Client *client,
HTTP_Response **response, void **user); int *result, void **user, HTTP_Response **response);
// TODO: comment // TODO: comment
int http_client_wait_response(HTTP_Client *client, HTTP_Response **response, void **user); int http_client_wait_response(HTTP_Client *client,
int *result, void **user, HTTP_Response **response);
// Free a response object. You can't access its fields // Free a response object. You can't access its fields
// again after this. // again after this.
@@ -1236,9 +1241,8 @@ int http_server_add_certificate(HTTP_Server *server,
int http_server_wakeup(HTTP_Server *server); int http_server_wakeup(HTTP_Server *server);
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
// the server wants monitored. Returns 0 on success, -1 if // the server wants monitored.
// the event register's capacity isn't large enough. void http_server_register_events(HTTP_Server *server,
int http_server_register_events(HTTP_Server *server,
EventRegister *reg); EventRegister *reg);
// The caller has waited for poll() to return and some // The caller has waited for poll() to return and some
@@ -1264,6 +1268,10 @@ typedef struct {
bool http_server_next_request(HTTP_Server *server, bool http_server_next_request(HTTP_Server *server,
HTTP_Request **request, HTTP_ResponseBuilder *builder); HTTP_Request **request, HTTP_ResponseBuilder *builder);
// TODO: comment
int http_server_wait_request(HTTP_Server *server,
HTTP_Request **request, HTTP_ResponseBuilder *builder);
// This function is called to set the status code of // This function is called to set the status code of
// a request's response. If this function is called // a request's response. If this function is called
// after the other response builder functions, it will // after the other response builder functions, it will
+23 -12
View File
@@ -1,31 +1,42 @@
#include "../chttp.h" #include "../chttp.h"
#ifdef _WIN32
#define POLL WSAPoll
#else
#define POLL poll
#endif
int main(void) int main(void)
{ {
int ret;
HTTP_Client client; HTTP_Client client;
if (http_client_init(&client) < 0) ret = http_client_init(&client);
if (ret < 0) {
printf("Couldn't initialize client (%s)\n", http_strerror(ret));
return -1; return -1;
}
HTTP_RequestBuilder builder = http_client_get_builder(&client); HTTP_RequestBuilder builder = http_client_get_builder(&client);
http_request_builder_trace(builder, true);
http_request_builder_method(builder, HTTP_METHOD_GET); http_request_builder_method(builder, HTTP_METHOD_GET);
http_request_builder_target(builder, HTTP_STR("http://coz.is")); http_request_builder_target(builder, HTTP_STR("http://coz.is"));
http_request_builder_header(builder, HTTP_STR("Greeting: Hello from the cHTTP example!")); http_request_builder_header(builder, HTTP_STR("Greeting: Hello from the cHTTP example!"));
if (http_request_builder_send(builder) < 0)
return -1;
ret = http_request_builder_send(builder);
if (ret < 0) {
printf("Couldn't build request (%s)\n", http_strerror(ret));
return -1;
}
int result;
void *user; void *user;
HTTP_Response *response; HTTP_Response *response;
if (http_client_wait_response(&client, &response, &user) < 0) ret = http_client_wait_response(&client, &result, &user, &response);
if (ret < 0) {
printf("Couldn't wait for response (%s)\n", http_strerror(ret));
return -1; return -1;
}
printf("Received a response!\n"); if (result == HTTP_OK) {
printf("Received %d bytes\n", response->body.len);
} else {
printf("Couldn't receive response (%s)\n", http_strerror(result));
}
http_client_free(&client); http_client_free(&client);
return 0; return 0;
+16 -29
View File
@@ -1,52 +1,39 @@
#include "../chttp.h" #include "../chttp.h"
#ifdef _WIN32
#define POLL WSAPoll
#else
#define POLL poll
#endif
int main(void) int main(void)
{ {
int ret;
HTTP_Server server; HTTP_Server server;
if (http_server_init(&server) < 0) ret = http_server_init(&server);
if (ret < 0) {
fprintf(stderr, "Couldn't initialize server (%s)\n", http_strerror(ret));
return -1; return -1;
}
http_server_set_reuse_addr(&server, true); http_server_set_reuse_addr(&server, true);
http_server_set_trace_bytes(&server, true); http_server_set_trace_bytes(&server, true);
if (http_server_listen_tcp(&server, HTTP_STR("127.0.0.1"), 8080) < 0) ret = http_server_listen_tcp(&server, HTTP_STR("127.0.0.1"), 8080);
if (ret < 0) {
fprintf(stderr, "Couldn't start listening (%s)\n", http_strerror(ret));
return -1; return -1;
}
for (;;) { for (;;) {
void *ptrs[HTTP_SERVER_POLL_CAPACITY];
struct pollfd polled[HTTP_SERVER_POLL_CAPACITY];
EventRegister reg = {
.ptrs=ptrs,
.polled=polled,
.max_polled=HTTP_SERVER_POLL_CAPACITY,
.num_polled=0,
};
if (http_server_register_events(&server, &reg) < 0)
return -1;
if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1);
if (http_server_process_events(&server, &reg) < 0)
return -1;
HTTP_Request *request; HTTP_Request *request;
HTTP_ResponseBuilder builder; HTTP_ResponseBuilder builder;
if (http_server_next_request(&server, &request, &builder)) { int ret = http_server_wait_request(&server, &request, &builder);
if (ret < 0) {
fprintf(stderr, "Couldn't wait for request (%s)\n", http_strerror(ret));
return -1;
}
http_response_builder_status(builder, 200); http_response_builder_status(builder, 200);
http_response_builder_body(builder, HTTP_STR("Hello, world!")); http_response_builder_body(builder, HTTP_STR("Hello, world!"));
http_response_builder_send(builder); http_response_builder_send(builder);
} }
}
http_server_free(&server); http_server_free(&server);
return 0; return 0;
-2
View File
@@ -57,7 +57,6 @@ 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/thread.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")
@@ -75,7 +74,6 @@ 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/thread.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")
+14
View File
@@ -94,3 +94,17 @@ void print_bytes(HTTP_String prefix, HTTP_String src)
} }
putc('\n', stream); putc('\n', stream);
} }
char *http_strerror(int code)
{
switch (code) {
case HTTP_OK: return "No error";
case HTTP_ERROR_UNSPECIFIED: return "Unspecified error";
case HTTP_ERROR_OOM: return "Out of memory";
case HTTP_ERROR_BADURL: return "Invalid URL";
case HTTP_ERROR_REQLIMIT: return "Parallel request limit reached";
case HTTP_ERROR_BADHANDLE: return "Invalid handle";
case HTTP_ERROR_NOTLS: return "TLS support not built-in";
}
return "???";
}
+29
View File
@@ -1,4 +1,27 @@
enum {
HTTP_OK = 0,
// A generic error occurred
HTTP_ERROR_UNSPECIFIED = -1,
// Out of memory
HTTP_ERROR_OOM = -2,
// Invalid URL
HTTP_ERROR_BADURL = -3,
// Parallel request limit reached
HTTP_ERROR_REQLIMIT = -4,
// Invalid handle
HTTP_ERROR_BADHANDLE = -5,
// TLS support not built-in
HTTP_ERROR_NOTLS = -6,
};
// String type used throughout cHTTP. // String type used throughout cHTTP.
typedef struct { typedef struct {
char *ptr; char *ptr;
@@ -23,6 +46,9 @@ HTTP_String http_trim(HTTP_String s);
// This is primarily used for debugging purposes. // This is primarily used for debugging purposes.
void print_bytes(HTTP_String prefix, HTTP_String src); void print_bytes(HTTP_String prefix, HTTP_String src);
// TODO: comment
char *http_strerror(int code);
// Macro to simplify converting string literals to // Macro to simplify converting string literals to
// HTTP_String. // HTTP_String.
// //
@@ -51,3 +77,6 @@ void print_bytes(HTTP_String prefix, HTTP_String src);
// Useful for passing HTTP_String to printf-style functions with "%.*s" format. // Useful for passing HTTP_String to printf-style functions with "%.*s" format.
// Example: printf("%.*s", HTTP_UNPACK(str)); // Example: printf("%.*s", HTTP_UNPACK(str));
#define HTTP_UNPACK(X) (X).len, (X).ptr #define HTTP_UNPACK(X) (X).len, (X).ptr
// TODO: comment
#define HTTP_UNREACHABLE __builtin_trap()
+48 -60
View File
@@ -21,10 +21,8 @@ int http_client_init(HTTP_Client *client)
client->num_ready = 0; client->num_ready = 0;
client->ready_head = 0; client->ready_head = 0;
if (socket_manager_init(&client->sockets, return socket_manager_init(&client->sockets,
client->socket_pool, HTTP_CLIENT_CAPACITY) < 0) client->socket_pool, HTTP_CLIENT_CAPACITY);
return -1;
return 0;
} }
void http_client_free(HTTP_Client *client) void http_client_free(HTTP_Client *client)
@@ -56,9 +54,7 @@ void http_client_set_output_limit(HTTP_Client *client, uint32_t limit)
int http_client_wakeup(HTTP_Client *client) int http_client_wakeup(HTTP_Client *client)
{ {
if (socket_manager_wakeup(&client->sockets) < 0) return socket_manager_wakeup(&client->sockets);
return -1;
return 0;
} }
// Get a connection pointer from a request builder. // Get a connection pointer from a request builder.
@@ -234,7 +230,7 @@ void http_request_builder_target(HTTP_RequestBuilder builder,
char *url_copy = malloc(url.len); char *url_copy = malloc(url.len);
if (url_copy == NULL) { if (url_copy == NULL) {
conn->state = HTTP_CLIENT_CONN_COMPLETE; conn->state = HTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; conn->result = HTTP_ERROR_OOM;
return; return;
} }
memcpy(url_copy, url.ptr, url.len); memcpy(url_copy, url.ptr, url.len);
@@ -245,14 +241,14 @@ void http_request_builder_target(HTTP_RequestBuilder builder,
// Parse the copied URL (all url.* pointers will reference url_buffer) // Parse the copied URL (all url.* pointers will reference url_buffer)
if (http_parse_url(conn->url_buffer.ptr, conn->url_buffer.len, &conn->url) < 0) { if (http_parse_url(conn->url_buffer.ptr, conn->url_buffer.len, &conn->url) < 0) {
conn->state = HTTP_CLIENT_CONN_COMPLETE; conn->state = HTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; conn->result = HTTP_ERROR_BADURL;
return; return;
} }
if (!http_streq(conn->url.scheme, HTTP_STR("http")) && if (!http_streq(conn->url.scheme, HTTP_STR("http")) &&
!http_streq(conn->url.scheme, HTTP_STR("https"))) { !http_streq(conn->url.scheme, HTTP_STR("https"))) {
conn->state = HTTP_CLIENT_CONN_COMPLETE; conn->state = HTTP_CLIENT_CONN_COMPLETE;
conn->result = -1; conn->result = HTTP_ERROR_BADURL;
return; return;
} }
@@ -260,7 +256,9 @@ void http_request_builder_target(HTTP_RequestBuilder builder,
if (conn->url.path.len == 0) if (conn->url.path.len == 0)
byte_queue_write(&conn->output, "/", 1); byte_queue_write(&conn->output, "/", 1);
else else
byte_queue_write(&conn->output, conn->url.path.ptr, conn->url.path.len); byte_queue_write(&conn->output,
conn->url.path.ptr,
conn->url.path.len);
// Write query string // Write query string
HTTP_String query = conn->url.query; HTTP_String query = conn->url.query;
@@ -352,47 +350,45 @@ void http_request_builder_body(HTTP_RequestBuilder builder,
byte_queue_write(&conn->output, str.ptr, str.len); byte_queue_write(&conn->output, str.ptr, str.len);
} }
static int static ConnectTarget url_to_connect_target(HTTP_URL url)
url_to_connect_target(HTTP_URL url,
ConnectTarget *target)
{ {
HTTP_Authority authority = url.authority; HTTP_Authority authority = url.authority;
ConnectTarget target;
if (authority.port < 1) { if (authority.port < 1) {
if (http_streq(url.scheme, HTTP_STR("https"))) if (http_streq(url.scheme, HTTP_STR("https")))
target->port = 443; target.port = 443;
else else
target->port = 80; target.port = 80;
} else { } else {
target->port = authority.port; target.port = authority.port;
} }
// Set up target based on host type
if (authority.host.mode == HTTP_HOST_MODE_NAME) { if (authority.host.mode == HTTP_HOST_MODE_NAME) {
target->type = CONNECT_TARGET_NAME; target.type = CONNECT_TARGET_NAME;
target->name = authority.host.name; target.name = authority.host.name;
} else if (authority.host.mode == HTTP_HOST_MODE_IPV4) { } else if (authority.host.mode == HTTP_HOST_MODE_IPV4) {
target->type = CONNECT_TARGET_IPV4; target.type = CONNECT_TARGET_IPV4;
target->ipv4 = authority.host.ipv4; target.ipv4 = authority.host.ipv4;
} else if (authority.host.mode == HTTP_HOST_MODE_IPV6) { } else if (authority.host.mode == HTTP_HOST_MODE_IPV6) {
target->type = CONNECT_TARGET_IPV6; target.type = CONNECT_TARGET_IPV6;
target->ipv6 = authority.host.ipv6; target.ipv6 = authority.host.ipv6;
} else { } else {
return -1; HTTP_UNREACHABLE;
} }
return 0; return target;
} }
int http_request_builder_send(HTTP_RequestBuilder builder) int http_request_builder_send(HTTP_RequestBuilder builder)
{ {
HTTP_Client *client = builder.client; HTTP_Client *client = builder.client;
if (client == NULL) if (client == NULL)
return -1; return HTTP_ERROR_REQLIMIT;
HTTP_ClientConn *conn = request_builder_to_conn(builder); HTTP_ClientConn *conn = request_builder_to_conn(builder);
if (conn == NULL) if (conn == NULL)
return -1; return HTTP_ERROR_BADHANDLE;
if (conn->state == HTTP_CLIENT_CONN_COMPLETE) if (conn->state == HTTP_CLIENT_CONN_COMPLETE)
goto error; // Early completion due to an error goto error; // Early completion due to an error
@@ -409,17 +405,14 @@ int http_request_builder_send(HTTP_RequestBuilder builder)
if (byte_queue_error(&conn->output)) if (byte_queue_error(&conn->output))
goto error; goto error;
ConnectTarget target; ConnectTarget target = url_to_connect_target(conn->url);
if (url_to_connect_target(conn->url, &target) < 0)
goto error;
bool secure = http_streq(conn->url.scheme, HTTP_STR("https")); bool secure = http_streq(conn->url.scheme, HTTP_STR("https"));
if (socket_connect(&client->sockets, 1, &target, secure, conn) < 0) if (socket_connect(&client->sockets, 1, &target, secure, conn) < 0)
goto error; goto error;
conn->state = HTTP_CLIENT_CONN_FLUSHING; conn->state = HTTP_CLIENT_CONN_FLUSHING;
conn->gen++; conn->gen++;
return 0; return HTTP_OK;
error: error:
conn->state = HTTP_CLIENT_CONN_FREE; conn->state = HTTP_CLIENT_CONN_FREE;
@@ -427,7 +420,7 @@ error:
byte_queue_free(&conn->input); byte_queue_free(&conn->input);
byte_queue_free(&conn->output); byte_queue_free(&conn->output);
client->num_conns--; client->num_conns--;
return -1; return conn->result;
} }
static void save_one_cookie(HTTP_CookieJar *cookie_jar, static void save_one_cookie(HTTP_CookieJar *cookie_jar,
@@ -500,22 +493,20 @@ static void save_cookies(HTTP_CookieJar *cookie_jar,
save_one_cookie(cookie_jar, headers[i], domain, path); save_one_cookie(cookie_jar, headers[i], domain, path);
} }
int http_client_register_events(HTTP_Client *client, void http_client_register_events(HTTP_Client *client,
EventRegister *reg) EventRegister *reg)
{ {
if (socket_manager_register_events(&client->sockets, reg) < 0) socket_manager_register_events(&client->sockets, reg);
return -1;
return 0;
} }
int http_client_process_events(HTTP_Client *client, int http_client_process_events(HTTP_Client *client,
EventRegister *reg) EventRegister *reg)
{ {
SocketEvent events[HTTP_CLIENT_CAPACITY]; SocketEvent events[HTTP_CLIENT_CAPACITY];
int num_events = socket_manager_translate_events( int ret = socket_manager_translate_events(&client->sockets, events, reg);
&client->sockets, events, reg); if (ret < 0)
if (num_events < 0) return ret;
return -1; int num_events = ret;
for (int i = 0; i < num_events; i++) { for (int i = 0; i < num_events; i++) {
@@ -638,11 +629,11 @@ int http_client_process_events(HTTP_Client *client,
} }
} }
return 0; return HTTP_OK;
} }
bool http_client_next_response(HTTP_Client *client, bool http_client_next_response(HTTP_Client *client,
HTTP_Response **response, void **user) int *result, void **user, HTTP_Response **response)
{ {
if (client->num_ready == 0) if (client->num_ready == 0)
return false; return false;
@@ -653,13 +644,13 @@ bool http_client_next_response(HTTP_Client *client,
assert(conn->state == HTTP_CLIENT_CONN_COMPLETE); assert(conn->state == HTTP_CLIENT_CONN_COMPLETE);
if (conn->result == 0) { *result = conn->result;
*user = conn->user;
if (conn->result == HTTP_OK) {
*response = &conn->response; *response = &conn->response;
} else { } else {
assert(conn->result == -1);
*response = NULL; *response = NULL;
} }
*user = conn->user;
return true; return true;
} }
@@ -695,30 +686,27 @@ void http_free_response(HTTP_Response *response)
#define POLL poll #define POLL poll
#endif #endif
int http_client_wait_response(HTTP_Client *client, HTTP_Response **response, void **user) int http_client_wait_response(HTTP_Client *client,
int *result, void **user, HTTP_Response **response)
{ {
for (;;) { for (;;) {
void *ptrs[HTTP_CLIENT_POLL_CAPACITY]; void *ptrs[HTTP_CLIENT_POLL_CAPACITY];
struct pollfd polled[HTTP_CLIENT_POLL_CAPACITY]; struct pollfd polled[HTTP_CLIENT_POLL_CAPACITY];
EventRegister reg = { EventRegister reg = { ptrs, polled, 0 };
.ptrs=ptrs, http_client_register_events(client, &reg);
.polled=polled,
.num_polled=0,
.max_polled=HTTP_CLIENT_POLL_CAPACITY,
};
if (http_client_register_events(client, &reg) < 0)
return -1;
if (reg.num_polled > 0) if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1); POLL(reg.polled, reg.num_polled, -1);
if (http_client_process_events(client, &reg) < 0) int ret = http_client_process_events(client, &reg);
return -1; if (ret < 0)
return ret;
if (http_client_next_response(client, response, user)) if (http_client_next_response(client, result, user, response))
break; break;
} }
return 0; return HTTP_OK;
} }
+5 -5
View File
@@ -196,9 +196,8 @@ void http_request_builder_body(HTTP_RequestBuilder builder,
int http_request_builder_send(HTTP_RequestBuilder builder); int http_request_builder_send(HTTP_RequestBuilder builder);
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
// the client wants monitored. Returns 0 on success, -1 if // the client wants monitored.
// the event register's capacity isn't large enough. void http_client_register_events(HTTP_Client *client,
int http_client_register_events(HTTP_Client *client,
EventRegister *reg); EventRegister *reg);
// The caller has waited for poll() to return and some // The caller has waited for poll() to return and some
@@ -215,10 +214,11 @@ int http_client_process_events(HTTP_Client *client,
// http_free_response function. // http_free_response function.
// TODO: Better comment talking about output arguments // TODO: Better comment talking about output arguments
bool http_client_next_response(HTTP_Client *client, bool http_client_next_response(HTTP_Client *client,
HTTP_Response **response, void **user); int *result, void **user, HTTP_Response **response);
// TODO: comment // TODO: comment
int http_client_wait_response(HTTP_Client *client, HTTP_Response **response, void **user); int http_client_wait_response(HTTP_Client *client,
int *result, void **user, HTTP_Response **response);
// Free a response object. You can't access its fields // Free a response object. You can't access its fields
// again after this. // again after this.
-1
View File
@@ -15,7 +15,6 @@
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h>
#include <poll.h> #include <poll.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
+41 -28
View File
@@ -34,10 +34,8 @@ int http_server_init(HTTP_Server *server)
server->num_ready = 0; server->num_ready = 0;
server->ready_head = 0; server->ready_head = 0;
if (socket_manager_init(&server->sockets, return socket_manager_init(&server->sockets,
server->socket_pool, HTTP_SERVER_CAPACITY) < 0) server->socket_pool, HTTP_SERVER_CAPACITY);
return -1;
return 0;
} }
void http_server_free(HTTP_Server *server) void http_server_free(HTTP_Server *server)
@@ -82,45 +80,35 @@ void http_server_set_backlog(HTTP_Server *server, int backlog)
int http_server_listen_tcp(HTTP_Server *server, int http_server_listen_tcp(HTTP_Server *server,
HTTP_String addr, Port port) HTTP_String addr, Port port)
{ {
if (socket_manager_listen_tcp(&server->sockets, addr, return socket_manager_listen_tcp(&server->sockets,
port, server->backlog, server->reuse_addr) < 0) addr, port, server->backlog, server->reuse_addr);
return -1;
return 0;
} }
int http_server_listen_tls(HTTP_Server *server, int http_server_listen_tls(HTTP_Server *server,
HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String addr, Port port, HTTP_String cert_file_name,
HTTP_String key_file_name) HTTP_String key_file_name)
{ {
if (socket_manager_listen_tls(&server->sockets, addr, return socket_manager_listen_tls(&server->sockets,
port, server->backlog, server->reuse_addr, addr, port, server->backlog, server->reuse_addr,
cert_file_name, key_file_name) < 0) cert_file_name, key_file_name);
return -1;
return 0;
} }
int http_server_add_certificate(HTTP_Server *server, int http_server_add_certificate(HTTP_Server *server,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
{ {
if (socket_manager_add_certificate(&server->sockets, return socket_manager_add_certificate(&server->sockets,
domain, cert_file, key_file) < 0) domain, cert_file, key_file);
return -1;
return 0;
} }
int http_server_wakeup(HTTP_Server *server) int http_server_wakeup(HTTP_Server *server)
{ {
if (socket_manager_wakeup(&server->sockets) < 0) return socket_manager_wakeup(&server->sockets);
return -1;
return 0;
} }
int http_server_register_events(HTTP_Server *server, void http_server_register_events(HTTP_Server *server,
EventRegister *reg) EventRegister *reg)
{ {
if (socket_manager_register_events(&server->sockets, reg) < 0) socket_manager_register_events(&server->sockets, reg);
return -1;
return 0;
} }
// Look at the head of the input buffer to see if // Look at the head of the input buffer to see if
@@ -233,9 +221,10 @@ int http_server_process_events(HTTP_Server *server,
EventRegister *reg) EventRegister *reg)
{ {
SocketEvent events[HTTP_SERVER_CAPACITY]; SocketEvent events[HTTP_SERVER_CAPACITY];
int num_events = socket_manager_translate_events(&server->sockets, events, reg); int ret = socket_manager_translate_events(&server->sockets, events, reg);
if (num_events < 0) if (ret < 0)
return -1; return ret;
int num_events = ret;
for (int i = 0; i < num_events; i++) { for (int i = 0; i < num_events; i++) {
@@ -277,7 +266,7 @@ int http_server_process_events(HTTP_Server *server,
} }
} }
return 0; return HTTP_OK;
} }
bool http_server_next_request(HTTP_Server *server, bool http_server_next_request(HTTP_Server *server,
@@ -296,6 +285,30 @@ bool http_server_next_request(HTTP_Server *server,
return true; return true;
} }
int http_server_wait_request(HTTP_Server *server,
HTTP_Request **request, HTTP_ResponseBuilder *builder)
{
for (;;) {
void *ptrs[HTTP_SERVER_POLL_CAPACITY];
struct pollfd polled[HTTP_SERVER_POLL_CAPACITY];
EventRegister reg = { ptrs, polled, 0 };
http_server_register_events(server, &reg);
if (reg.num_polled > 0)
POLL(reg.polled, reg.num_polled, -1);
int ret = http_server_process_events(server, &reg);
if (ret < 0)
return ret;
if (http_server_next_request(server, request, builder))
break;
}
return HTTP_OK;
}
// Get a connection pointer from a response builder. // Get a connection pointer from a response builder.
// If the builder is invalid, returns NULL. // If the builder is invalid, returns NULL.
// Note that only connections in the responding states // Note that only connections in the responding states
+6 -3
View File
@@ -178,9 +178,8 @@ int http_server_add_certificate(HTTP_Server *server,
int http_server_wakeup(HTTP_Server *server); int http_server_wakeup(HTTP_Server *server);
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
// the server wants monitored. Returns 0 on success, -1 if // the server wants monitored.
// the event register's capacity isn't large enough. void http_server_register_events(HTTP_Server *server,
int http_server_register_events(HTTP_Server *server,
EventRegister *reg); EventRegister *reg);
// The caller has waited for poll() to return and some // The caller has waited for poll() to return and some
@@ -206,6 +205,10 @@ typedef struct {
bool http_server_next_request(HTTP_Server *server, bool http_server_next_request(HTTP_Server *server,
HTTP_Request **request, HTTP_ResponseBuilder *builder); HTTP_Request **request, HTTP_ResponseBuilder *builder);
// TODO: comment
int http_server_wait_request(HTTP_Server *server,
HTTP_Request **request, HTTP_ResponseBuilder *builder);
// This function is called to set the status code of // This function is called to set the status code of
// a request's response. If this function is called // a request's response. If this function is called
// after the other response builder functions, it will // after the other response builder functions, it will
+69 -157
View File
@@ -39,7 +39,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
WSADATA wsaData; WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2); WORD wVersionRequested = MAKEWORD(2, 2);
if (WSAStartup(wVersionRequested, &wsaData)) if (WSAStartup(wVersionRequested, &wsaData))
return -1; return HTTP_ERROR_UNSPECIFIED;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET && *global_cleanup) if (sock == INVALID_SOCKET && *global_cleanup)
@@ -49,7 +49,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
// Bind to loopback address with port 0 (dynamic port assignment) // Bind to loopback address with port 0 (dynamic port assignment)
@@ -64,21 +64,21 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) {
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
// Optional: Set socket to non-blocking mode // Optional: Set socket to non-blocking mode
@@ -88,20 +88,20 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
closesocket(sock); closesocket(sock);
if (*global_cleanup) if (*global_cleanup)
WSACleanup(); WSACleanup();
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
*a = sock; *a = sock;
*b = sock; *b = sock;
return 0; return HTTP_OK;
#else #else
*global_cleanup = false; *global_cleanup = false;
int fds[2]; int fds[2];
if (pipe(fds) < 0) if (pipe(fds) < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
*a = fds[0]; *a = fds[0];
*b = fds[1]; *b = fds[1];
return 0; return HTTP_OK;
#endif #endif
} }
@@ -110,20 +110,20 @@ static int set_socket_blocking(NATIVE_SOCKET sock, bool value)
#ifdef _WIN32 #ifdef _WIN32
u_long mode = !value; u_long mode = !value;
if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR)
return -1; return HTTP_ERROR_UNSPECIFIED;
return HTTP_OK;
#endif #endif
#ifdef __linux__ #ifdef __linux__
int flags = fcntl(sock, F_GETFL, 0); int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) if (flags < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
if (value) flags &= ~O_NONBLOCK; if (value) flags &= ~O_NONBLOCK;
else flags |= O_NONBLOCK; else flags |= O_NONBLOCK;
if (fcntl(sock, F_SETFL, flags) < 0) if (fcntl(sock, F_SETFL, flags) < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
return HTTP_OK;
#endif #endif
return 0;
} }
static NATIVE_SOCKET create_listen_socket(HTTP_String addr, static NATIVE_SOCKET create_listen_socket(HTTP_String addr,
@@ -193,13 +193,15 @@ 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)
{ {
if (mutex_init(&sm->mutex) < 0)
return -1;
sm->plain_sock = NATIVE_SOCKET_INVALID; sm->plain_sock = NATIVE_SOCKET_INVALID;
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
if (create_socket_pair(&sm->wait_sock, &sm->signal_sock,
&sm->global_cleanup) < 0) int ret = create_socket_pair(
return -1; &sm->wait_sock,
&sm->signal_sock,
&sm->global_cleanup);
if (ret < 0) return ret;
sm->at_least_one_secure_connect = false; sm->at_least_one_secure_connect = false;
sm->num_used = 0; sm->num_used = 0;
@@ -210,7 +212,7 @@ int socket_manager_init(SocketManager *sm, Socket *socks,
socks[i].state = SOCKET_STATE_FREE; socks[i].state = SOCKET_STATE_FREE;
socks[i].gen = 1; socks[i].gen = 1;
} }
return 0; return HTTP_OK;
} }
void socket_manager_free(SocketManager *sm) void socket_manager_free(SocketManager *sm)
@@ -233,8 +235,6 @@ void socket_manager_free(SocketManager *sm)
if (sm->global_cleanup) if (sm->global_cleanup)
WSACleanup(); WSACleanup();
#endif #endif
mutex_free(&sm->mutex);
} }
int socket_manager_listen_tcp(SocketManager *sm, int socket_manager_listen_tcp(SocketManager *sm,
@@ -242,13 +242,13 @@ int socket_manager_listen_tcp(SocketManager *sm,
bool reuse_addr) bool reuse_addr)
{ {
if (sm->plain_sock != NATIVE_SOCKET_INVALID) if (sm->plain_sock != NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog); sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
if (sm->plain_sock == NATIVE_SOCKET_INVALID) if (sm->plain_sock == NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
return 0; return HTTP_OK;
} }
int socket_manager_listen_tls(SocketManager *sm, int socket_manager_listen_tls(SocketManager *sm,
@@ -256,35 +256,39 @@ int socket_manager_listen_tls(SocketManager *sm,
bool reuse_addr, HTTP_String cert_file, bool reuse_addr, HTTP_String cert_file,
HTTP_String key_file) HTTP_String key_file)
{ {
#ifndef HTTPS_ENABLED
return HTTP_ERROR_NOTLS;
#endif
if (sm->secure_sock != NATIVE_SOCKET_INVALID) if (sm->secure_sock != NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog); sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
if (sm->secure_sock == NATIVE_SOCKET_INVALID) if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
if (server_secure_context_init(&sm->server_secure_context, if (server_secure_context_init(&sm->server_secure_context,
cert_file, key_file) < 0) { cert_file, key_file) < 0) {
CLOSE_NATIVE_SOCKET(sm->secure_sock); CLOSE_NATIVE_SOCKET(sm->secure_sock);
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
return 0; return HTTP_OK;
} }
int socket_manager_add_certificate(SocketManager *sm, int socket_manager_add_certificate(SocketManager *sm,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
{ {
if (sm->secure_sock == NATIVE_SOCKET_INVALID) if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1; return HTTP_ERROR_UNSPECIFIED;
int ret = server_secure_context_add_certificate( int ret = server_secure_context_add_certificate(
&sm->server_secure_context, domain, cert_file, key_file); &sm->server_secure_context, domain, cert_file, key_file);
if (ret < 0) if (ret < 0)
return -1; return ret;
return 0; return HTTP_OK;
} }
static bool is_secure(Socket *s) static bool is_secure(Socket *s)
@@ -680,27 +684,21 @@ static void socket_update(Socket *s)
int socket_manager_wakeup(SocketManager *sm) int socket_manager_wakeup(SocketManager *sm)
{ {
if (mutex_lock(&sm->mutex) < 0) // NOTE: It's assumed send/write operate atomically
return -1; // on The descriptor.
// Send a byte through the signal socket to wake up any thread
// blocked on poll() with the wait socket
char byte = 1; char byte = 1;
int ret = 0; int ret = 0;
#ifdef _WIN32 #ifdef _WIN32
if (send(sm->signal_sock, &byte, 1, 0) < 0) if (send(sm->signal_sock, &byte, 1, 0) < 0)
ret = -1; return HTTP_ERROR_UNSPECIFIED;
#else #else
if (write(sm->signal_sock, &byte, 1) < 0) if (write(sm->signal_sock, &byte, 1) < 0)
ret = -1; return HTTP_ERROR_UNSPECIFIED;
#endif #endif
return HTTP_OK;
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
static void socket_manager_register_events_nolock( void socket_manager_register_events(
SocketManager *sm, EventRegister *reg) SocketManager *sm, EventRegister *reg)
{ {
reg->num_polled = 0; reg->num_polled = 0;
@@ -760,19 +758,6 @@ static void socket_manager_register_events_nolock(
} }
} }
int socket_manager_register_events(SocketManager *sm,
EventRegister *reg)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
socket_manager_register_events_nolock(sm, reg);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return 0;
}
static SocketHandle static SocketHandle
socket_to_handle(SocketManager *sm, Socket *s) socket_to_handle(SocketManager *sm, Socket *s)
{ {
@@ -790,7 +775,7 @@ static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle)
return &sm->sockets[idx]; return &sm->sockets[idx];
} }
static int socket_manager_translate_events_nolock( int socket_manager_translate_events(
SocketManager *sm, SocketEvent *events, SocketManager *sm, SocketEvent *events,
EventRegister *reg) EventRegister *reg)
{ {
@@ -913,19 +898,6 @@ static int socket_manager_translate_events_nolock(
return num_events; return num_events;
} }
int socket_manager_translate_events(SocketManager *sm,
SocketEvent *events, EventRegister *reg)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret = socket_manager_translate_events_nolock(sm, events, reg);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
}
static int resolve_connect_targets(ConnectTarget *targets, static int resolve_connect_targets(ConnectTarget *targets,
int num_targets, AddressAndPort *resolved, int max_resolved) int num_targets, AddressAndPort *resolved, int max_resolved)
{ {
@@ -946,7 +918,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1); RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
if (name == NULL) { if (name == NULL) {
free_addr_list(resolved, num_resolved); free_addr_list(resolved, num_resolved);
return -1; return HTTP_ERROR_OOM;
} }
name->refs = 0; name->refs = 0;
memcpy(name->data, targets[i].name.ptr, targets[i].name.len); memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
@@ -956,7 +928,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
// 512 bytes is more than enough for a DNS hostname (max 253 chars) // 512 bytes is more than enough for a DNS hostname (max 253 chars)
char hostname[1<<9]; char hostname[1<<9];
if (targets[i].name.len >= (int) sizeof(hostname)) if (targets[i].name.len >= (int) sizeof(hostname))
return -1; return HTTP_ERROR_OOM;
memcpy(hostname, targets[i].name.ptr, targets[i].name.len); memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
hostname[targets[i].name.len] = '\0'; hostname[targets[i].name.len] = '\0';
#endif #endif
@@ -968,7 +940,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
free(name); free(name);
#endif #endif
free_addr_list(resolved, num_resolved); free_addr_list(resolved, num_resolved);
return -1; return HTTP_ERROR_UNSPECIFIED;
} }
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
@@ -1040,17 +1012,17 @@ int socket_connect(SocketManager *sm, int num_targets,
ConnectTarget *targets, bool secure, void *user) ConnectTarget *targets, bool secure, void *user)
{ {
if (sm->num_used == sm->max_used) if (sm->num_used == sm->max_used)
return -1; return HTTP_ERROR_UNSPECIFIED;
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
if (!sm->at_least_one_secure_connect) { if (!sm->at_least_one_secure_connect) {
if (client_secure_context_init(&sm->client_secure_context) < 0) if (client_secure_context_init(&sm->client_secure_context) < 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
sm->at_least_one_secure_connect = true; sm->at_least_one_secure_connect = true;
} }
#else #else
if (secure) if (secure)
return -1; return HTTP_ERROR_NOTLS;
#endif #endif
AddressAndPort resolved[MAX_CONNECT_TARGETS]; AddressAndPort resolved[MAX_CONNECT_TARGETS];
@@ -1058,7 +1030,7 @@ int socket_connect(SocketManager *sm, int num_targets,
targets, num_targets, resolved, MAX_CONNECT_TARGETS); targets, num_targets, resolved, MAX_CONNECT_TARGETS);
if (num_resolved <= 0) if (num_resolved <= 0)
return -1; return HTTP_ERROR_UNSPECIFIED;
Socket *s = sm->sockets; Socket *s = sm->sockets;
while (s->state != SOCKET_STATE_FREE) { while (s->state != SOCKET_STATE_FREE) {
@@ -1075,7 +1047,7 @@ int socket_connect(SocketManager *sm, int num_targets,
s->next_addr = 0; s->next_addr = 0;
s->addrs = malloc(num_resolved * sizeof(AddressAndPort)); s->addrs = malloc(num_resolved * sizeof(AddressAndPort));
if (s->addrs == NULL) if (s->addrs == NULL)
return -1; return HTTP_ERROR_OOM;
for (int i = 0; i < num_resolved; i++) for (int i = 0; i < num_resolved; i++)
s->addrs[i] = resolved[i]; s->addrs[i] = resolved[i];
} }
@@ -1093,7 +1065,7 @@ int socket_connect(SocketManager *sm, int num_targets,
sm->num_used++; sm->num_used++;
socket_update(s); socket_update(s);
return 0; return HTTP_OK;
} }
static bool would_block(void) static bool would_block(void)
@@ -1115,7 +1087,7 @@ static bool interrupted(void)
#endif #endif
} }
static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, int socket_recv(SocketManager *sm, SocketHandle handle,
char *dst, int max) char *dst, int max)
{ {
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
@@ -1166,20 +1138,7 @@ static int socket_recv_nolock(SocketManager *sm, SocketHandle handle,
} }
} }
int socket_recv(SocketManager *sm, SocketHandle handle, int socket_send(SocketManager *sm, SocketHandle handle,
char *dst, int max)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret = socket_recv_nolock(sm, handle, dst, max);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
}
static int socket_send_nolock(SocketManager *sm, SocketHandle handle,
char *src, int len) char *src, int len)
{ {
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
@@ -1227,93 +1186,46 @@ static int socket_send_nolock(SocketManager *sm, SocketHandle handle,
} }
} }
int socket_send(SocketManager *sm, SocketHandle handle, void socket_close(SocketManager *sm, SocketHandle handle)
char *src, int len)
{ {
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret = socket_send_nolock(sm, handle, src, len);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
}
int socket_close(SocketManager *sm, SocketHandle handle)
{
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret; int ret;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
if (s == NULL) if (s == NULL)
ret = -1; return;
else {
// Only transition to SHUTDOWN if socket is not already DIED
if (s->state != SOCKET_STATE_DIED) { if (s->state != SOCKET_STATE_DIED) {
UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN); UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN);
s->events = 0; s->events = 0;
socket_update(s); socket_update(s);
} }
}
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
int socket_is_secure(SocketManager *sm, SocketHandle handle) bool socket_is_secure(SocketManager *sm, SocketHandle handle)
{ {
if (mutex_lock(&sm->mutex) < 0)
return -1;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
int ret;
if (s == NULL) if (s == NULL)
ret = -1; return false;
else return is_secure(s);
ret = is_secure(s);
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
int socket_set_user(SocketManager *sm, SocketHandle handle, void *user) void socket_set_user(SocketManager *sm, SocketHandle handle, void *user)
{ {
if (mutex_lock(&sm->mutex) < 0)
return -1;
int ret;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
if (s == NULL) if (s == NULL)
ret = -1; return;
else {
s->user = user; s->user = user;
ret = 0;
}
if (mutex_unlock(&sm->mutex) < 0)
return -1;
return ret;
} }
bool socket_ready(SocketManager *sm, SocketHandle handle) bool socket_ready(SocketManager *sm, SocketHandle handle)
{ {
if (mutex_lock(&sm->mutex) < 0) {
assert(0); // TODO
}
bool ready = false; bool ready = false;
Socket *s = handle_to_socket(sm, handle); Socket *s = handle_to_socket(sm, handle);
if (s && s->events == 0 && s->state != SOCKET_STATE_DIED) { if (s == NULL)
ready = true; return false;
}
if (mutex_unlock(&sm->mutex) < 0) { if (s->events == 0 && s->state != SOCKET_STATE_DIED)
assert(0); // TODO return true;
}
return ready; return false;
} }
+5 -13
View File
@@ -182,11 +182,6 @@ typedef struct {
// header. // header.
typedef struct { typedef struct {
// This guards access to the main thread using
// the manager from other threads calling the
// wakeup function.
Mutex mutex;
// 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
@@ -276,14 +271,11 @@ typedef struct {
void **ptrs; void **ptrs;
struct pollfd *polled; struct pollfd *polled;
int num_polled; int num_polled;
int max_polled;
} EventRegister; } EventRegister;
// Resets the event register with the list of descriptors // Resets the event register with the list of descriptors
// the socket manager wants monitored. Returns 0 on // the socket manager wants monitored.
// success, -1 if the event register's capacity isn't void socket_manager_register_events(SocketManager *sm,
// large enough.
int socket_manager_register_events(SocketManager *sm,
EventRegister *reg); EventRegister *reg);
// After poll() is called on the previously registered // After poll() is called on the previously registered
@@ -328,15 +320,15 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
int socket_send(SocketManager *sm, SocketHandle handle, int socket_send(SocketManager *sm, SocketHandle handle,
char *src, int len); char *src, int len);
int socket_close(SocketManager *sm, SocketHandle handle); void socket_close(SocketManager *sm, SocketHandle handle);
// Returns -1 on error, 0 if the socket was accepted // Returns -1 on error, 0 if the socket was accepted
// from the plaintext listener, or 1 if it was accepted // from the plaintext listener, or 1 if it was accepted
// by the secure listener. // by the secure listener.
int socket_is_secure(SocketManager *sm, SocketHandle handle); bool socket_is_secure(SocketManager *sm, SocketHandle handle);
// Set the user pointer of a socket // Set the user pointer of a socket
int socket_set_user(SocketManager *sm, SocketHandle handle, void *user); void socket_set_user(SocketManager *sm, SocketHandle handle, void *user);
// Returns true iff the socket is ready for reading or // Returns true iff the socket is ready for reading or
// writing. // writing.
-48
View File
@@ -1,48 +0,0 @@
int mutex_init(Mutex *mutex)
{
#ifdef _WIN32
InitializeCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_init(mutex, NULL))
return -1;
return 0;
#endif
}
int mutex_free(Mutex *mutex)
{
#ifdef _WIN32
DeleteCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_destroy(mutex))
return -1;
return 0;
#endif
}
int mutex_lock(Mutex *mutex)
{
#ifdef _WIN32
EnterCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_lock(mutex))
return -1;
return 0;
#endif
}
int mutex_unlock(Mutex *mutex)
{
#ifdef _WIN32
LeaveCriticalSection(mutex);
return 0;
#else
if (pthread_mutex_unlock(mutex))
return -1;
return 0;
#endif
}
-11
View File
@@ -1,11 +0,0 @@
#ifdef _WIN32
typedef CRITICAL_SECTION Mutex;
#else
typedef pthread_mutex_t Mutex;
#endif
int mutex_init(Mutex *mutex);
int mutex_free(Mutex *mutex);
int mutex_lock(Mutex *mutex);
int mutex_unlock(Mutex *mutex);