Split off Connection from HTTPClient.cpp/h

This commit is contained in:
Henrik Rydgård committed 2025-09-04 16:58:47 +02:00
1 parent e8dd3c2223
commit 37a32e2c99
11 files changed
+267 -224

No files matched your search

+2
View File
@@ -804,6 +804,8 @@ add_library(Common STATIC
Common/Math/math_util.h
Common/Math/Statistics.h
Common/Math/Statistics.cpp
Common/Net/Connection.cpp
Common/Net/Connection.h
Common/Net/HTTPClient.cpp
Common/Net/HTTPClient.h
Common/Net/HTTPHeaders.cpp
+2
View File
@@ -451,6 +451,7 @@
<ClInclude Include="Math\math_util.h" />
<ClInclude Include="Math\SIMDHeaders.h" />
<ClInclude Include="Math\Statistics.h" />
<ClInclude Include="Net\Connection.h" />
<ClInclude Include="Net\HTTPNaettRequest.h" />
<ClInclude Include="Net\NetBuffer.h" />
<ClInclude Include="Net\HTTPClient.h" />
@@ -906,6 +907,7 @@
<ClCompile Include="Math\lin\vec3.cpp" />
<ClCompile Include="Math\math_util.cpp" />
<ClCompile Include="Math\Statistics.cpp" />
<ClCompile Include="Net\Connection.cpp" />
<ClCompile Include="Net\HTTPNaettRequest.cpp" />
<ClCompile Include="Net\NetBuffer.cpp" />
<ClCompile Include="Net\HTTPClient.cpp" />
+6
View File
@@ -689,6 +689,9 @@
<Filter>Audio</Filter>
</ClInclude>
<ClInclude Include="LoongArch64Emitter.h" />
<ClInclude Include="Net\Connection.h">
<Filter>Net</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
@@ -1290,6 +1293,9 @@
<Filter>ext\imgui</Filter>
</ClCompile>
<ClCompile Include="LoongArch64Emitter.cpp" />
<ClCompile Include="Net\Connection.cpp">
<Filter>Net</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Crypto">
+205
View File
@@ -0,0 +1,205 @@
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <string>
#include "Common/File/Path.h"
#include "Common/TimeUtil.h"
#include "Common/Log.h"
#include "Common/Net/SocketCompat.h"
#include "Common/Buffer.h"
#include "Common/File/FileDescriptor.h"
#include "Common/SysError.h"
#include "Common/Net/Connection.h"
namespace net {
Connection::~Connection() {
Disconnect();
if (resolved_ != nullptr)
DNSResolveFree(resolved_);
}
// For whatever crazy reason, htons isn't available on android x86 on the build server. so here we go.
// TODO: Fix for big-endian
inline unsigned short myhtons(unsigned short x) {
return (x >> 8) | (x << 8);
}
const char *DNSTypeAsString(DNSType type) {
switch (type) {
case DNSType::IPV4:
return "IPV4";
case DNSType::IPV6:
return "IPV6";
case DNSType::ANY:
return "ANY";
default:
return "N/A";
}
}
bool Connection::Resolve(const char *host, int port, DNSType type) {
if ((intptr_t)sock_ != -1) {
ERROR_LOG(Log::IO, "Resolve: Already have a socket");
return false;
}
if (!host || port < 1 || port > 65535) {
ERROR_LOG(Log::IO, "Resolve: Invalid host or port (%d)", port);
return false;
}
host_ = host;
port_ = port;
char port_str[16];
snprintf(port_str, sizeof(port_str), "%d", port);
std::string processedHostname(host);
if (customResolve_) {
processedHostname = customResolve_(host);
}
std::string err;
if (!net::DNSResolve(processedHostname.c_str(), port_str, &resolved_, err, type)) {
WARN_LOG(Log::IO, "Failed to resolve host '%s': '%s' (%s)", host, err.c_str(), DNSTypeAsString(type));
// Zero port so that future calls fail.
port_ = 0;
return false;
}
return true;
}
static void FormatAddr(char *addrbuf, size_t bufsize, const addrinfo *info) {
switch (info->ai_family) {
case AF_INET:
case AF_INET6:
inet_ntop(info->ai_family, &((sockaddr_in *)info->ai_addr)->sin_addr, addrbuf, bufsize);
break;
default:
snprintf(addrbuf, bufsize, "(Unknown AF %d)", info->ai_family);
break;
}
}
bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) {
if (port_ <= 0) {
ERROR_LOG(Log::IO, "Bad port");
return false;
}
sock_ = -1;
for (int tries = maxTries; tries > 0; --tries) {
std::vector<uintptr_t> sockets;
fd_set fds;
int maxfd = 1;
FD_ZERO(&fds);
for (addrinfo *possible = resolved_; possible != nullptr; possible = possible->ai_next) {
if (possible->ai_family != AF_INET && possible->ai_family != AF_INET6)
continue;
int sock = socket(possible->ai_family, SOCK_STREAM, IPPROTO_TCP);
if ((intptr_t)sock == -1) {
ERROR_LOG(Log::IO, "Bad socket");
continue;
}
// Windows sockets aren't limited by socket number, just by count, so checking FD_SETSIZE there is wrong.
#if !PPSSPP_PLATFORM(WINDOWS)
if (sock >= FD_SETSIZE) {
ERROR_LOG(Log::IO, "Socket doesn't fit in FD_SET: %d We probably have a leak.", sock);
closesocket(sock);
continue;
}
#endif
fd_util::SetNonBlocking(sock, true);
// Start trying to connect (async with timeout.)
errno = 0;
if (connect(sock, possible->ai_addr, (int)possible->ai_addrlen) < 0) {
int errorCode = socket_errno;
std::string errorString = GetStringErrorMsg(errorCode);
bool unreachable = errorCode == ENETUNREACH;
bool inProgress = errorCode == EINPROGRESS || errorCode == EWOULDBLOCK;
if (!inProgress) {
char addrStr[128]{};
FormatAddr(addrStr, sizeof(addrStr), possible);
if (!unreachable) {
ERROR_LOG(Log::HTTP, "connect(%d) call to %s failed (%d: %s)", sock, addrStr, errorCode, errorString.c_str());
} else {
INFO_LOG(Log::HTTP, "connect(%d): Ignoring unreachable resolved address %s", sock, addrStr);
}
closesocket(sock);
continue;
}
}
sockets.push_back(sock);
FD_SET(sock, &fds);
if (maxfd < sock + 1) {
maxfd = sock + 1;
}
}
int selectResult = 0;
long timeoutHalfSeconds = floor(2 * timeout);
while (timeoutHalfSeconds >= 0 && selectResult == 0) {
struct timeval tv {};
tv.tv_sec = 0;
if (timeoutHalfSeconds > 0) {
// Wait up to 0.5 seconds between cancel checks.
tv.tv_usec = 500000;
} else {
// Wait the remaining <= 0.5 seconds. Possibly 0, but that's okay.
tv.tv_usec = (timeout - floor(2 * timeout) / 2) * 1000000.0;
}
--timeoutHalfSeconds;
selectResult = select(maxfd, nullptr, &fds, nullptr, &tv);
if (cancelConnect && *cancelConnect) {
WARN_LOG(Log::HTTP, "connect: cancelled (1): %s:%d", host_.c_str(), port_);
break;
}
}
if (selectResult > 0) {
// Something connected. Pick the first one that did (if multiple.)
for (int sock : sockets) {
if ((intptr_t)sock_ == -1 && FD_ISSET(sock, &fds)) {
sock_ = sock;
} else {
closesocket(sock);
}
}
// Great, now we're good to go.
return true;
} else {
// Fail. Close all the sockets.
for (int sock : sockets) {
closesocket(sock);
}
}
if (cancelConnect && *cancelConnect) {
WARN_LOG(Log::HTTP, "connect: cancelled (2): %s:%d", host_.c_str(), port_);
break;
}
sleep_ms(1, "connect");
}
// Nothing connected, unfortunately.
return false;
}
void Connection::Disconnect() {
if ((intptr_t)sock_ != -1) {
closesocket(sock_);
sock_ = -1;
}
}
} // net
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <functional>
#include <cstdint>
#include <string>
#include "Common/Net/Resolve.h"
namespace net {
typedef std::function<std::string(const std::string &)> ResolveFunc;
class Connection {
public:
virtual ~Connection();
explicit Connection(ResolveFunc func) : customResolve_(func) {}
// Inits the sockaddr_in.
bool Resolve(const char *host, int port, DNSType type = DNSType::ANY);
bool Connect(int maxTries = 2, double timeout = 20.0f, bool *cancelConnect = nullptr);
void Disconnect();
// Only to be used for bring-up and debugging.
uintptr_t sock() const { return sock_; }
protected:
// Store the remote host here, so we can send it along through HTTP/1.1 requests.
// TODO: Move to http::client?
std::string host_;
int port_ = -1;
addrinfo *resolved_ = nullptr;
private:
uintptr_t sock_ = -1;
ResolveFunc customResolve_;
};
} // namespace net
-191
View File
@@ -3,7 +3,6 @@
#include <cstdlib>
#include "Common/Net/HTTPClient.h"
#include "Common/TimeUtil.h"
#include "Common/StringUtils.h"
#include "Common/System/OSD.h"
@@ -19,196 +18,6 @@
#include "Common/Net/NetBuffer.h"
#include "Common/Log.h"
namespace net {
Connection::~Connection() {
Disconnect();
if (resolved_ != nullptr)
DNSResolveFree(resolved_);
}
// For whatever crazy reason, htons isn't available on android x86 on the build server. so here we go.
// TODO: Fix for big-endian
inline unsigned short myhtons(unsigned short x) {
return (x >> 8) | (x << 8);
}
const char *DNSTypeAsString(DNSType type) {
switch (type) {
case DNSType::IPV4:
return "IPV4";
case DNSType::IPV6:
return "IPV6";
case DNSType::ANY:
return "ANY";
default:
return "N/A";
}
}
bool Connection::Resolve(const char *host, int port, DNSType type) {
if ((intptr_t)sock_ != -1) {
ERROR_LOG(Log::IO, "Resolve: Already have a socket");
return false;
}
if (!host || port < 1 || port > 65535) {
ERROR_LOG(Log::IO, "Resolve: Invalid host or port (%d)", port);
return false;
}
host_ = host;
port_ = port;
char port_str[16];
snprintf(port_str, sizeof(port_str), "%d", port);
std::string processedHostname(host);
if (customResolve_) {
processedHostname = customResolve_(host);
}
std::string err;
if (!net::DNSResolve(processedHostname.c_str(), port_str, &resolved_, err, type)) {
WARN_LOG(Log::IO, "Failed to resolve host '%s': '%s' (%s)", host, err.c_str(), DNSTypeAsString(type));
// Zero port so that future calls fail.
port_ = 0;
return false;
}
return true;
}
static void FormatAddr(char *addrbuf, size_t bufsize, const addrinfo *info) {
switch (info->ai_family) {
case AF_INET:
case AF_INET6:
inet_ntop(info->ai_family, &((sockaddr_in *)info->ai_addr)->sin_addr, addrbuf, bufsize);
break;
default:
snprintf(addrbuf, bufsize, "(Unknown AF %d)", info->ai_family);
break;
}
}
bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) {
if (port_ <= 0) {
ERROR_LOG(Log::IO, "Bad port");
return false;
}
sock_ = -1;
for (int tries = maxTries; tries > 0; --tries) {
std::vector<uintptr_t> sockets;
fd_set fds;
int maxfd = 1;
FD_ZERO(&fds);
for (addrinfo *possible = resolved_; possible != nullptr; possible = possible->ai_next) {
if (possible->ai_family != AF_INET && possible->ai_family != AF_INET6)
continue;
int sock = socket(possible->ai_family, SOCK_STREAM, IPPROTO_TCP);
if ((intptr_t)sock == -1) {
ERROR_LOG(Log::IO, "Bad socket");
continue;
}
// Windows sockets aren't limited by socket number, just by count, so checking FD_SETSIZE there is wrong.
#if !PPSSPP_PLATFORM(WINDOWS)
if (sock >= FD_SETSIZE) {
ERROR_LOG(Log::IO, "Socket doesn't fit in FD_SET: %d We probably have a leak.", sock);
closesocket(sock);
continue;
}
#endif
fd_util::SetNonBlocking(sock, true);
// Start trying to connect (async with timeout.)
errno = 0;
if (connect(sock, possible->ai_addr, (int)possible->ai_addrlen) < 0) {
int errorCode = socket_errno;
std::string errorString = GetStringErrorMsg(errorCode);
bool unreachable = errorCode == ENETUNREACH;
bool inProgress = errorCode == EINPROGRESS || errorCode == EWOULDBLOCK;
if (!inProgress) {
char addrStr[128]{};
FormatAddr(addrStr, sizeof(addrStr), possible);
if (!unreachable) {
ERROR_LOG(Log::HTTP, "connect(%d) call to %s failed (%d: %s)", sock, addrStr, errorCode, errorString.c_str());
} else {
INFO_LOG(Log::HTTP, "connect(%d): Ignoring unreachable resolved address %s", sock, addrStr);
}
closesocket(sock);
continue;
}
}
sockets.push_back(sock);
FD_SET(sock, &fds);
if (maxfd < sock + 1) {
maxfd = sock + 1;
}
}
int selectResult = 0;
long timeoutHalfSeconds = floor(2 * timeout);
while (timeoutHalfSeconds >= 0 && selectResult == 0) {
struct timeval tv{};
tv.tv_sec = 0;
if (timeoutHalfSeconds > 0) {
// Wait up to 0.5 seconds between cancel checks.
tv.tv_usec = 500000;
} else {
// Wait the remaining <= 0.5 seconds. Possibly 0, but that's okay.
tv.tv_usec = (timeout - floor(2 * timeout) / 2) * 1000000.0;
}
--timeoutHalfSeconds;
selectResult = select(maxfd, nullptr, &fds, nullptr, &tv);
if (cancelConnect && *cancelConnect) {
WARN_LOG(Log::HTTP, "connect: cancelled (1): %s:%d", host_.c_str(), port_);
break;
}
}
if (selectResult > 0) {
// Something connected. Pick the first one that did (if multiple.)
for (int sock : sockets) {
if ((intptr_t)sock_ == -1 && FD_ISSET(sock, &fds)) {
sock_ = sock;
} else {
closesocket(sock);
}
}
// Great, now we're good to go.
return true;
} else {
// Fail. Close all the sockets.
for (int sock : sockets) {
closesocket(sock);
}
}
if (cancelConnect && *cancelConnect) {
WARN_LOG(Log::HTTP, "connect: cancelled (2): %s:%d", host_.c_str(), port_);
break;
}
sleep_ms(1, "connect");
}
// Nothing connected, unfortunately.
return false;
}
void Connection::Disconnect() {
if ((intptr_t)sock_ != -1) {
closesocket(sock_);
sock_ = -1;
}
}
} // net
namespace http {
// TODO: do something sane here
+1 -33
View File
@@ -11,39 +11,7 @@
#include "Common/Net/Resolve.h"
#include "Common/Net/HTTPRequest.h"
namespace net {
typedef std::function<std::string(const std::string &)> ResolveFunc;
class Connection {
public:
virtual ~Connection();
explicit Connection(ResolveFunc func) : customResolve_(func) {}
// Inits the sockaddr_in.
bool Resolve(const char *host, int port, DNSType type = DNSType::ANY);
bool Connect(int maxTries = 2, double timeout = 20.0f, bool *cancelConnect = nullptr);
void Disconnect();
// Only to be used for bring-up and debugging.
uintptr_t sock() const { return sock_; }
protected:
// Store the remote host here, so we can send it along through HTTP/1.1 requests.
// TODO: Move to http::client?
std::string host_;
int port_ = -1;
addrinfo *resolved_ = nullptr;
private:
uintptr_t sock_ = -1;
ResolveFunc customResolve_;
};
} // namespace net
#include "Common/Net/Connection.h"
namespace http {
+2
View File
@@ -99,6 +99,7 @@
<ClInclude Include="..\..\Common\GPU\Vulkan\VulkanLoader.h" />
<ClInclude Include="..\..\Common\Math\CrossSIMD.h" />
<ClInclude Include="..\..\Common\Math\Statistics.h" />
<ClInclude Include="..\..\Common\Net\Connection.h" />
<ClInclude Include="..\..\Common\Net\HTTPNaettRequest.h" />
<ClInclude Include="..\..\Common\Net\HTTPRequest.h" />
<ClInclude Include="..\..\Common\Net\NetBuffer.h" />
@@ -285,6 +286,7 @@
<ClCompile Include="..\..\Common\GPU\GPUBackendCommon.cpp" />
<ClCompile Include="..\..\Common\GPU\Vulkan\VulkanLoader.cpp" />
<ClCompile Include="..\..\Common\Math\Statistics.cpp" />
<ClCompile Include="..\..\Common\Net\Connection.cpp" />
<ClCompile Include="..\..\Common\Net\HTTPNaettRequest.cpp" />
<ClCompile Include="..\..\Common\Net\HTTPRequest.cpp" />
<ClCompile Include="..\..\Common\Net\NetBuffer.cpp" />
+6
View File
@@ -520,6 +520,9 @@
<ClCompile Include="..\..\ext\imgui\imgui_extras.cpp">
<Filter>ext\imgui</Filter>
</ClCompile>
<ClCompile Include="..\..\Common\Net\Connection.cpp">
<Filter>Net</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="targetver.h" />
@@ -999,6 +1002,9 @@
<ClInclude Include="..\..\ext\imgui\imgui_extras.h">
<Filter>ext\imgui</Filter>
</ClInclude>
<ClInclude Include="..\..\Common\Net\Connection.h">
<Filter>Net</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\Common\Math\fast\fast_matrix_neon.S">
+1
View File
@@ -336,6 +336,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Common/Math/expression_parser.cpp \
$(SRC)/Common/Math/lin/vec3.cpp.arm \
$(SRC)/Common/Math/lin/matrix4x4.cpp.arm \
$(SRC)/Common/Net/Connection.cpp \
$(SRC)/Common/Net/HTTPClient.cpp \
$(SRC)/Common/Net/HTTPHeaders.cpp \
$(SRC)/Common/Net/HTTPRequest.cpp \
+1
View File
@@ -441,6 +441,7 @@ SOURCES_CXX += \
$(COMMONDIR)/Math/Statistics.cpp \
$(COMMONDIR)/Math/lin/vec3.cpp \
$(COMMONDIR)/Math/lin/matrix4x4.cpp \
$(COMMONDIR)/Net/Connection.cpp \
$(COMMONDIR)/Net/HTTPClient.cpp \
$(COMMONDIR)/Net/HTTPHeaders.cpp \
$(COMMONDIR)/Net/HTTPServer.cpp \