From 4f97692245112ef27b80d2cfe99ed8b125a4db8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 2 Nov 2022 01:03:01 +0800 Subject: [PATCH 1/3] Networking: support nowait connection (Windows) --- CMakeLists.txt | 3 +- src/common/networking.c | 115 ----------------------------- src/common/networking.h | 30 +++++--- src/common/networking_linux.c | 77 +++++++++++++++++++ src/common/networking_windows.c | 127 ++++++++++++++++++++++++++++++++ src/modules/publicip.c | 13 ++-- src/modules/weather.c | 11 +-- 7 files changed, 239 insertions(+), 137 deletions(-) delete mode 100644 src/common/networking.c create mode 100644 src/common/networking_linux.c create mode 100644 src/common/networking_windows.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e059926..91649c043 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,7 +220,6 @@ set(LIBFASTFETCH_SRC src/common/parsing.c src/common/settings.c src/common/library.c - src/common/networking.c src/common/bar.c src/logo/logo.c src/logo/builtin.c @@ -293,6 +292,7 @@ set(LIBFASTFETCH_SRC if(LINUX OR APPLE OR ANDROID OR BSD) list(APPEND LIBFASTFETCH_SRC src/common/processing_linux.c + src/common/networking_linux.c src/detection/users/users_linux.c src/detection/terminalshell/terminalshell_linux.c src/detection/localip/localip_linux.c @@ -360,6 +360,7 @@ endif() if(WIN32) list(APPEND LIBFASTFETCH_SRC src/common/processing_windows.c + src/common/networking_windows.c src/detection/host/host_windows.cpp src/detection/bios/bios_windows.cpp src/detection/board/board_windows.cpp diff --git a/src/common/networking.c b/src/common/networking.c deleted file mode 100644 index a5c8ed629..000000000 --- a/src/common/networking.c +++ /dev/null @@ -1,115 +0,0 @@ -#ifdef _WIN32 - #include - #include - #include - - static BOOL WINAPI initWsaData(PINIT_ONCE once, PVOID param, PVOID* context) - { - (void)once; - (void)param; - static WSADATA wsaData; - *context = &wsaData; - return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0; - } - - //Types of winsock2 are full of mess. Disable warnings for them and keep clean for posix - #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" - #pragma GCC diagnostic ignored "-Wconversion" - #pragma GCC diagnostic ignored "-Wsign-conversion" -#else - #include - #include - #include - #include - - #define closesocket close -#endif - -//Must be included after -#include "fastfetch.h" -#include "common/networking.h" - -FFSockType ffNetworkingSendHttpRequest(const char* host, const char* path, const char* headers, uint32_t timeout) -{ - #ifdef _WIN32 - static INIT_ONCE once = INIT_ONCE_STATIC_INIT; - WSADATA* pData; - if(!InitOnceExecuteOnce(&once, initWsaData, NULL, (LPVOID*) &pData)) - return INVALID_SOCKET; - #endif - - struct addrinfo hints = { - .ai_family = AF_INET, - .ai_socktype = SOCK_STREAM, - }; - - struct addrinfo* addr; - - if(getaddrinfo(host, "80", &hints, &addr) != 0) - return INVALID_SOCKET; - - FFSockType sockfd = (FFSockType)socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if(sockfd == INVALID_SOCKET) - { - freeaddrinfo(addr); - return INVALID_SOCKET; - } - - if(timeout > 0) - { - struct timeval timev; - timev.tv_sec = 0; - timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds - setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); - } - - if(connect(sockfd, addr->ai_addr, addr->ai_addrlen) == -1) - { - closesocket(sockfd); - freeaddrinfo(addr); - return INVALID_SOCKET; - } - - freeaddrinfo(addr); - - FFstrbuf command; - ffStrbufInitA(&command, 64); - ffStrbufAppendS(&command, "GET "); - ffStrbufAppendS(&command, path); - ffStrbufAppendS(&command, " HTTP/1.1\nHost: "); - ffStrbufAppendS(&command, host); - ffStrbufAppendS(&command, "\r\n"); - ffStrbufAppendS(&command, headers); - ffStrbufAppendS(&command, "\r\n"); - - if(send(sockfd, command.chars, command.length, 0) == -1) - { - ffStrbufDestroy(&command); - closesocket(sockfd); - return INVALID_SOCKET; - } - ffStrbufDestroy(&command); - return sockfd; -} - -bool ffNetworkingRecvHttpResponse(FFSockType sockfd, FFstrbuf* buffer) -{ - ssize_t received = recv(sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); - - if(received > 0) - { - buffer->length += (uint32_t) received; - buffer->chars[buffer->length] = '\0'; - } - - closesocket(sockfd); - return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n"); -} - -bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer) -{ - FFSockType sockfd = ffNetworkingSendHttpRequest(host, path, headers, timeout); - if(sockfd != INVALID_SOCKET) - return ffNetworkingRecvHttpResponse(sockfd, buffer); - return false; -} diff --git a/src/common/networking.h b/src/common/networking.h index 0a1b36e5d..aaad894c9 100644 --- a/src/common/networking.h +++ b/src/common/networking.h @@ -6,17 +6,27 @@ #include "util/FFstrbuf.h" #ifdef _WIN32 - typedef uintptr_t FFSockType; //SOCKET, unsigned - #ifndef INVALID_SOCKET //Don't conflict with - #define INVALID_SOCKET ((uintptr_t)~0) + #include +#endif + +typedef struct FFNetworkingState { + #ifdef _WIN32 + uintptr_t sockfd; + OVERLAPPED overlapped; + #else + int sockfd; #endif -#else - typedef int FFSockType; // signed - #define INVALID_SOCKET (-1) -#endif +} FFNetworkingState; -FFSockType ffNetworkingSendHttpRequest(const char* host, const char* path, const char* headers, uint32_t timeout); -bool ffNetworkingRecvHttpResponse(FFSockType sockfd, FFstrbuf* buffer); -bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer); +bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); +bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout); + +static inline bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer) +{ + FFNetworkingState state; + if(ffNetworkingSendHttpRequest(&state, host, path, headers)) + return ffNetworkingRecvHttpResponse(&state, buffer, timeout); + return false; +} #endif diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c new file mode 100644 index 000000000..22e5311fa --- /dev/null +++ b/src/common/networking_linux.c @@ -0,0 +1,77 @@ +#include "fastfetch.h" +#include "common/networking.h" + +#include +#include +#include +#include + +bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) +{ + struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + + struct addrinfo* addr; + + if(getaddrinfo(host, "80", &hints, &addr) != 0) + return false; + + state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if(state->sockfd == -1) + { + freeaddrinfo(addr); + return false; + } + + if(connect(state->sockfd, addr->ai_addr, addr->ai_addrlen) == -1) + { + close(state->sockfd); + freeaddrinfo(addr); + return false; + } + + freeaddrinfo(addr); + + FFstrbuf command; + ffStrbufInitA(&command, 64); + ffStrbufAppendS(&command, "GET "); + ffStrbufAppendS(&command, path); + ffStrbufAppendS(&command, " HTTP/1.1\nHost: "); + ffStrbufAppendS(&command, host); + ffStrbufAppendS(&command, "\r\n"); + ffStrbufAppendS(&command, headers); + ffStrbufAppendS(&command, "\r\n"); + + if(send(state->sockfd, command.chars, command.length, 0) < 0) + { + ffStrbufDestroy(&command); + close(state->sockfd); + return false; + } + ffStrbufDestroy(&command); + return true; +} + +bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) +{ + if(timeout > 0) + { + struct timeval timev; + timev.tv_sec = 0; + timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); + } + + ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); + + if(received > 0) + { + buffer->length += (uint32_t) received; + buffer->chars[buffer->length] = '\0'; + } + + close(state->sockfd); + return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n"); +} diff --git a/src/common/networking_windows.c b/src/common/networking_windows.c new file mode 100644 index 000000000..dd32fac16 --- /dev/null +++ b/src/common/networking_windows.c @@ -0,0 +1,127 @@ +#include +#include + +//Must be included after +#include "fastfetch.h" +#include "common/networking.h" + +static LPFN_CONNECTEX ConnectEx; + +static BOOL WINAPI initWsaData(PINIT_ONCE once, PVOID param, PVOID* context) +{ + (void)once; + (void)param; + static WSADATA wsaData; + *context = &wsaData; + if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + return FALSE; + + if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) + return FALSE; + + //Dummy socket needed for WSAIoctl + SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(sockfd == INVALID_SOCKET) + return FALSE; + + DWORD dwBytes; + GUID guid = WSAID_CONNECTEX; + if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), + &ConnectEx, sizeof(ConnectEx), + &dwBytes, NULL, NULL) != 0) + return FALSE; + + return closesocket(sockfd) == 0; +} + +bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) +{ + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; + WSADATA* pData; + if(!InitOnceExecuteOnce(&once, initWsaData, NULL, (LPVOID*) &pData)) + return false; + + memset(state, 0, sizeof(*state)); + + struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + + struct addrinfo* addr; + + if(getaddrinfo(host, "80", &hints, &addr) != 0) + return false; + + state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if(state->sockfd == INVALID_SOCKET) + { + freeaddrinfo(addr); + return false; + } + + { + //ConnectEx requires the socket to be initially bound + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = INADDR_ANY, + .sin_port = 0, + }; + if(bind(state->sockfd, (SOCKADDR *)&addr, sizeof(addr)) != 0) + { + printf("bind %d\n", WSAGetLastError()); + return false; + } + } + + FFstrbuf command; + ffStrbufInitA(&command, 64); + ffStrbufAppendS(&command, "GET "); + ffStrbufAppendS(&command, path); + ffStrbufAppendS(&command, " HTTP/1.1\nHost: "); + ffStrbufAppendS(&command, host); + ffStrbufAppendS(&command, "\r\n"); + ffStrbufAppendS(&command, headers); + ffStrbufAppendS(&command, "\r\n"); + + BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen, command.chars, command.length, NULL, &state->overlapped); + freeaddrinfo(addr); + ffStrbufDestroy(&command); + + if(!result && WSAGetLastError() != WSA_IO_PENDING) + { + closesocket(state->sockfd); + return false; + } + + ffStrbufDestroy(&command); + return true; +} + +bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) +{ + DWORD transfer, flags; + if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) + { + closesocket(state->sockfd); + return false; + } + + if(timeout > 0) + { + //https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-setsockopt + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); + } + + ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0); + + if(received > 0) + { + buffer->length += (uint32_t) received; + buffer->chars[buffer->length] = '\0'; + } + + closesocket(state->sockfd); + return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n"); +} diff --git a/src/modules/publicip.c b/src/modules/publicip.c index 019f13dfb..61c85698e 100644 --- a/src/modules/publicip.c +++ b/src/modules/publicip.c @@ -5,12 +5,13 @@ #define FF_PUBLICIP_MODULE_NAME "Public IP" #define FF_PUBLICIP_NUM_FORMAT_ARGS 1 -static FFSockType sockfd; +static FFNetworkingState state; +static int status = -1; void ffPreparePublicIp(FFinstance* instance) { if(instance->config.publicIpUrl.length == 0) - sockfd = ffNetworkingSendHttpRequest("ipinfo.io", "/ip", NULL, instance->config.publicIpTimeout); + status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/ip", NULL); else { FFstrbuf host; @@ -27,7 +28,7 @@ void ffPreparePublicIp(FFinstance* instance) host.chars[pathStartIndex] = '\0'; } - sockfd = ffNetworkingSendHttpRequest(host.chars, path.length == 0 ? "/" : path.chars, NULL, instance->config.publicIpTimeout); + status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); ffStrbufDestroy(&path); ffStrbufDestroy(&host); @@ -36,10 +37,10 @@ void ffPreparePublicIp(FFinstance* instance) void ffPrintPublicIp(FFinstance* instance) { - if(sockfd == 0) + if(status == -1) ffPreparePublicIp(instance); - if(sockfd == INVALID_SOCKET) + if(status == 0) { ffPrintError(instance, FF_PUBLICIP_MODULE_NAME, 0, &instance->config.publicIP, "Failed to connect to an IP detection server"); return; @@ -47,7 +48,7 @@ void ffPrintPublicIp(FFinstance* instance) FFstrbuf result; ffStrbufInitA(&result, 4096); - bool success = ffNetworkingRecvHttpResponse(sockfd, &result); + bool success = ffNetworkingRecvHttpResponse(&state, &result, instance->config.publicIpTimeout); if(success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); if(!success || result.length == 0) diff --git a/src/modules/weather.c b/src/modules/weather.c index 11dc9748e..a6bc63ce9 100644 --- a/src/modules/weather.c +++ b/src/modules/weather.c @@ -5,23 +5,24 @@ #define FF_WEATHER_MODULE_NAME "Weather" #define FF_WEATHER_NUM_FORMAT_ARGS 1 -static FFSockType sockfd; +static FFNetworkingState state; +static int status = -1; void ffPrepareWeather(FFinstance* instance) { FFstrbuf path; ffStrbufInitS(&path, "/?format="); ffStrbufAppend(&path, &instance->config.weatherOutputFormat); - sockfd = ffNetworkingSendHttpRequest("wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n", instance->config.weatherTimeout); + status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n"); ffStrbufDestroy(&path); } void ffPrintWeather(FFinstance* instance) { - if(sockfd == 0) + if(status == -1) ffPrepareWeather(instance); - if(sockfd == INVALID_SOCKET) + if(status == 0) { ffPrintError(instance, FF_WEATHER_MODULE_NAME, 0, &instance->config.weather, "Failed to connect to 'wttr.in'"); return; @@ -29,7 +30,7 @@ void ffPrintWeather(FFinstance* instance) FFstrbuf result; ffStrbufInitA(&result, 4096); - bool success = ffNetworkingRecvHttpResponse(sockfd, &result); + bool success = ffNetworkingRecvHttpResponse(&state, &result, instance->config.weatherTimeout); if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); if(!success || result.length == 0) From 46ebb52afaf53410d884ece65669556a6181af9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 2 Nov 2022 16:10:12 +0800 Subject: [PATCH 2/3] Thread: split `ffThreadCreateAndDetach`; add `ffThreadJoin` --- src/common/init.c | 10 +++++----- src/common/thread.h | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/common/init.c b/src/common/init.c index 7f986818e..962bde9fe 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -305,13 +305,13 @@ FF_THREAD_ENTRY_DECL_WRAPPER(ffDetectGTK4, FFinstance*) void startDetectionThreads(FFinstance* instance) { #ifdef FF_HAVE_THREADS - ffThreadCreateAndDetach(ffConnectDisplayServerThreadMain, instance); + ffThreadDetach(ffThreadCreate(ffConnectDisplayServerThreadMain, instance)); #ifdef FF_DETECT_QT_GTK - ffThreadCreateAndDetach(ffDetectQtThreadMain, instance); - ffThreadCreateAndDetach(ffDetectGTK2ThreadMain, instance); - ffThreadCreateAndDetach(ffDetectGTK3ThreadMain, instance); - ffThreadCreateAndDetach(ffDetectGTK4ThreadMain, instance); + ffThreadDetach(ffThreadCreate(ffDetectQtThreadMain, instance)); + ffThreadDetach(ffThreadCreate(ffDetectGTK2ThreadMain, instance)); + ffThreadDetach(ffThreadCreate(ffDetectGTK3ThreadMain, instance)); + ffThreadDetach(ffThreadCreate(ffDetectGTK4ThreadMain, instance)); #endif #else diff --git a/src/common/thread.h b/src/common/thread.h index 035ff9350..74c536cf2 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -9,35 +9,40 @@ #if defined(_WIN32) #include #include - #include // Win32 isn't available on MSYS2 + #include #define FF_THREAD_MUTEX_INITIALIZER SRWLOCK_INIT typedef SRWLOCK FFThreadMutex; + typedef HANDLE FFThreadType; static inline void ffThreadMutexLock(FFThreadMutex* mutex) { AcquireSRWLockExclusive(mutex); } static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { ReleaseSRWLockExclusive(mutex); } - static inline void ffThreadCreateAndDetach(unsigned (__stdcall* func)(void*), void* data) { - uintptr_t newThread = _beginthreadex(NULL, 0, func, data, 0, NULL); - if(newThread) - CloseHandle((HANDLE)newThread); + static inline FFThreadType ffThreadCreate(unsigned (__stdcall* func)(void*), void* data) { + return (FFThreadType)_beginthreadex(NULL, 0, func, data, 0, NULL); } #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; } + static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); } + static inline void ffThreadJoin(FFThreadType thread) { WaitForSingleObject(thread, INFINITE); } #else #include #define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER typedef pthread_mutex_t FFThreadMutex; + typedef pthread_t FFThreadType; static inline void ffThreadMutexLock(FFThreadMutex* mutex) { pthread_mutex_lock(mutex); } static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { pthread_mutex_unlock(mutex); } - static inline void ffThreadCreateAndDetach(void* (* func)(void*), void* data) { - pthread_t newThread; - if(pthread_create(&newThread, NULL, func, data) == 0) - pthread_detach(newThread); + static inline FFThreadType ffThreadCreate(void* (* func)(void*), void* data) { + FFThreadType newThread = NULL; + pthread_create(&newThread, NULL, func, data); + return newThread; } #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static void* fn ## ThreadMain (void* data) { fn((paramType)data); return NULL; } + static inline void ffThreadDetach(FFThreadType thread) { pthread_detach(thread); } + static inline void ffThreadJoin(FFThreadType thread) { pthread_join(thread, NULL); } #endif #else //FF_HAVE_THREADS #define FF_THREAD_MUTEX_INITIALIZER 0 typedef char FFThreadMutex; static inline void ffThreadMutexLock(FFThreadMutex* mutex) { FF_UNUSED(mutex) } static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { FF_UNUSED(mutex) } + #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) #endif //FF_HAVE_THREADS #endif From 9da7bb3f30a8a99bdb8564636e599babdcd883b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 2 Nov 2022 17:29:43 +0800 Subject: [PATCH 3/3] Networking: support nowait connection (Posix) Thread was used for now. It's possible to do nonblocking connect & send with io_uring on Linux, but it requires newer kernel and linking to liburing. I don't think it is worth the effort. --- src/common/networking.h | 13 +++++-- src/common/networking_linux.c | 66 ++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/common/networking.h b/src/common/networking.h index aaad894c9..f27850477 100644 --- a/src/common/networking.h +++ b/src/common/networking.h @@ -3,6 +3,7 @@ #ifndef FF_INCLUDED_common_networking #define FF_INCLUDED_common_networking +#include "common/thread.h" #include "util/FFstrbuf.h" #ifdef _WIN32 @@ -11,10 +12,16 @@ typedef struct FFNetworkingState { #ifdef _WIN32 - uintptr_t sockfd; - OVERLAPPED overlapped; + uintptr_t sockfd; + OVERLAPPED overlapped; #else - int sockfd; + int sockfd; + FFstrbuf host; + FFstrbuf command; + + #ifdef FF_HAVE_THREADS + FFThreadType thread; + #endif #endif } FFNetworkingState; diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index 22e5311fa..5d656fddd 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -6,7 +6,7 @@ #include #include -bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) +static void connectAndSend(FFNetworkingState* state) { struct addrinfo hints = { .ai_family = AF_INET, @@ -15,47 +15,73 @@ bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, con struct addrinfo* addr; - if(getaddrinfo(host, "80", &hints, &addr) != 0) - return false; + if(getaddrinfo(state->host.chars, "80", &hints, &addr) != 0) + goto error; state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if(state->sockfd == -1) { freeaddrinfo(addr); - return false; + goto error; } if(connect(state->sockfd, addr->ai_addr, addr->ai_addrlen) == -1) { close(state->sockfd); freeaddrinfo(addr); - return false; + goto error; } freeaddrinfo(addr); - FFstrbuf command; - ffStrbufInitA(&command, 64); - ffStrbufAppendS(&command, "GET "); - ffStrbufAppendS(&command, path); - ffStrbufAppendS(&command, " HTTP/1.1\nHost: "); - ffStrbufAppendS(&command, host); - ffStrbufAppendS(&command, "\r\n"); - ffStrbufAppendS(&command, headers); - ffStrbufAppendS(&command, "\r\n"); - - if(send(state->sockfd, command.chars, command.length, 0) < 0) + if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0) { - ffStrbufDestroy(&command); close(state->sockfd); - return false; + goto error; } - ffStrbufDestroy(&command); - return true; + + goto exit; + +error: + state->sockfd = -1; + +exit: + ffStrbufDestroy(&state->host); + ffStrbufDestroy(&state->command); +} + +FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*); + +bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) +{ + ffStrbufInitS(&state->host, host); + + ffStrbufInitA(&state->command, 64); + ffStrbufAppendS(&state->command, "GET "); + ffStrbufAppendS(&state->command, path); + ffStrbufAppendS(&state->command, " HTTP/1.1\nHost: "); + ffStrbufAppendS(&state->command, host); + ffStrbufAppendS(&state->command, "\r\n"); + ffStrbufAppendS(&state->command, headers); + ffStrbufAppendS(&state->command, "\r\n"); + + #ifdef FF_HAVE_THREADS + state->thread = ffThreadCreate(connectAndSendThreadMain, state); + return state->thread != NULL; + #else + connectAndSend(state); + return state->sockfd != -1; + #endif } bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) { + #ifdef FF_HAVE_THREADS + ffThreadJoin(state->thread); + #endif + if(state->sockfd == -1) + return false; + if(timeout > 0) { struct timeval timev;