diff --git a/chttp.c b/chttp.c index 2d79f34..969eb7c 100644 --- a/chttp.c +++ b/chttp.c @@ -1997,12 +1997,29 @@ static char *state_to_str(SocketState state) } #endif -static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) +static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_cleanup) { #ifdef _WIN32 SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock == INVALID_SOCKET) + + *global_cleanup = false; + if (sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { + + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + if (WSAStartup(wVersionRequested, &wsaData)) + return -1; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET && *global_cleanup) + WSACleanup(); + } + + if (sock == INVALID_SOCKET) { + if (*global_cleanup) + WSACleanup(); return -1; + } // Bind to loopback address with port 0 (dynamic port assignment) struct sockaddr_in addr; @@ -2014,16 +2031,22 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } @@ -2032,6 +2055,8 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) u_long mode = 1; if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } @@ -2039,6 +2064,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) *b = sock; return 0; #else + *global_cleanup = false; int fds[2]; if (pipe(fds) < 0) return -1; @@ -2140,7 +2166,8 @@ int socket_manager_init(SocketManager *sm, Socket *socks, return -1; sm->plain_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID; - if (create_socket_pair(&sm->wait_sock, &sm->signal_sock) < 0) + if (create_socket_pair(&sm->wait_sock, &sm->signal_sock, + &sm->global_cleanup) < 0) return -1; sm->at_least_one_secure_connect = false; @@ -2171,6 +2198,11 @@ void socket_manager_free(SocketManager *sm) if (sm->secure_sock != NATIVE_SOCKET_INVALID) CLOSE_NATIVE_SOCKET(sm->secure_sock); +#ifdef _WIN32 + if (sm->global_cleanup) + WSACleanup(); +#endif + mutex_free(&sm->mutex); } diff --git a/chttp.h b/chttp.h index 6cb7e05..0c6e8b4 100644 --- a/chttp.h +++ b/chttp.h @@ -530,6 +530,12 @@ typedef struct { ClientSecureContext client_secure_context; ServerSecureContext server_secure_context; + // If the socket manager needed to initialize some + // global state for its initialization, this flag + // will be set so that it will remember to cleanup + // that state during deinitialization. + bool global_cleanup; + // Array of sockets. Structs with state FREE // are unused. int num_used; diff --git a/examples/simple_server.c b/examples/simple_server.c index 5bf0777..1036332 100644 --- a/examples/simple_server.c +++ b/examples/simple_server.c @@ -43,6 +43,7 @@ int main(void) 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); } } diff --git a/src/socket.c b/src/socket.c index 25f0020..2d6db17 100644 --- a/src/socket.c +++ b/src/socket.c @@ -28,12 +28,29 @@ static char *state_to_str(SocketState state) } #endif -static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) +static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_cleanup) { #ifdef _WIN32 SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock == INVALID_SOCKET) + + *global_cleanup = false; + if (sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { + + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + if (WSAStartup(wVersionRequested, &wsaData)) + return -1; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET && *global_cleanup) + WSACleanup(); + } + + if (sock == INVALID_SOCKET) { + if (*global_cleanup) + WSACleanup(); return -1; + } // Bind to loopback address with port 0 (dynamic port assignment) struct sockaddr_in addr; @@ -45,16 +62,22 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } @@ -63,6 +86,8 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) u_long mode = 1; if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) { closesocket(sock); + if (*global_cleanup) + WSACleanup(); return -1; } @@ -70,6 +95,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) *b = sock; return 0; #else + *global_cleanup = false; int fds[2]; if (pipe(fds) < 0) return -1; @@ -171,7 +197,8 @@ int socket_manager_init(SocketManager *sm, Socket *socks, return -1; sm->plain_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID; - if (create_socket_pair(&sm->wait_sock, &sm->signal_sock) < 0) + if (create_socket_pair(&sm->wait_sock, &sm->signal_sock, + &sm->global_cleanup) < 0) return -1; sm->at_least_one_secure_connect = false; @@ -202,6 +229,11 @@ void socket_manager_free(SocketManager *sm) if (sm->secure_sock != NATIVE_SOCKET_INVALID) CLOSE_NATIVE_SOCKET(sm->secure_sock); +#ifdef _WIN32 + if (sm->global_cleanup) + WSACleanup(); +#endif + mutex_free(&sm->mutex); } diff --git a/src/socket.h b/src/socket.h index 9cfc601..f6072c6 100644 --- a/src/socket.h +++ b/src/socket.h @@ -213,6 +213,12 @@ typedef struct { ClientSecureContext client_secure_context; ServerSecureContext server_secure_context; + // If the socket manager needed to initialize some + // global state for its initialization, this flag + // will be set so that it will remember to cleanup + // that state during deinitialization. + bool global_cleanup; + // Array of sockets. Structs with state FREE // are unused. int num_used;