From c4c41653f448754d8591214298deb27233f4d4bd Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Wed, 26 Nov 2025 22:06:05 +0100 Subject: [PATCH] Add error codes --- chttp.c | 477 +++++++++++++++------------------------ chttp.h | 82 ++++--- examples/simple_client.c | 35 ++- examples/simple_server.c | 49 ++-- misc/amalg.py | 2 - src/basic.c | 14 ++ src/basic.h | 29 +++ src/client.c | 108 ++++----- src/client.h | 10 +- src/includes.h | 1 - src/server.c | 69 +++--- src/server.h | 9 +- src/socket.c | 234 ++++++------------- src/socket.h | 18 +- src/thread.c | 48 ---- src/thread.h | 11 - 16 files changed, 483 insertions(+), 713 deletions(-) delete mode 100644 src/thread.c delete mode 100644 src/thread.h diff --git a/chttp.c b/chttp.c index 3535356..c4a4f88 100644 --- a/chttp.c +++ b/chttp.c @@ -107,6 +107,20 @@ void print_bytes(HTTP_String prefix, HTTP_String src) 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 //////////////////////////////////////////////////////////////////////////////////////// @@ -1702,58 +1716,6 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out) 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 //////////////////////////////////////////////////////////////////////////////////////// @@ -2008,7 +1970,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData)) - return -1; + return HTTP_ERROR_UNSPECIFIED; sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 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 (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } // 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); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { closesocket(sock); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { closesocket(sock); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } // 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); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } *a = sock; *b = sock; - return 0; + return HTTP_OK; #else *global_cleanup = false; int fds[2]; if (pipe(fds) < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; *a = fds[0]; *b = fds[1]; - return 0; + return HTTP_OK; #endif } @@ -2079,20 +2041,20 @@ static int set_socket_blocking(NATIVE_SOCKET sock, bool value) #ifdef _WIN32 u_long mode = !value; if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) - return -1; + return HTTP_ERROR_UNSPECIFIED; + return HTTP_OK; #endif #ifdef __linux__ int flags = fcntl(sock, F_GETFL, 0); if (flags < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; if (value) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if (fcntl(sock, F_SETFL, flags) < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; + return HTTP_OK; #endif - - return 0; } 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 num_socks) { - if (mutex_init(&sm->mutex) < 0) - return -1; sm->plain_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID; - if (create_socket_pair(&sm->wait_sock, &sm->signal_sock, - &sm->global_cleanup) < 0) - return -1; + + int ret = create_socket_pair( + &sm->wait_sock, + &sm->signal_sock, + &sm->global_cleanup); + if (ret < 0) return ret; + sm->at_least_one_secure_connect = false; sm->num_used = 0; @@ -2179,7 +2143,7 @@ int socket_manager_init(SocketManager *sm, Socket *socks, socks[i].state = SOCKET_STATE_FREE; socks[i].gen = 1; } - return 0; + return HTTP_OK; } void socket_manager_free(SocketManager *sm) @@ -2202,8 +2166,6 @@ void socket_manager_free(SocketManager *sm) if (sm->global_cleanup) WSACleanup(); #endif - - mutex_free(&sm->mutex); } int socket_manager_listen_tcp(SocketManager *sm, @@ -2211,13 +2173,13 @@ int socket_manager_listen_tcp(SocketManager *sm, bool reuse_addr) { if (sm->plain_sock != NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog); 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, @@ -2225,35 +2187,39 @@ int socket_manager_listen_tls(SocketManager *sm, bool reuse_addr, HTTP_String cert_file, HTTP_String key_file) { +#ifndef HTTPS_ENABLED + return HTTP_ERROR_NOTLS; +#endif + if (sm->secure_sock != NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog); if (sm->secure_sock == NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; if (server_secure_context_init(&sm->server_secure_context, cert_file, key_file) < 0) { CLOSE_NATIVE_SOCKET(sm->secure_sock); sm->secure_sock = NATIVE_SOCKET_INVALID; - return -1; + return HTTP_ERROR_UNSPECIFIED; } - return 0; + return HTTP_OK; } int socket_manager_add_certificate(SocketManager *sm, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) { if (sm->secure_sock == NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; int ret = server_secure_context_add_certificate( &sm->server_secure_context, domain, cert_file, key_file); if (ret < 0) - return -1; + return ret; - return 0; + return HTTP_OK; } static bool is_secure(Socket *s) @@ -2649,27 +2615,21 @@ static void socket_update(Socket *s) int socket_manager_wakeup(SocketManager *sm) { - if (mutex_lock(&sm->mutex) < 0) - return -1; - - // Send a byte through the signal socket to wake up any thread - // blocked on poll() with the wait socket + // NOTE: It's assumed send/write operate atomically + // on The descriptor. char byte = 1; int ret = 0; #ifdef _WIN32 if (send(sm->signal_sock, &byte, 1, 0) < 0) - ret = -1; + return HTTP_ERROR_UNSPECIFIED; #else if (write(sm->signal_sock, &byte, 1) < 0) - ret = -1; + return HTTP_ERROR_UNSPECIFIED; #endif - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; + return HTTP_OK; } -static void socket_manager_register_events_nolock( +void socket_manager_register_events( SocketManager *sm, EventRegister *reg) { 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 socket_to_handle(SocketManager *sm, Socket *s) { @@ -2759,7 +2706,7 @@ static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle) return &sm->sockets[idx]; } -static int socket_manager_translate_events_nolock( +int socket_manager_translate_events( SocketManager *sm, SocketEvent *events, EventRegister *reg) { @@ -2882,19 +2829,6 @@ static int socket_manager_translate_events_nolock( 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, 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); if (name == NULL) { free_addr_list(resolved, num_resolved); - return -1; + return HTTP_ERROR_OOM; } name->refs = 0; 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) char hostname[1<<9]; if (targets[i].name.len >= (int) sizeof(hostname)) - return -1; + return HTTP_ERROR_OOM; memcpy(hostname, targets[i].name.ptr, targets[i].name.len); hostname[targets[i].name.len] = '\0'; #endif @@ -2937,7 +2871,7 @@ static int resolve_connect_targets(ConnectTarget *targets, free(name); #endif free_addr_list(resolved, num_resolved); - return -1; + return HTTP_ERROR_UNSPECIFIED; } 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) { if (sm->num_used == sm->max_used) - return -1; + return HTTP_ERROR_UNSPECIFIED; #ifdef HTTPS_ENABLED if (!sm->at_least_one_secure_connect) { if (client_secure_context_init(&sm->client_secure_context) < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; sm->at_least_one_secure_connect = true; } #else if (secure) - return -1; + return HTTP_ERROR_NOTLS; #endif AddressAndPort resolved[MAX_CONNECT_TARGETS]; @@ -3027,7 +2961,7 @@ int socket_connect(SocketManager *sm, int num_targets, targets, num_targets, resolved, MAX_CONNECT_TARGETS); if (num_resolved <= 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; Socket *s = sm->sockets; while (s->state != SOCKET_STATE_FREE) { @@ -3044,7 +2978,7 @@ int socket_connect(SocketManager *sm, int num_targets, s->next_addr = 0; s->addrs = malloc(num_resolved * sizeof(AddressAndPort)); if (s->addrs == NULL) - return -1; + return HTTP_ERROR_OOM; for (int i = 0; i < num_resolved; i++) s->addrs[i] = resolved[i]; } @@ -3062,7 +2996,7 @@ int socket_connect(SocketManager *sm, int num_targets, sm->num_used++; socket_update(s); - return 0; + return HTTP_OK; } static bool would_block(void) @@ -3084,7 +3018,7 @@ static bool interrupted(void) #endif } -static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, +int socket_recv(SocketManager *sm, SocketHandle handle, char *dst, int max) { 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, - 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, +int socket_send(SocketManager *sm, SocketHandle handle, char *src, int len) { 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, - char *src, int len) +void socket_close(SocketManager *sm, SocketHandle handle) { - 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; Socket *s = handle_to_socket(sm, handle); if (s == NULL) - ret = -1; - else { - // Only transition to SHUTDOWN if socket is not already DIED - if (s->state != SOCKET_STATE_DIED) { - UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN); - s->events = 0; - socket_update(s); - } + return; + + if (s->state != SOCKET_STATE_DIED) { + UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN); + s->events = 0; + 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); - - int ret; if (s == NULL) - ret = -1; - else - ret = is_secure(s); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; + return false; + return is_secure(s); } -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); if (s == NULL) - ret = -1; - else { - s->user = user; - ret = 0; - } + return; - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; + s->user = user; } bool socket_ready(SocketManager *sm, SocketHandle handle) { - if (mutex_lock(&sm->mutex) < 0) { - assert(0); // TODO - } - bool ready = false; Socket *s = handle_to_socket(sm, handle); - if (s && s->events == 0 && s->state != SOCKET_STATE_DIED) { - ready = true; - } + if (s == NULL) + return false; - if (mutex_unlock(&sm->mutex) < 0) { - assert(0); // TODO - } - return ready; + if (s->events == 0 && s->state != SOCKET_STATE_DIED) + return true; + + return false; } //////////////////////////////////////////////////////////////////////////////////////// @@ -3772,10 +3646,8 @@ int http_client_init(HTTP_Client *client) client->num_ready = 0; client->ready_head = 0; - if (socket_manager_init(&client->sockets, - client->socket_pool, HTTP_CLIENT_CAPACITY) < 0) - return -1; - return 0; + return socket_manager_init(&client->sockets, + client->socket_pool, HTTP_CLIENT_CAPACITY); } 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) { - if (socket_manager_wakeup(&client->sockets) < 0) - return -1; - return 0; + return socket_manager_wakeup(&client->sockets); } // 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); if (url_copy == NULL) { conn->state = HTTP_CLIENT_CONN_COMPLETE; - conn->result = -1; + conn->result = HTTP_ERROR_OOM; return; } 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) if (http_parse_url(conn->url_buffer.ptr, conn->url_buffer.len, &conn->url) < 0) { conn->state = HTTP_CLIENT_CONN_COMPLETE; - conn->result = -1; + conn->result = HTTP_ERROR_BADURL; return; } if (!http_streq(conn->url.scheme, HTTP_STR("http")) && !http_streq(conn->url.scheme, HTTP_STR("https"))) { conn->state = HTTP_CLIENT_CONN_COMPLETE; - conn->result = -1; + conn->result = HTTP_ERROR_BADURL; return; } @@ -4011,7 +3881,9 @@ void http_request_builder_target(HTTP_RequestBuilder builder, if (conn->url.path.len == 0) byte_queue_write(&conn->output, "/", 1); 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 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); } -static int -url_to_connect_target(HTTP_URL url, - ConnectTarget *target) +static ConnectTarget url_to_connect_target(HTTP_URL url) { HTTP_Authority authority = url.authority; + ConnectTarget target; if (authority.port < 1) { if (http_streq(url.scheme, HTTP_STR("https"))) - target->port = 443; + target.port = 443; else - target->port = 80; + target.port = 80; } else { - target->port = authority.port; + target.port = authority.port; } - // Set up target based on host type if (authority.host.mode == HTTP_HOST_MODE_NAME) { - target->type = CONNECT_TARGET_NAME; - target->name = authority.host.name; + target.type = CONNECT_TARGET_NAME; + target.name = authority.host.name; } else if (authority.host.mode == HTTP_HOST_MODE_IPV4) { - target->type = CONNECT_TARGET_IPV4; - target->ipv4 = authority.host.ipv4; + target.type = CONNECT_TARGET_IPV4; + target.ipv4 = authority.host.ipv4; } else if (authority.host.mode == HTTP_HOST_MODE_IPV6) { - target->type = CONNECT_TARGET_IPV6; - target->ipv6 = authority.host.ipv6; + target.type = CONNECT_TARGET_IPV6; + target.ipv6 = authority.host.ipv6; } else { - return -1; + HTTP_UNREACHABLE; } - return 0; + return target; } int http_request_builder_send(HTTP_RequestBuilder builder) { HTTP_Client *client = builder.client; if (client == NULL) - return -1; + return HTTP_ERROR_REQLIMIT; HTTP_ClientConn *conn = request_builder_to_conn(builder); if (conn == NULL) - return -1; + return HTTP_ERROR_BADHANDLE; if (conn->state == HTTP_CLIENT_CONN_COMPLETE) 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)) goto error; - ConnectTarget target; - if (url_to_connect_target(conn->url, &target) < 0) - goto error; - + ConnectTarget target = url_to_connect_target(conn->url); bool secure = http_streq(conn->url.scheme, HTTP_STR("https")); if (socket_connect(&client->sockets, 1, &target, secure, conn) < 0) goto error; conn->state = HTTP_CLIENT_CONN_FLUSHING; conn->gen++; - return 0; + return HTTP_OK; error: conn->state = HTTP_CLIENT_CONN_FREE; @@ -4178,7 +4045,7 @@ error: byte_queue_free(&conn->input); byte_queue_free(&conn->output); client->num_conns--; - return -1; + return conn->result; } 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); } -int http_client_register_events(HTTP_Client *client, +void http_client_register_events(HTTP_Client *client, EventRegister *reg) { - if (socket_manager_register_events(&client->sockets, reg) < 0) - return -1; - return 0; + socket_manager_register_events(&client->sockets, reg); } int http_client_process_events(HTTP_Client *client, EventRegister *reg) { SocketEvent events[HTTP_CLIENT_CAPACITY]; - int num_events = socket_manager_translate_events( - &client->sockets, events, reg); - if (num_events < 0) - return -1; + int ret = socket_manager_translate_events(&client->sockets, events, reg); + if (ret < 0) + return ret; + int num_events = ret; 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, - HTTP_Response **response, void **user) + int *result, void **user, HTTP_Response **response) { if (client->num_ready == 0) return false; @@ -4404,13 +4269,13 @@ bool http_client_next_response(HTTP_Client *client, 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; } else { - assert(conn->result == -1); *response = NULL; } - *user = conn->user; return true; } @@ -4446,32 +4311,29 @@ void http_free_response(HTTP_Response *response) #define POLL poll #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 (;;) { + void *ptrs[HTTP_CLIENT_POLL_CAPACITY]; struct pollfd polled[HTTP_CLIENT_POLL_CAPACITY]; - EventRegister reg = { - .ptrs=ptrs, - .polled=polled, - .num_polled=0, - .max_polled=HTTP_CLIENT_POLL_CAPACITY, - }; - if (http_client_register_events(client, ®) < 0) - return -1; + EventRegister reg = { ptrs, polled, 0 }; + http_client_register_events(client, ®); if (reg.num_polled > 0) POLL(reg.polled, reg.num_polled, -1); - if (http_client_process_events(client, ®) < 0) - return -1; + int ret = http_client_process_events(client, ®); + if (ret < 0) + return ret; - if (http_client_next_response(client, response, user)) + if (http_client_next_response(client, result, user, response)) break; } - return 0; + return HTTP_OK; } //////////////////////////////////////////////////////////////////////////////////////// @@ -4513,10 +4375,8 @@ int http_server_init(HTTP_Server *server) server->num_ready = 0; server->ready_head = 0; - if (socket_manager_init(&server->sockets, - server->socket_pool, HTTP_SERVER_CAPACITY) < 0) - return -1; - return 0; + return socket_manager_init(&server->sockets, + server->socket_pool, HTTP_SERVER_CAPACITY); } 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, HTTP_String addr, Port port) { - if (socket_manager_listen_tcp(&server->sockets, addr, - port, server->backlog, server->reuse_addr) < 0) - return -1; - return 0; + return socket_manager_listen_tcp(&server->sockets, + addr, port, server->backlog, server->reuse_addr); } int http_server_listen_tls(HTTP_Server *server, HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String key_file_name) { - if (socket_manager_listen_tls(&server->sockets, addr, - port, server->backlog, server->reuse_addr, - cert_file_name, key_file_name) < 0) - return -1; - return 0; + return socket_manager_listen_tls(&server->sockets, + addr, port, server->backlog, server->reuse_addr, + cert_file_name, key_file_name); } int http_server_add_certificate(HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) { - if (socket_manager_add_certificate(&server->sockets, - domain, cert_file, key_file) < 0) - return -1; - return 0; + return socket_manager_add_certificate(&server->sockets, + domain, cert_file, key_file); } int http_server_wakeup(HTTP_Server *server) { - if (socket_manager_wakeup(&server->sockets) < 0) - return -1; - return 0; + return socket_manager_wakeup(&server->sockets); } -int http_server_register_events(HTTP_Server *server, +void http_server_register_events(HTTP_Server *server, EventRegister *reg) { - if (socket_manager_register_events(&server->sockets, reg) < 0) - return -1; - return 0; + socket_manager_register_events(&server->sockets, reg); } // 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) { SocketEvent events[HTTP_SERVER_CAPACITY]; - int num_events = socket_manager_translate_events(&server->sockets, events, reg); - if (num_events < 0) - return -1; + int ret = socket_manager_translate_events(&server->sockets, events, reg); + if (ret < 0) + return ret; + int num_events = ret; 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, @@ -4775,6 +4626,30 @@ bool http_server_next_request(HTTP_Server *server, 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, ®); + + if (reg.num_polled > 0) + POLL(reg.polled, reg.num_polled, -1); + + int ret = http_server_process_events(server, ®); + 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. // If the builder is invalid, returns NULL. // Note that only connections in the responding states diff --git a/chttp.h b/chttp.h index 76a8f80..791da8c 100644 --- a/chttp.h +++ b/chttp.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -41,6 +40,29 @@ // 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. typedef struct { char *ptr; @@ -65,6 +87,9 @@ HTTP_String http_trim(HTTP_String s); // This is primarily used for debugging purposes. void print_bytes(HTTP_String prefix, HTTP_String src); +// TODO: comment +char *http_strerror(int code); + // Macro to simplify converting string literals to // HTTP_String. // @@ -94,6 +119,9 @@ void print_bytes(HTTP_String prefix, HTTP_String src); // Example: printf("%.*s", HTTP_UNPACK(str)); #define HTTP_UNPACK(X) (X).len, (X).ptr +// TODO: comment +#define HTTP_UNREACHABLE __builtin_trap() + //////////////////////////////////////////////////////////////////////////////////////// // src/parse.h //////////////////////////////////////////////////////////////////////////////////////// @@ -253,21 +281,6 @@ typedef struct { // Returns 0 on success, -1 on error 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 //////////////////////////////////////////////////////////////////////////////////////// @@ -499,11 +512,6 @@ typedef struct { // header. 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 // for plaintext, while the second is for TLS. // The socket manager will accept and add new @@ -593,14 +601,11 @@ typedef struct { void **ptrs; struct pollfd *polled; int num_polled; - int max_polled; } EventRegister; // Resets the event register with the list of descriptors -// the socket manager wants monitored. Returns 0 on -// success, -1 if the event register's capacity isn't -// large enough. -int socket_manager_register_events(SocketManager *sm, +// the socket manager wants monitored. +void socket_manager_register_events(SocketManager *sm, EventRegister *reg); // 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, 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 // from the plaintext listener, or 1 if it was accepted // 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 -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 // writing. @@ -1025,9 +1030,8 @@ void http_request_builder_body(HTTP_RequestBuilder builder, int http_request_builder_send(HTTP_RequestBuilder builder); // Resets the event register with the list of descriptors -// the client wants monitored. Returns 0 on success, -1 if -// the event register's capacity isn't large enough. -int http_client_register_events(HTTP_Client *client, +// the client wants monitored. +void http_client_register_events(HTTP_Client *client, EventRegister *reg); // 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. // TODO: Better comment talking about output arguments bool http_client_next_response(HTTP_Client *client, - HTTP_Response **response, void **user); + int *result, void **user, HTTP_Response **response); // 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 // again after this. @@ -1236,9 +1241,8 @@ int http_server_add_certificate(HTTP_Server *server, int http_server_wakeup(HTTP_Server *server); // Resets the event register with the list of descriptors -// the server wants monitored. Returns 0 on success, -1 if -// the event register's capacity isn't large enough. -int http_server_register_events(HTTP_Server *server, +// the server wants monitored. +void http_server_register_events(HTTP_Server *server, EventRegister *reg); // The caller has waited for poll() to return and some @@ -1264,6 +1268,10 @@ typedef struct { bool http_server_next_request(HTTP_Server *server, 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 // a request's response. If this function is called // after the other response builder functions, it will diff --git a/examples/simple_client.c b/examples/simple_client.c index e60e57e..b4149a9 100644 --- a/examples/simple_client.c +++ b/examples/simple_client.c @@ -1,31 +1,42 @@ #include "../chttp.h" -#ifdef _WIN32 -#define POLL WSAPoll -#else -#define POLL poll -#endif - int main(void) { + int ret; + 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; + } 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_target(builder, HTTP_STR("http://coz.is")); 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; 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; + } - 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); return 0; diff --git a/examples/simple_server.c b/examples/simple_server.c index 1036332..9004399 100644 --- a/examples/simple_server.c +++ b/examples/simple_server.c @@ -1,51 +1,38 @@ #include "../chttp.h" -#ifdef _WIN32 -#define POLL WSAPoll -#else -#define POLL poll -#endif - int main(void) { + int ret; + 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; + } http_server_set_reuse_addr(&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; + } 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, ®) < 0) - return -1; - - if (reg.num_polled > 0) - POLL(reg.polled, reg.num_polled, -1); - - if (http_server_process_events(&server, ®) < 0) - return -1; - HTTP_Request *request; HTTP_ResponseBuilder builder; - if (http_server_next_request(&server, &request, &builder)) { - http_response_builder_status(builder, 200); - http_response_builder_body(builder, HTTP_STR("Hello, world!")); - http_response_builder_send(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_body(builder, HTTP_STR("Hello, world!")); + http_response_builder_send(builder); } http_server_free(&server); diff --git a/misc/amalg.py b/misc/amalg.py index b2b912f..71350e6 100644 --- a/misc/amalg.py +++ b/misc/amalg.py @@ -57,7 +57,6 @@ header.append_text(desc) header.append_file("src/includes.h") header.append_file("src/basic.h") header.append_file("src/parse.h") -header.append_file("src/thread.h") header.append_file("src/secure_context.h") header.append_file("src/socket.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_file("src/basic.c") source.append_file("src/parse.c") -source.append_file("src/thread.c") source.append_file("src/secure_context.c") source.append_file("src/socket.c") source.append_file("src/byte_queue.c") diff --git a/src/basic.c b/src/basic.c index 28d5693..560aefa 100644 --- a/src/basic.c +++ b/src/basic.c @@ -94,3 +94,17 @@ void print_bytes(HTTP_String prefix, HTTP_String src) } 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 "???"; +} diff --git a/src/basic.h b/src/basic.h index eff6f7c..6edec5a 100644 --- a/src/basic.h +++ b/src/basic.h @@ -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. typedef struct { char *ptr; @@ -23,6 +46,9 @@ HTTP_String http_trim(HTTP_String s); // This is primarily used for debugging purposes. void print_bytes(HTTP_String prefix, HTTP_String src); +// TODO: comment +char *http_strerror(int code); + // Macro to simplify converting string literals to // 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. // Example: printf("%.*s", HTTP_UNPACK(str)); #define HTTP_UNPACK(X) (X).len, (X).ptr + +// TODO: comment +#define HTTP_UNREACHABLE __builtin_trap() diff --git a/src/client.c b/src/client.c index 76b44f7..abb057d 100644 --- a/src/client.c +++ b/src/client.c @@ -21,10 +21,8 @@ int http_client_init(HTTP_Client *client) client->num_ready = 0; client->ready_head = 0; - if (socket_manager_init(&client->sockets, - client->socket_pool, HTTP_CLIENT_CAPACITY) < 0) - return -1; - return 0; + return socket_manager_init(&client->sockets, + client->socket_pool, HTTP_CLIENT_CAPACITY); } 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) { - if (socket_manager_wakeup(&client->sockets) < 0) - return -1; - return 0; + return socket_manager_wakeup(&client->sockets); } // 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); if (url_copy == NULL) { conn->state = HTTP_CLIENT_CONN_COMPLETE; - conn->result = -1; + conn->result = HTTP_ERROR_OOM; return; } 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) if (http_parse_url(conn->url_buffer.ptr, conn->url_buffer.len, &conn->url) < 0) { conn->state = HTTP_CLIENT_CONN_COMPLETE; - conn->result = -1; + conn->result = HTTP_ERROR_BADURL; return; } if (!http_streq(conn->url.scheme, HTTP_STR("http")) && !http_streq(conn->url.scheme, HTTP_STR("https"))) { conn->state = HTTP_CLIENT_CONN_COMPLETE; - conn->result = -1; + conn->result = HTTP_ERROR_BADURL; return; } @@ -260,7 +256,9 @@ void http_request_builder_target(HTTP_RequestBuilder builder, if (conn->url.path.len == 0) byte_queue_write(&conn->output, "/", 1); 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 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); } -static int -url_to_connect_target(HTTP_URL url, - ConnectTarget *target) +static ConnectTarget url_to_connect_target(HTTP_URL url) { HTTP_Authority authority = url.authority; + ConnectTarget target; if (authority.port < 1) { if (http_streq(url.scheme, HTTP_STR("https"))) - target->port = 443; + target.port = 443; else - target->port = 80; + target.port = 80; } else { - target->port = authority.port; + target.port = authority.port; } - // Set up target based on host type if (authority.host.mode == HTTP_HOST_MODE_NAME) { - target->type = CONNECT_TARGET_NAME; - target->name = authority.host.name; + target.type = CONNECT_TARGET_NAME; + target.name = authority.host.name; } else if (authority.host.mode == HTTP_HOST_MODE_IPV4) { - target->type = CONNECT_TARGET_IPV4; - target->ipv4 = authority.host.ipv4; + target.type = CONNECT_TARGET_IPV4; + target.ipv4 = authority.host.ipv4; } else if (authority.host.mode == HTTP_HOST_MODE_IPV6) { - target->type = CONNECT_TARGET_IPV6; - target->ipv6 = authority.host.ipv6; + target.type = CONNECT_TARGET_IPV6; + target.ipv6 = authority.host.ipv6; } else { - return -1; + HTTP_UNREACHABLE; } - return 0; + return target; } int http_request_builder_send(HTTP_RequestBuilder builder) { HTTP_Client *client = builder.client; if (client == NULL) - return -1; + return HTTP_ERROR_REQLIMIT; HTTP_ClientConn *conn = request_builder_to_conn(builder); if (conn == NULL) - return -1; + return HTTP_ERROR_BADHANDLE; if (conn->state == HTTP_CLIENT_CONN_COMPLETE) 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)) goto error; - ConnectTarget target; - if (url_to_connect_target(conn->url, &target) < 0) - goto error; - + ConnectTarget target = url_to_connect_target(conn->url); bool secure = http_streq(conn->url.scheme, HTTP_STR("https")); if (socket_connect(&client->sockets, 1, &target, secure, conn) < 0) goto error; conn->state = HTTP_CLIENT_CONN_FLUSHING; conn->gen++; - return 0; + return HTTP_OK; error: conn->state = HTTP_CLIENT_CONN_FREE; @@ -427,7 +420,7 @@ error: byte_queue_free(&conn->input); byte_queue_free(&conn->output); client->num_conns--; - return -1; + return conn->result; } 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); } -int http_client_register_events(HTTP_Client *client, +void http_client_register_events(HTTP_Client *client, EventRegister *reg) { - if (socket_manager_register_events(&client->sockets, reg) < 0) - return -1; - return 0; + socket_manager_register_events(&client->sockets, reg); } int http_client_process_events(HTTP_Client *client, EventRegister *reg) { SocketEvent events[HTTP_CLIENT_CAPACITY]; - int num_events = socket_manager_translate_events( - &client->sockets, events, reg); - if (num_events < 0) - return -1; + int ret = socket_manager_translate_events(&client->sockets, events, reg); + if (ret < 0) + return ret; + int num_events = ret; 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, - HTTP_Response **response, void **user) + int *result, void **user, HTTP_Response **response) { if (client->num_ready == 0) return false; @@ -653,13 +644,13 @@ bool http_client_next_response(HTTP_Client *client, 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; } else { - assert(conn->result == -1); *response = NULL; } - *user = conn->user; return true; } @@ -695,30 +686,27 @@ void http_free_response(HTTP_Response *response) #define POLL poll #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 (;;) { + void *ptrs[HTTP_CLIENT_POLL_CAPACITY]; struct pollfd polled[HTTP_CLIENT_POLL_CAPACITY]; - EventRegister reg = { - .ptrs=ptrs, - .polled=polled, - .num_polled=0, - .max_polled=HTTP_CLIENT_POLL_CAPACITY, - }; - if (http_client_register_events(client, ®) < 0) - return -1; + EventRegister reg = { ptrs, polled, 0 }; + http_client_register_events(client, ®); if (reg.num_polled > 0) POLL(reg.polled, reg.num_polled, -1); - if (http_client_process_events(client, ®) < 0) - return -1; + int ret = http_client_process_events(client, ®); + if (ret < 0) + return ret; - if (http_client_next_response(client, response, user)) + if (http_client_next_response(client, result, user, response)) break; } - return 0; + return HTTP_OK; } diff --git a/src/client.h b/src/client.h index c9c4217..5c73bee 100644 --- a/src/client.h +++ b/src/client.h @@ -196,9 +196,8 @@ void http_request_builder_body(HTTP_RequestBuilder builder, int http_request_builder_send(HTTP_RequestBuilder builder); // Resets the event register with the list of descriptors -// the client wants monitored. Returns 0 on success, -1 if -// the event register's capacity isn't large enough. -int http_client_register_events(HTTP_Client *client, +// the client wants monitored. +void http_client_register_events(HTTP_Client *client, EventRegister *reg); // 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. // TODO: Better comment talking about output arguments bool http_client_next_response(HTTP_Client *client, - HTTP_Response **response, void **user); + int *result, void **user, HTTP_Response **response); // 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 // again after this. diff --git a/src/includes.h b/src/includes.h index bb88b07..e555527 100644 --- a/src/includes.h +++ b/src/includes.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/src/server.c b/src/server.c index 61aad98..7b3ef39 100644 --- a/src/server.c +++ b/src/server.c @@ -34,10 +34,8 @@ int http_server_init(HTTP_Server *server) server->num_ready = 0; server->ready_head = 0; - if (socket_manager_init(&server->sockets, - server->socket_pool, HTTP_SERVER_CAPACITY) < 0) - return -1; - return 0; + return socket_manager_init(&server->sockets, + server->socket_pool, HTTP_SERVER_CAPACITY); } 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, HTTP_String addr, Port port) { - if (socket_manager_listen_tcp(&server->sockets, addr, - port, server->backlog, server->reuse_addr) < 0) - return -1; - return 0; + return socket_manager_listen_tcp(&server->sockets, + addr, port, server->backlog, server->reuse_addr); } int http_server_listen_tls(HTTP_Server *server, HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String key_file_name) { - if (socket_manager_listen_tls(&server->sockets, addr, - port, server->backlog, server->reuse_addr, - cert_file_name, key_file_name) < 0) - return -1; - return 0; + return socket_manager_listen_tls(&server->sockets, + addr, port, server->backlog, server->reuse_addr, + cert_file_name, key_file_name); } int http_server_add_certificate(HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) { - if (socket_manager_add_certificate(&server->sockets, - domain, cert_file, key_file) < 0) - return -1; - return 0; + return socket_manager_add_certificate(&server->sockets, + domain, cert_file, key_file); } int http_server_wakeup(HTTP_Server *server) { - if (socket_manager_wakeup(&server->sockets) < 0) - return -1; - return 0; + return socket_manager_wakeup(&server->sockets); } -int http_server_register_events(HTTP_Server *server, +void http_server_register_events(HTTP_Server *server, EventRegister *reg) { - if (socket_manager_register_events(&server->sockets, reg) < 0) - return -1; - return 0; + socket_manager_register_events(&server->sockets, reg); } // 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) { SocketEvent events[HTTP_SERVER_CAPACITY]; - int num_events = socket_manager_translate_events(&server->sockets, events, reg); - if (num_events < 0) - return -1; + int ret = socket_manager_translate_events(&server->sockets, events, reg); + if (ret < 0) + return ret; + int num_events = ret; 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, @@ -296,6 +285,30 @@ bool http_server_next_request(HTTP_Server *server, 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, ®); + + if (reg.num_polled > 0) + POLL(reg.polled, reg.num_polled, -1); + + int ret = http_server_process_events(server, ®); + 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. // If the builder is invalid, returns NULL. // Note that only connections in the responding states diff --git a/src/server.h b/src/server.h index fa79a0c..a8f706a 100644 --- a/src/server.h +++ b/src/server.h @@ -178,9 +178,8 @@ int http_server_add_certificate(HTTP_Server *server, int http_server_wakeup(HTTP_Server *server); // Resets the event register with the list of descriptors -// the server wants monitored. Returns 0 on success, -1 if -// the event register's capacity isn't large enough. -int http_server_register_events(HTTP_Server *server, +// the server wants monitored. +void http_server_register_events(HTTP_Server *server, EventRegister *reg); // The caller has waited for poll() to return and some @@ -206,6 +205,10 @@ typedef struct { bool http_server_next_request(HTTP_Server *server, 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 // a request's response. If this function is called // after the other response builder functions, it will diff --git a/src/socket.c b/src/socket.c index 2d6db17..a7103c9 100644 --- a/src/socket.c +++ b/src/socket.c @@ -39,7 +39,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData)) - return -1; + return HTTP_ERROR_UNSPECIFIED; sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 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 (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } // 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); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { closesocket(sock); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { closesocket(sock); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } // 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); if (*global_cleanup) WSACleanup(); - return -1; + return HTTP_ERROR_UNSPECIFIED; } *a = sock; *b = sock; - return 0; + return HTTP_OK; #else *global_cleanup = false; int fds[2]; if (pipe(fds) < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; *a = fds[0]; *b = fds[1]; - return 0; + return HTTP_OK; #endif } @@ -110,20 +110,20 @@ static int set_socket_blocking(NATIVE_SOCKET sock, bool value) #ifdef _WIN32 u_long mode = !value; if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) - return -1; + return HTTP_ERROR_UNSPECIFIED; + return HTTP_OK; #endif #ifdef __linux__ int flags = fcntl(sock, F_GETFL, 0); if (flags < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; if (value) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if (fcntl(sock, F_SETFL, flags) < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; + return HTTP_OK; #endif - - return 0; } 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 num_socks) { - if (mutex_init(&sm->mutex) < 0) - return -1; sm->plain_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID; - if (create_socket_pair(&sm->wait_sock, &sm->signal_sock, - &sm->global_cleanup) < 0) - return -1; + + int ret = create_socket_pair( + &sm->wait_sock, + &sm->signal_sock, + &sm->global_cleanup); + if (ret < 0) return ret; + sm->at_least_one_secure_connect = false; sm->num_used = 0; @@ -210,7 +212,7 @@ int socket_manager_init(SocketManager *sm, Socket *socks, socks[i].state = SOCKET_STATE_FREE; socks[i].gen = 1; } - return 0; + return HTTP_OK; } void socket_manager_free(SocketManager *sm) @@ -233,8 +235,6 @@ void socket_manager_free(SocketManager *sm) if (sm->global_cleanup) WSACleanup(); #endif - - mutex_free(&sm->mutex); } int socket_manager_listen_tcp(SocketManager *sm, @@ -242,13 +242,13 @@ int socket_manager_listen_tcp(SocketManager *sm, bool reuse_addr) { if (sm->plain_sock != NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog); 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, @@ -256,35 +256,39 @@ int socket_manager_listen_tls(SocketManager *sm, bool reuse_addr, HTTP_String cert_file, HTTP_String key_file) { +#ifndef HTTPS_ENABLED + return HTTP_ERROR_NOTLS; +#endif + if (sm->secure_sock != NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog); if (sm->secure_sock == NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; if (server_secure_context_init(&sm->server_secure_context, cert_file, key_file) < 0) { CLOSE_NATIVE_SOCKET(sm->secure_sock); sm->secure_sock = NATIVE_SOCKET_INVALID; - return -1; + return HTTP_ERROR_UNSPECIFIED; } - return 0; + return HTTP_OK; } int socket_manager_add_certificate(SocketManager *sm, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) { if (sm->secure_sock == NATIVE_SOCKET_INVALID) - return -1; + return HTTP_ERROR_UNSPECIFIED; int ret = server_secure_context_add_certificate( &sm->server_secure_context, domain, cert_file, key_file); if (ret < 0) - return -1; + return ret; - return 0; + return HTTP_OK; } static bool is_secure(Socket *s) @@ -680,27 +684,21 @@ static void socket_update(Socket *s) int socket_manager_wakeup(SocketManager *sm) { - if (mutex_lock(&sm->mutex) < 0) - return -1; - - // Send a byte through the signal socket to wake up any thread - // blocked on poll() with the wait socket + // NOTE: It's assumed send/write operate atomically + // on The descriptor. char byte = 1; int ret = 0; #ifdef _WIN32 if (send(sm->signal_sock, &byte, 1, 0) < 0) - ret = -1; + return HTTP_ERROR_UNSPECIFIED; #else if (write(sm->signal_sock, &byte, 1) < 0) - ret = -1; + return HTTP_ERROR_UNSPECIFIED; #endif - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; + return HTTP_OK; } -static void socket_manager_register_events_nolock( +void socket_manager_register_events( SocketManager *sm, EventRegister *reg) { 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 socket_to_handle(SocketManager *sm, Socket *s) { @@ -790,7 +775,7 @@ static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle) return &sm->sockets[idx]; } -static int socket_manager_translate_events_nolock( +int socket_manager_translate_events( SocketManager *sm, SocketEvent *events, EventRegister *reg) { @@ -913,19 +898,6 @@ static int socket_manager_translate_events_nolock( 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, 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); if (name == NULL) { free_addr_list(resolved, num_resolved); - return -1; + return HTTP_ERROR_OOM; } name->refs = 0; 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) char hostname[1<<9]; if (targets[i].name.len >= (int) sizeof(hostname)) - return -1; + return HTTP_ERROR_OOM; memcpy(hostname, targets[i].name.ptr, targets[i].name.len); hostname[targets[i].name.len] = '\0'; #endif @@ -968,7 +940,7 @@ static int resolve_connect_targets(ConnectTarget *targets, free(name); #endif free_addr_list(resolved, num_resolved); - return -1; + return HTTP_ERROR_UNSPECIFIED; } 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) { if (sm->num_used == sm->max_used) - return -1; + return HTTP_ERROR_UNSPECIFIED; #ifdef HTTPS_ENABLED if (!sm->at_least_one_secure_connect) { if (client_secure_context_init(&sm->client_secure_context) < 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; sm->at_least_one_secure_connect = true; } #else if (secure) - return -1; + return HTTP_ERROR_NOTLS; #endif AddressAndPort resolved[MAX_CONNECT_TARGETS]; @@ -1058,7 +1030,7 @@ int socket_connect(SocketManager *sm, int num_targets, targets, num_targets, resolved, MAX_CONNECT_TARGETS); if (num_resolved <= 0) - return -1; + return HTTP_ERROR_UNSPECIFIED; Socket *s = sm->sockets; while (s->state != SOCKET_STATE_FREE) { @@ -1075,7 +1047,7 @@ int socket_connect(SocketManager *sm, int num_targets, s->next_addr = 0; s->addrs = malloc(num_resolved * sizeof(AddressAndPort)); if (s->addrs == NULL) - return -1; + return HTTP_ERROR_OOM; for (int i = 0; i < num_resolved; i++) s->addrs[i] = resolved[i]; } @@ -1093,7 +1065,7 @@ int socket_connect(SocketManager *sm, int num_targets, sm->num_used++; socket_update(s); - return 0; + return HTTP_OK; } static bool would_block(void) @@ -1115,7 +1087,7 @@ static bool interrupted(void) #endif } -static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, +int socket_recv(SocketManager *sm, SocketHandle handle, char *dst, int max) { 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, - 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, +int socket_send(SocketManager *sm, SocketHandle handle, char *src, int len) { 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, - char *src, int len) +void socket_close(SocketManager *sm, SocketHandle handle) { - 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; Socket *s = handle_to_socket(sm, handle); if (s == NULL) - ret = -1; - else { - // Only transition to SHUTDOWN if socket is not already DIED - if (s->state != SOCKET_STATE_DIED) { - UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN); - s->events = 0; - socket_update(s); - } + return; + + if (s->state != SOCKET_STATE_DIED) { + UPDATE_STATE(s->state, SOCKET_STATE_SHUTDOWN); + s->events = 0; + 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); - - int ret; if (s == NULL) - ret = -1; - else - ret = is_secure(s); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; + return false; + return is_secure(s); } -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); if (s == NULL) - ret = -1; - else { - s->user = user; - ret = 0; - } + return; - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; + s->user = user; } bool socket_ready(SocketManager *sm, SocketHandle handle) { - if (mutex_lock(&sm->mutex) < 0) { - assert(0); // TODO - } - bool ready = false; Socket *s = handle_to_socket(sm, handle); - if (s && s->events == 0 && s->state != SOCKET_STATE_DIED) { - ready = true; - } + if (s == NULL) + return false; - if (mutex_unlock(&sm->mutex) < 0) { - assert(0); // TODO - } - return ready; + if (s->events == 0 && s->state != SOCKET_STATE_DIED) + return true; + + return false; } diff --git a/src/socket.h b/src/socket.h index f6072c6..6de5ba0 100644 --- a/src/socket.h +++ b/src/socket.h @@ -182,11 +182,6 @@ typedef struct { // header. 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 // for plaintext, while the second is for TLS. // The socket manager will accept and add new @@ -276,14 +271,11 @@ typedef struct { void **ptrs; struct pollfd *polled; int num_polled; - int max_polled; } EventRegister; // Resets the event register with the list of descriptors -// the socket manager wants monitored. Returns 0 on -// success, -1 if the event register's capacity isn't -// large enough. -int socket_manager_register_events(SocketManager *sm, +// the socket manager wants monitored. +void socket_manager_register_events(SocketManager *sm, EventRegister *reg); // 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, 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 // from the plaintext listener, or 1 if it was accepted // 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 -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 // writing. diff --git a/src/thread.c b/src/thread.c deleted file mode 100644 index bd7dbbf..0000000 --- a/src/thread.c +++ /dev/null @@ -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 -} diff --git a/src/thread.h b/src/thread.h deleted file mode 100644 index 4debe69..0000000 --- a/src/thread.h +++ /dev/null @@ -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);