From 5cb3977c3c852661ac7256fdb31642a09a98236a Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Mon, 21 Jul 2025 12:06:23 +0200 Subject: [PATCH] Fix windows compilation errors --- Makefile | 4 +- TODO.md | 5 +- chttp.c | 239 +++++++++++++++------------- chttp.h | 7 +- examples/engine/iocp_server.c | 2 +- examples/server/000_http_server.c | 2 +- examples/server/010_zero_copy_api.c | 2 +- examples/server/020_response_undo.c | 4 +- examples/server/050_https_server.c | 2 +- src/basic.h | 5 +- src/server.c | 100 ++---------- src/server.h | 2 +- src/socket.c | 111 ++++++++++--- src/socket.h | 28 +++- 14 files changed, 286 insertions(+), 227 deletions(-) diff --git a/Makefile b/Makefile index 4068a27..0304486 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC = gcc CFLAGS = -I. -Wall -Wextra -O0 -g3 -LFLAGS = -lssl -lcrypto +LFLAGS = AR = ar CFILES = $(shell find src -name "*.c") @@ -15,8 +15,10 @@ STATIC_LIB = lib$(LIBNAME).a # Detect OS and set executable extension ifeq ($(OS),Windows_NT) EXT = .exe + LFLAGS = -lws2_32 else EXT = .out + LFLAGS += -lssl -lcrypto endif # Installation directories diff --git a/TODO.md b/TODO.md index 8aa3249..8fb7d0c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,7 @@ Allow no TLS builds make sure no TLS builds work on windows Allow resolving requests from different threads -Find a way to make sure http.h and http.c are always up to date +Find a way to make sure chttp.h and chttp.c are always up to date parse Transfer-Encoding Make it cross-platform Find a way to compile OpenSSL on windows @@ -9,4 +9,5 @@ handle 3xx client redirections add discussion on string management add discussion on error management check that virtual hosts over HTTPS work -add examples for the router \ No newline at end of file +add examples for the router +what is client-side SNI? Do we need it? \ No newline at end of file diff --git a/chttp.c b/chttp.c index 88cb9b4..1e0af8a 100644 --- a/chttp.c +++ b/chttp.c @@ -62,6 +62,27 @@ #include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define SOCKET_TYPE SOCKET +#define BAD_SOCKET INVALID_SOCKET +#define POLL WSAPoll +#define CLOSE_SOCKET closesocket +#endif + +#ifdef __linux__ +#include +#include +#include +#include +#define SOCKET_TYPE int +#define BAD_SOCKET -1 +#define POLL poll +#define CLOSE_SOCKET close +#endif + + #ifdef HTTPS_ENABLED #include #include @@ -100,7 +121,7 @@ typedef enum { typedef struct { SocketState state; SocketWantEvent event; - int fd; + SOCKET_TYPE fd; #if HTTPS_ENABLED SSL *ssl; SSL_CTX *ssl_ctx; @@ -128,6 +149,9 @@ typedef struct { #endif } SocketGroup; +SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog); + + void socket_global_init (void); void socket_global_free (void); int socket_group_init (SocketGroup *group); @@ -135,7 +159,7 @@ int socket_group_init_server(SocketGroup *group, HTTP_String cert_file, int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key); void socket_group_free (SocketGroup *group); SocketState socket_state (Socket *sock); -void socket_accept (Socket *sock, SocketGroup *group, int fd); +void socket_accept (Socket *sock, SocketGroup *group, SOCKET_TYPE fd); void socket_connect (Socket *sock, SocketGroup *group, HTTP_String host, uint16_t port); void socket_connect_ipv4 (Socket *sock, SocketGroup *group, HTTP_IPv4 addr, uint16_t port); void socket_connect_ipv6 (Socket *sock, SocketGroup *group, HTTP_IPv6 addr, uint16_t port); @@ -2491,11 +2515,15 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, #ifdef _WIN32 #include +#include +#define POLL WSAPoll #endif #ifdef __linux__ #include #include +#include +#define POLL poll #endif #ifdef HTTPS_ENABLED @@ -2511,6 +2539,71 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, #include "socket.h" #endif +static int set_socket_blocking(SOCKET_TYPE sock, bool value) +{ +#ifdef _WIN32 + u_long mode = !value; + if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) + return -1; +#endif + +#ifdef __linux__ + int flags = fcntl(listen_fd, F_GETFL, 0); + if (flags < 0) + return -1; + if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) + return BAD_SOCKET; +#endif + + return 0; +} + +SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog) +{ + SOCKET_TYPE listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (listen_fd == BAD_SOCKET) + return BAD_SOCKET; + + if (set_socket_blocking(listen_fd, false) < 0) { + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + + if (reuse_addr) { + int one = 1; + setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one)); + } + + struct in_addr addr_buf; + if (addr.len == 0) + addr_buf.s_addr = htonl(INADDR_ANY); + else { + + _Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4)); + if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) { + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + } + + struct sockaddr_in bind_buf; + bind_buf.sin_family = AF_INET; + bind_buf.sin_addr = addr_buf; + bind_buf.sin_port = htons(port); + if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { // TODO: how does bind fail on windows? + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + + if (listen(listen_fd, backlog) < 0) { // TODO: how does listen fail on windows? + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + + return listen_fd; +} + + void socket_global_init(void) { #ifdef HTTPS_ENABLED @@ -2764,7 +2857,7 @@ SocketState socket_state(Socket *sock) return sock->state; } -void socket_accept(Socket *sock, SocketGroup *group, int fd) +void socket_accept(Socket *sock, SocketGroup *group, SOCKET_TYPE fd) { #ifdef HTTPS_ENABLED sock->ssl = NULL; @@ -2785,13 +2878,12 @@ void socket_accept(Socket *sock, SocketGroup *group, int fd) sock->addr_cursor = 0; sock->hostname = NULL; sock->port = 0; - - // Set non-blocking mode for the accepted socket - int flags = fcntl(fd, F_GETFL, 0); // TODO: fail on error - if (flags >= 0) { - fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + if (set_socket_blocking(fd, false) < 0) { + sock->state = SOCKET_STATE_DIED; + return; } - + // Start the TLS handshake process socket_update(sock); } @@ -2946,8 +3038,8 @@ void socket_update(Socket *sock) } #endif - if (sock->fd != -1) - close(sock->fd); + if (sock->fd != BAD_SOCKET) + CLOSE_SOCKET(sock->fd); // If cursor reached the end, die if (sock->addr_cursor >= sock->addr_count) { @@ -2959,8 +3051,8 @@ void socket_update(Socket *sock) // Take current address AddrInfo *ai = &sock->addr_list[sock->addr_cursor]; int family = ai->is_ipv6 ? AF_INET6 : AF_INET; - int fd = socket(family, SOCK_STREAM, 0); - if (fd < 0) { + SOCKET_TYPE fd = socket(family, SOCK_STREAM, 0); + if (fd == BAD_SOCKET) { // Try next address sock->addr_cursor++; sock->event = SOCKET_WANT_NONE; @@ -2969,9 +3061,12 @@ void socket_update(Socket *sock) break; } - // Set non-blocking - int flags = fcntl(fd, F_GETFL, 0); - if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK); // TODO: Handle error by setting the socket to DIED + if (set_socket_blocking(fd, false) < 0) { + CLOSE_SOCKET(fd); + sock->event = SOCKET_WANT_NONE; + sock->state = SOCKET_STATE_DIED; + break; + } // Prepare sockaddr int ret; @@ -2998,7 +3093,7 @@ void socket_update(Socket *sock) break; } - if (ret < 0 && errno == EINPROGRESS) { + if (ret < 0 && errno == EINPROGRESS) { // TODO: I'm pretty sure all the error numbers need to be changed for windows // Connection pending sock->fd = fd; sock->event = SOCKET_WANT_WRITE; @@ -3025,7 +3120,7 @@ void socket_update(Socket *sock) // Check connect result int err = 0; socklen_t len = sizeof(err); - if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err != 0) { + if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0 || err != 0) { close(sock->fd); // If remote peer not working, try next address if (err == ECONNREFUSED || err == ETIMEDOUT || err == ENETUNREACH || err == EHOSTUNREACH) { @@ -3302,9 +3397,9 @@ void socket_free(Socket *sock) SSL_free(sock->ssl); #endif - if (sock->fd >= 0) { - close(sock->fd); - sock->fd = -1; + if (sock->fd != BAD_SOCKET) { + CLOSE_SOCKET(sock->fd); + sock->fd = BAD_SOCKET; } if (sock->hostname) { @@ -3344,7 +3439,7 @@ int socket_wait(Socket **socks, int num_socks) // TODO: is this used? polled[i].revents = 0; } - int ret = poll(polled, num_socks, -1); + int ret = POLL(polled, num_socks, -1); if (ret < 0) return -1; @@ -3842,23 +3937,6 @@ HTTP_Response *http_post(HTTP_String url, HTTP_String *headers, int num_headers, #include #include -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define POLL WSAPoll -#define CLOSE_SOCKET closesocket -#endif - -#ifdef __linux__ -#include -#include -#include -#include -#include -#define POLL poll -#define CLOSE_SOCKET close -#endif - #ifndef HTTP_AMALGAMATION #include "engine.h" #include "socket.h" @@ -3877,8 +3955,8 @@ typedef struct { struct HTTP_Server { SocketGroup group; - int listen_fd; - int secure_fd; + SOCKET_TYPE listen_fd; + SOCKET_TYPE secure_fd; int num_conns; Connection conns[MAX_CONNS]; @@ -3888,59 +3966,6 @@ struct HTTP_Server { int ready[MAX_CONNS]; }; -static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog) -{ - int listen_fd = socket(AF_INET, SOCK_STREAM, 0); - if (listen_fd < 0) - return -1; - - { - int flags = fcntl(listen_fd, F_GETFL, 0); - if (flags < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - - if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - } - - if (reuse_addr) { - int one = 1; - setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - } - - struct in_addr addr_buf; - if (addr.len == 0) - addr_buf.s_addr = htonl(INADDR_ANY); - else { - - _Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4)); - if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - } - - struct sockaddr_in bind_buf; - bind_buf.sin_family = AF_INET; - bind_buf.sin_addr = addr_buf; - bind_buf.sin_port = htons(port); - if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - - if (listen(listen_fd, backlog) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - - return listen_fd; -} - HTTP_Server *http_server_init(HTTP_String addr, uint16_t port) { return http_server_init_ex(addr, port, 0, HTTP_STR(""), HTTP_STR("")); @@ -3963,17 +3988,17 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port, } if (port == 0) - server->listen_fd = -1; + server->listen_fd = BAD_SOCKET; else { server->listen_fd = listen_socket(addr, port, reuse_addr, backlog); - if (server->listen_fd < 0) { + if (server->listen_fd == BAD_SOCKET) { free(server); return NULL; } } if (secure_port == 0) - server->secure_fd = -1; + server->secure_fd = BAD_SOCKET; else { if (socket_group_init_server(&server->group, cert_key, private_key) < 0) { @@ -3983,7 +4008,7 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port, } server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog); - if (server->secure_fd < 0) { + if (server->secure_fd == BAD_SOCKET) { socket_group_free(&server->group); CLOSE_SOCKET(server->listen_fd); free(server); @@ -4016,7 +4041,7 @@ void http_server_free(HTTP_Server *server) CLOSE_SOCKET(server->secure_fd); CLOSE_SOCKET(server->listen_fd); - if (server->secure_fd != -1) + if (server->secure_fd != BAD_SOCKET) socket_group_free(&server->group); free(server); } @@ -4048,7 +4073,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl if (server->num_conns < MAX_CONNS) { - if (server->listen_fd != -1) { + if (server->listen_fd != BAD_SOCKET) { polled[num_polled].fd = server->listen_fd; polled[num_polled].events = POLLIN; polled[num_polled].revents = 0; @@ -4056,7 +4081,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl num_polled++; } - if (server->secure_fd != -1) { + if (server->secure_fd != BAD_SOCKET) { polled[num_polled].fd = server->secure_fd; polled[num_polled].events = POLLIN; polled[num_polled].revents = 0; @@ -4102,7 +4127,10 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl if ((polled[i].revents & POLLIN) && server->num_conns < MAX_CONNS) { - int new_fd = accept(polled[i].fd, NULL, NULL); + SOCKET_TYPE new_fd = accept(polled[i].fd, NULL, NULL); + if (new_fd == BAD_SOCKET) { + // TODO + } int k = 0; while (server->conns[k].used) @@ -4211,16 +4239,13 @@ void http_response_status(HTTP_ResponseHandle res, int status) http_engine_status(&conn->engine, status); } -void http_response_header(HTTP_ResponseHandle res, const char *fmt, ...) +void http_response_header(HTTP_ResponseHandle res, HTTP_String str) { Connection *conn = handle2conn(res); if (conn == NULL) return; - va_list args; - va_start(args, fmt); - http_engine_header_fmt2(&conn->engine, fmt, args); - va_end(args); + http_engine_header(&conn->engine, str); } void http_response_body(HTTP_ResponseHandle res, HTTP_String str) diff --git a/chttp.h b/chttp.h index fdf294d..c447f5b 100644 --- a/chttp.h +++ b/chttp.h @@ -16,7 +16,7 @@ // String type used throughout cHTTP. typedef struct { char *ptr; - long len; + int len; } HTTP_String; // Compare two strings and return true iff they have @@ -33,6 +33,9 @@ bool http_streqcase(HTTP_String s1, HTTP_String s2); // the new one references the contents of the original one. HTTP_String http_trim(HTTP_String s); +// TODO: comment +void print_bytes(HTTP_String prefix, HTTP_String src); + // Macro to simplify converting string literals to // HTTP_String. // @@ -491,7 +494,7 @@ void http_server_free (HTTP_Server *server); int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandle *handle); int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); void http_response_status (HTTP_ResponseHandle res, int status); -void http_response_header (HTTP_ResponseHandle res, const char *fmt, ...); +void http_response_header (HTTP_ResponseHandle res, HTTP_String str); void http_response_body (HTTP_ResponseHandle res, HTTP_String str); void http_response_bodycap (HTTP_ResponseHandle res, int mincap); char* http_response_bodybuf (HTTP_ResponseHandle res, int *cap); diff --git a/examples/engine/iocp_server.c b/examples/engine/iocp_server.c index 9192828..d16a9ee 100644 --- a/examples/engine/iocp_server.c +++ b/examples/engine/iocp_server.c @@ -207,7 +207,7 @@ int main(void) // See blocking_server_with_engine.c to learn about how to // build a response http_engine_status(&engs[key], 200); - http_engine_body(&engs[key], "Hello, world!", -1); + http_engine_body(&engs[key], HTTP_STR("Hello, world!")); http_engine_done(&engs[key]); } diff --git a/examples/server/000_http_server.c b/examples/server/000_http_server.c index fb2ab08..ca9e0d3 100644 --- a/examples/server/000_http_server.c +++ b/examples/server/000_http_server.c @@ -71,7 +71,7 @@ int main(void) http_response_status(res, 200); // Then zero or more headers - http_response_header(res, "Content-Type: text/plain"); + http_response_header(res, HTTP_STR("Content-Type: text/plain")); // Then you can write zero or more chunks of the response body http_response_body(res, HTTP_STR("Hello")); diff --git a/examples/server/010_zero_copy_api.c b/examples/server/010_zero_copy_api.c index 239ebe2..e7cda4b 100644 --- a/examples/server/010_zero_copy_api.c +++ b/examples/server/010_zero_copy_api.c @@ -23,7 +23,7 @@ int main(void) if (ret < 0) return -1; http_response_status(res, 200); - http_response_header(res, "Content-Type: text/plain"); + http_response_header(res, HTTP_STR("Content-Type: text/plain")); // The previous example used the *_body function to // write the response body in chunks: diff --git a/examples/server/020_response_undo.c b/examples/server/020_response_undo.c index 2f8a31d..6055c6a 100644 --- a/examples/server/020_response_undo.c +++ b/examples/server/020_response_undo.c @@ -21,7 +21,7 @@ int main(void) // Say we are building a request.. http_response_status(res, 200); - http_response_header(res, "Content-Type: text/plain"); + http_response_header(res, HTTP_STR("Content-Type: text/plain")); // .. and in the middle of building an error condition // occurs. Maybe a file was missing or an allocation fails. @@ -39,7 +39,7 @@ int main(void) // Now we are back to setting the status code http_response_status(res, 500); - http_response_header(res, "Content-Type: text/plain"); + http_response_header(res, HTTP_STR("Content-Type: text/plain")); http_response_body(res, HTTP_STR("An error occurred!")); http_response_done(res); diff --git a/examples/server/050_https_server.c b/examples/server/050_https_server.c index ce68691..6be54b2 100644 --- a/examples/server/050_https_server.c +++ b/examples/server/050_https_server.c @@ -64,7 +64,7 @@ int main(void) if (ret < 0) return -1; http_response_status(res, 200); - http_response_header(res, "Content-Type: text/plain"); + http_response_header(res, HTTP_STR("Content-Type: text/plain")); http_response_body(res, HTTP_STR("Hello")); http_response_body(res, HTTP_STR(", world!")); http_response_done(res); diff --git a/src/basic.h b/src/basic.h index e5c1f05..6c1c892 100644 --- a/src/basic.h +++ b/src/basic.h @@ -6,7 +6,7 @@ // String type used throughout cHTTP. typedef struct { char *ptr; - long len; + int len; } HTTP_String; // Compare two strings and return true iff they have @@ -23,6 +23,9 @@ bool http_streqcase(HTTP_String s1, HTTP_String s2); // the new one references the contents of the original one. HTTP_String http_trim(HTTP_String s); +// TODO: comment +void print_bytes(HTTP_String prefix, HTTP_String src); + // Macro to simplify converting string literals to // HTTP_String. // diff --git a/src/server.c b/src/server.c index b6f43ed..061c99a 100644 --- a/src/server.c +++ b/src/server.c @@ -3,23 +3,6 @@ #include #include -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define POLL WSAPoll -#define CLOSE_SOCKET closesocket -#endif - -#ifdef __linux__ -#include -#include -#include -#include -#include -#define POLL poll -#define CLOSE_SOCKET close -#endif - #ifndef HTTP_AMALGAMATION #include "engine.h" #include "socket.h" @@ -38,8 +21,8 @@ typedef struct { struct HTTP_Server { SocketGroup group; - int listen_fd; - int secure_fd; + SOCKET_TYPE listen_fd; + SOCKET_TYPE secure_fd; int num_conns; Connection conns[MAX_CONNS]; @@ -49,59 +32,6 @@ struct HTTP_Server { int ready[MAX_CONNS]; }; -static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog) -{ - int listen_fd = socket(AF_INET, SOCK_STREAM, 0); - if (listen_fd < 0) - return -1; - - { - int flags = fcntl(listen_fd, F_GETFL, 0); - if (flags < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - - if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - } - - if (reuse_addr) { - int one = 1; - setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - } - - struct in_addr addr_buf; - if (addr.len == 0) - addr_buf.s_addr = htonl(INADDR_ANY); - else { - - _Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4)); - if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - } - - struct sockaddr_in bind_buf; - bind_buf.sin_family = AF_INET; - bind_buf.sin_addr = addr_buf; - bind_buf.sin_port = htons(port); - if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - - if (listen(listen_fd, backlog) < 0) { - CLOSE_SOCKET(listen_fd); - return -1; - } - - return listen_fd; -} - HTTP_Server *http_server_init(HTTP_String addr, uint16_t port) { return http_server_init_ex(addr, port, 0, HTTP_STR(""), HTTP_STR("")); @@ -124,17 +54,17 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port, } if (port == 0) - server->listen_fd = -1; + server->listen_fd = BAD_SOCKET; else { server->listen_fd = listen_socket(addr, port, reuse_addr, backlog); - if (server->listen_fd < 0) { + if (server->listen_fd == BAD_SOCKET) { free(server); return NULL; } } if (secure_port == 0) - server->secure_fd = -1; + server->secure_fd = BAD_SOCKET; else { if (socket_group_init_server(&server->group, cert_key, private_key) < 0) { @@ -144,7 +74,7 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port, } server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog); - if (server->secure_fd < 0) { + if (server->secure_fd == BAD_SOCKET) { socket_group_free(&server->group); CLOSE_SOCKET(server->listen_fd); free(server); @@ -177,7 +107,7 @@ void http_server_free(HTTP_Server *server) CLOSE_SOCKET(server->secure_fd); CLOSE_SOCKET(server->listen_fd); - if (server->secure_fd != -1) + if (server->secure_fd != BAD_SOCKET) socket_group_free(&server->group); free(server); } @@ -209,7 +139,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl if (server->num_conns < MAX_CONNS) { - if (server->listen_fd != -1) { + if (server->listen_fd != BAD_SOCKET) { polled[num_polled].fd = server->listen_fd; polled[num_polled].events = POLLIN; polled[num_polled].revents = 0; @@ -217,7 +147,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl num_polled++; } - if (server->secure_fd != -1) { + if (server->secure_fd != BAD_SOCKET) { polled[num_polled].fd = server->secure_fd; polled[num_polled].events = POLLIN; polled[num_polled].revents = 0; @@ -263,7 +193,10 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl if ((polled[i].revents & POLLIN) && server->num_conns < MAX_CONNS) { - int new_fd = accept(polled[i].fd, NULL, NULL); + SOCKET_TYPE new_fd = accept(polled[i].fd, NULL, NULL); + if (new_fd == BAD_SOCKET) { + // TODO + } int k = 0; while (server->conns[k].used) @@ -372,16 +305,13 @@ void http_response_status(HTTP_ResponseHandle res, int status) http_engine_status(&conn->engine, status); } -void http_response_header(HTTP_ResponseHandle res, const char *fmt, ...) +void http_response_header(HTTP_ResponseHandle res, HTTP_String str) { Connection *conn = handle2conn(res); if (conn == NULL) return; - va_list args; - va_start(args, fmt); - http_engine_header_fmt2(&conn->engine, fmt, args); - va_end(args); + http_engine_header(&conn->engine, str); } void http_response_body(HTTP_ResponseHandle res, HTTP_String str) diff --git a/src/server.h b/src/server.h index 73d2490..e4ae4af 100644 --- a/src/server.h +++ b/src/server.h @@ -24,7 +24,7 @@ void http_server_free (HTTP_Server *server); int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandle *handle); int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); void http_response_status (HTTP_ResponseHandle res, int status); -void http_response_header (HTTP_ResponseHandle res, const char *fmt, ...); +void http_response_header (HTTP_ResponseHandle res, HTTP_String str); void http_response_body (HTTP_ResponseHandle res, HTTP_String str); void http_response_bodycap (HTTP_ResponseHandle res, int mincap); char* http_response_bodybuf (HTTP_ResponseHandle res, int *cap); diff --git a/src/socket.c b/src/socket.c index 2779738..7ed45e1 100644 --- a/src/socket.c +++ b/src/socket.c @@ -9,11 +9,15 @@ #ifdef _WIN32 #include +#include +#define POLL WSAPoll #endif #ifdef __linux__ #include #include +#include +#define POLL poll #endif #ifdef HTTPS_ENABLED @@ -29,6 +33,71 @@ #include "socket.h" #endif +static int set_socket_blocking(SOCKET_TYPE sock, bool value) +{ +#ifdef _WIN32 + u_long mode = !value; + if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) + return -1; +#endif + +#ifdef __linux__ + int flags = fcntl(listen_fd, F_GETFL, 0); + if (flags < 0) + return -1; + if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) + return BAD_SOCKET; +#endif + + return 0; +} + +SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog) +{ + SOCKET_TYPE listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (listen_fd == BAD_SOCKET) + return BAD_SOCKET; + + if (set_socket_blocking(listen_fd, false) < 0) { + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + + if (reuse_addr) { + int one = 1; + setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one)); + } + + struct in_addr addr_buf; + if (addr.len == 0) + addr_buf.s_addr = htonl(INADDR_ANY); + else { + + _Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4)); + if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) { + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + } + + struct sockaddr_in bind_buf; + bind_buf.sin_family = AF_INET; + bind_buf.sin_addr = addr_buf; + bind_buf.sin_port = htons(port); + if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { // TODO: how does bind fail on windows? + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + + if (listen(listen_fd, backlog) < 0) { // TODO: how does listen fail on windows? + CLOSE_SOCKET(listen_fd); + return BAD_SOCKET; + } + + return listen_fd; +} + + void socket_global_init(void) { #ifdef HTTPS_ENABLED @@ -282,7 +351,7 @@ SocketState socket_state(Socket *sock) return sock->state; } -void socket_accept(Socket *sock, SocketGroup *group, int fd) +void socket_accept(Socket *sock, SocketGroup *group, SOCKET_TYPE fd) { #ifdef HTTPS_ENABLED sock->ssl = NULL; @@ -303,13 +372,12 @@ void socket_accept(Socket *sock, SocketGroup *group, int fd) sock->addr_cursor = 0; sock->hostname = NULL; sock->port = 0; - - // Set non-blocking mode for the accepted socket - int flags = fcntl(fd, F_GETFL, 0); // TODO: fail on error - if (flags >= 0) { - fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + if (set_socket_blocking(fd, false) < 0) { + sock->state = SOCKET_STATE_DIED; + return; } - + // Start the TLS handshake process socket_update(sock); } @@ -464,8 +532,8 @@ void socket_update(Socket *sock) } #endif - if (sock->fd != -1) - close(sock->fd); + if (sock->fd != BAD_SOCKET) + CLOSE_SOCKET(sock->fd); // If cursor reached the end, die if (sock->addr_cursor >= sock->addr_count) { @@ -477,8 +545,8 @@ void socket_update(Socket *sock) // Take current address AddrInfo *ai = &sock->addr_list[sock->addr_cursor]; int family = ai->is_ipv6 ? AF_INET6 : AF_INET; - int fd = socket(family, SOCK_STREAM, 0); - if (fd < 0) { + SOCKET_TYPE fd = socket(family, SOCK_STREAM, 0); + if (fd == BAD_SOCKET) { // Try next address sock->addr_cursor++; sock->event = SOCKET_WANT_NONE; @@ -487,9 +555,12 @@ void socket_update(Socket *sock) break; } - // Set non-blocking - int flags = fcntl(fd, F_GETFL, 0); - if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK); // TODO: Handle error by setting the socket to DIED + if (set_socket_blocking(fd, false) < 0) { + CLOSE_SOCKET(fd); + sock->event = SOCKET_WANT_NONE; + sock->state = SOCKET_STATE_DIED; + break; + } // Prepare sockaddr int ret; @@ -516,7 +587,7 @@ void socket_update(Socket *sock) break; } - if (ret < 0 && errno == EINPROGRESS) { + if (ret < 0 && errno == EINPROGRESS) { // TODO: I'm pretty sure all the error numbers need to be changed for windows // Connection pending sock->fd = fd; sock->event = SOCKET_WANT_WRITE; @@ -543,7 +614,7 @@ void socket_update(Socket *sock) // Check connect result int err = 0; socklen_t len = sizeof(err); - if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err != 0) { + if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0 || err != 0) { close(sock->fd); // If remote peer not working, try next address if (err == ECONNREFUSED || err == ETIMEDOUT || err == ENETUNREACH || err == EHOSTUNREACH) { @@ -820,9 +891,9 @@ void socket_free(Socket *sock) SSL_free(sock->ssl); #endif - if (sock->fd >= 0) { - close(sock->fd); - sock->fd = -1; + if (sock->fd != BAD_SOCKET) { + CLOSE_SOCKET(sock->fd); + sock->fd = BAD_SOCKET; } if (sock->hostname) { @@ -862,7 +933,7 @@ int socket_wait(Socket **socks, int num_socks) // TODO: is this used? polled[i].revents = 0; } - int ret = poll(polled, num_socks, -1); + int ret = POLL(polled, num_socks, -1); if (ret < 0) return -1; diff --git a/src/socket.h b/src/socket.h index ec147ad..7617091 100644 --- a/src/socket.h +++ b/src/socket.h @@ -55,6 +55,27 @@ #include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define SOCKET_TYPE SOCKET +#define BAD_SOCKET INVALID_SOCKET +#define POLL WSAPoll +#define CLOSE_SOCKET closesocket +#endif + +#ifdef __linux__ +#include +#include +#include +#include +#define SOCKET_TYPE int +#define BAD_SOCKET -1 +#define POLL poll +#define CLOSE_SOCKET close +#endif + + #ifdef HTTPS_ENABLED #include #include @@ -93,7 +114,7 @@ typedef enum { typedef struct { SocketState state; SocketWantEvent event; - int fd; + SOCKET_TYPE fd; #if HTTPS_ENABLED SSL *ssl; SSL_CTX *ssl_ctx; @@ -121,6 +142,9 @@ typedef struct { #endif } SocketGroup; +SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog); + + void socket_global_init (void); void socket_global_free (void); int socket_group_init (SocketGroup *group); @@ -128,7 +152,7 @@ int socket_group_init_server(SocketGroup *group, HTTP_String cert_file, int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key); void socket_group_free (SocketGroup *group); SocketState socket_state (Socket *sock); -void socket_accept (Socket *sock, SocketGroup *group, int fd); +void socket_accept (Socket *sock, SocketGroup *group, SOCKET_TYPE fd); void socket_connect (Socket *sock, SocketGroup *group, HTTP_String host, uint16_t port); void socket_connect_ipv4 (Socket *sock, SocketGroup *group, HTTP_IPv4 addr, uint16_t port); void socket_connect_ipv6 (Socket *sock, SocketGroup *group, HTTP_IPv6 addr, uint16_t port);