mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-25 08:14:45 +02:00
Split off Connection from HTTPClient.cpp/h
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user