diff --git a/Core/FileLoaders/CachingFileLoader.cpp b/Core/FileLoaders/CachingFileLoader.cpp index 597fafadccc3..61a745904598 100644 --- a/Core/FileLoaders/CachingFileLoader.cpp +++ b/Core/FileLoaders/CachingFileLoader.cpp @@ -25,12 +25,12 @@ // Takes ownership of backend. CachingFileLoader::CachingFileLoader(FileLoader *backend) - : backend_(backend) { + : ProxiedFileLoader(backend) { } void CachingFileLoader::Prepare() { std::call_once(preparedFlag_, [this](){ - filesize_ = backend_->FileSize(); + filesize_ = ProxiedFileLoader::FileSize(); if (filesize_ > 0) { InitCache(); } @@ -41,27 +41,25 @@ CachingFileLoader::~CachingFileLoader() { if (filesize_ > 0) { ShutdownCache(); } - // Takes ownership. - delete backend_; } bool CachingFileLoader::Exists() { if (exists_ == -1) { - exists_ = backend_->Exists() ? 1 : 0; + exists_ = ProxiedFileLoader::Exists() ? 1 : 0; } return exists_ == 1; } bool CachingFileLoader::ExistsFast() { if (exists_ == -1) { - return backend_->ExistsFast(); + return ProxiedFileLoader::ExistsFast(); } return exists_ == 1; } bool CachingFileLoader::IsDirectory() { if (isDirectory_ == -1) { - isDirectory_ = backend_->IsDirectory() ? 1 : 0; + isDirectory_ = ProxiedFileLoader::IsDirectory() ? 1 : 0; } return isDirectory_ == 1; } @@ -71,10 +69,6 @@ s64 CachingFileLoader::FileSize() { return filesize_; } -std::string CachingFileLoader::Path() const { - return backend_->Path(); -} - size_t CachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) { Prepare(); if (absolutePos >= filesize_) { @@ -288,11 +282,3 @@ void CachingFileLoader::StartReadAhead(s64 pos) { }); th.detach(); } - -bool CachingFileLoader::IsRemote() { - return backend_->IsRemote(); -} - -void CachingFileLoader::Cancel() { - backend_->Cancel(); -} diff --git a/Core/FileLoaders/CachingFileLoader.h b/Core/FileLoaders/CachingFileLoader.h index 03d3aa726ed5..97fe0aa682dd 100644 --- a/Core/FileLoaders/CachingFileLoader.h +++ b/Core/FileLoaders/CachingFileLoader.h @@ -23,25 +23,21 @@ #include "Common/CommonTypes.h" #include "Core/Loaders.h" -class CachingFileLoader : public FileLoader { +class CachingFileLoader : public ProxiedFileLoader { public: CachingFileLoader(FileLoader *backend); ~CachingFileLoader() override; - bool IsRemote() override; bool Exists() override; bool ExistsFast() override; bool IsDirectory() override; s64 FileSize() override; - std::string Path() const override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { return ReadAt(absolutePos, bytes * count, data, flags) / bytes; } size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; - void Cancel() override; - private: void Prepare(); void InitCache(); @@ -61,7 +57,6 @@ class CachingFileLoader : public FileLoader { }; s64 filesize_ = 0; - FileLoader *backend_; int exists_ = -1; int isDirectory_ = -1; u64 generation_; diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index b098df4866b2..bb48ad3dbf87 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -41,12 +41,12 @@ std::mutex DiskCachingFileLoader::cachesMutex_; // Takes ownership of backend. DiskCachingFileLoader::DiskCachingFileLoader(FileLoader *backend) - : backend_(backend) { + : ProxiedFileLoader(backend) { } void DiskCachingFileLoader::Prepare() { std::call_once(preparedFlag_, [this]() { - filesize_ = backend_->FileSize(); + filesize_ = ProxiedFileLoader::FileSize(); if (filesize_ > 0) { InitCache(); } @@ -57,13 +57,11 @@ DiskCachingFileLoader::~DiskCachingFileLoader() { if (filesize_ > 0) { ShutdownCache(); } - // Takes ownership. - delete backend_; } bool DiskCachingFileLoader::Exists() { Prepare(); - return backend_->Exists(); + return ProxiedFileLoader::Exists(); } bool DiskCachingFileLoader::ExistsFast() { @@ -72,19 +70,11 @@ bool DiskCachingFileLoader::ExistsFast() { return true; } -bool DiskCachingFileLoader::IsDirectory() { - return backend_->IsDirectory(); -} - s64 DiskCachingFileLoader::FileSize() { Prepare(); return filesize_; } -std::string DiskCachingFileLoader::Path() const { - return backend_->Path(); -} - size_t DiskCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) { Prepare(); size_t readSize; @@ -115,14 +105,6 @@ size_t DiskCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, return readSize; } -bool DiskCachingFileLoader::IsRemote() { - return backend_->IsRemote(); -} - -void DiskCachingFileLoader::Cancel() { - backend_->Cancel(); -} - std::vector DiskCachingFileLoader::GetCachedPathsInUse() { std::lock_guard guard(cachesMutex_); @@ -139,7 +121,7 @@ std::vector DiskCachingFileLoader::GetCachedPathsInUse() { void DiskCachingFileLoader::InitCache() { std::lock_guard guard(cachesMutex_); - std::string path = backend_->Path(); + std::string path = ProxiedFileLoader::Path(); auto &entry = caches_[path]; if (!entry) { entry = new DiskCachingFileLoaderCache(path, filesize_); @@ -155,7 +137,7 @@ void DiskCachingFileLoader::ShutdownCache() { if (cache_->Release()) { // If it ran out of counts, delete it. delete cache_; - caches_.erase(backend_->Path()); + caches_.erase(ProxiedFileLoader::Path()); } cache_ = nullptr; } diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index e396ff5736ad..622ffb0f0727 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -27,25 +27,20 @@ class DiskCachingFileLoaderCache; -class DiskCachingFileLoader : public FileLoader { +class DiskCachingFileLoader : public ProxiedFileLoader { public: DiskCachingFileLoader(FileLoader *backend); ~DiskCachingFileLoader() override; - bool IsRemote() override; bool Exists() override; bool ExistsFast() override; - bool IsDirectory() override; s64 FileSize() override; - std::string Path() const override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { return ReadAt(absolutePos, bytes * count, data, flags) / bytes; } size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; - void Cancel() override; - static std::vector GetCachedPathsInUse(); private: @@ -55,7 +50,6 @@ class DiskCachingFileLoader : public FileLoader { std::once_flag preparedFlag_; s64 filesize_ = 0; - FileLoader *backend_; DiskCachingFileLoaderCache *cache_ = nullptr; // We don't support concurrent disk cache access (we use memory cached indexes.) diff --git a/Core/FileLoaders/HTTPFileLoader.cpp b/Core/FileLoaders/HTTPFileLoader.cpp index 4dda28584490..9c14f5b9fb34 100644 --- a/Core/FileLoaders/HTTPFileLoader.cpp +++ b/Core/FileLoaders/HTTPFileLoader.cpp @@ -28,17 +28,23 @@ HTTPFileLoader::HTTPFileLoader(const std::string &filename) void HTTPFileLoader::Prepare() { std::call_once(preparedFlag_, [this](){ if (!client_.Resolve(url_.Host().c_str(), url_.Port())) { - // TODO: Should probably set some flag? + ERROR_LOG(LOADER, "HTTP request failed, unable to resolve: %s port %d", url_.Host().c_str(), url_.Port()); + latestError_ = "Could not connect (name not resolved)"; return; } + client_.SetDataTimeout(20.0); Connect(); if (!connected_) { + ERROR_LOG(LOADER, "HTTP request failed, failed to connect: %s port %d", url_.Host().c_str(), url_.Port()); + latestError_ = "Could not connect (refused to connect)"; return; } int err = client_.SendRequest("HEAD", url_.Resource().c_str()); if (err < 0) { + ERROR_LOG(LOADER, "HTTP request failed, failed to send request: %s port %d", url_.Host().c_str(), url_.Port()); + latestError_ = "Could not connect (could not request data)"; Disconnect(); return; } @@ -49,6 +55,7 @@ void HTTPFileLoader::Prepare() { if (code != 200) { // Leave size at 0, invalid. ERROR_LOG(LOADER, "HTTP request failed, got %03d for %s", code, filename_.c_str()); + latestError_ = "Could not connect (invalid response)"; Disconnect(); return; } @@ -138,6 +145,7 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f int err = client_.SendRequest("GET", url_.Resource().c_str(), requestHeaders, nullptr); if (err < 0) { + latestError_ = "Invalid response reading data"; Disconnect(); return 0; } @@ -147,6 +155,7 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f int code = client_.ReadResponseHeaders(&readbuf, responseHeaders); if (code != 206) { ERROR_LOG(LOADER, "HTTP server did not respond with range, received code=%03d", code); + latestError_ = "Invalid response reading data"; Disconnect(); return 0; } @@ -185,6 +194,7 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f if (!supportedResponse) { ERROR_LOG(LOADER, "HTTP server did not respond with the range we wanted."); + latestError_ = "Invalid response reading data"; return 0; } diff --git a/Core/FileLoaders/HTTPFileLoader.h b/Core/FileLoaders/HTTPFileLoader.h index ab4773604f39..d853153177b8 100644 --- a/Core/FileLoaders/HTTPFileLoader.h +++ b/Core/FileLoaders/HTTPFileLoader.h @@ -48,6 +48,10 @@ class HTTPFileLoader : public FileLoader { cancelConnect_ = true; } + std::string LatestError() const override { + return latestError_; + } + private: void Prepare(); @@ -67,6 +71,7 @@ class HTTPFileLoader : public FileLoader { std::string filename_; bool connected_ = false; bool cancelConnect_ = false; + const char *latestError_ = ""; std::once_flag preparedFlag_; std::mutex readAtMutex_; diff --git a/Core/FileLoaders/RamCachingFileLoader.cpp b/Core/FileLoaders/RamCachingFileLoader.cpp index ccaa3c7befbb..4b934cebdffa 100644 --- a/Core/FileLoaders/RamCachingFileLoader.cpp +++ b/Core/FileLoaders/RamCachingFileLoader.cpp @@ -28,7 +28,7 @@ // Takes ownership of backend. RamCachingFileLoader::RamCachingFileLoader(FileLoader *backend) - : backend_(backend) { + : ProxiedFileLoader(backend) { filesize_ = backend->FileSize(); if (filesize_ > 0) { InitCache(); @@ -39,27 +39,25 @@ RamCachingFileLoader::~RamCachingFileLoader() { if (filesize_ > 0) { ShutdownCache(); } - // Takes ownership. - delete backend_; } bool RamCachingFileLoader::Exists() { if (exists_ == -1) { - exists_ = backend_->Exists() ? 1 : 0; + exists_ = ProxiedFileLoader::Exists() ? 1 : 0; } return exists_ == 1; } bool RamCachingFileLoader::ExistsFast() { if (exists_ == -1) { - return backend_->ExistsFast(); + return ProxiedFileLoader::ExistsFast(); } return exists_ == 1; } bool RamCachingFileLoader::IsDirectory() { if (isDirectory_ == -1) { - isDirectory_ = backend_->IsDirectory() ? 1 : 0; + isDirectory_ = ProxiedFileLoader::IsDirectory() ? 1 : 0; } return isDirectory_ == 1; } @@ -68,10 +66,6 @@ s64 RamCachingFileLoader::FileSize() { return filesize_; } -std::string RamCachingFileLoader::Path() const { - return backend_->Path(); -} - size_t RamCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) { size_t readSize = 0; if (cache_ == nullptr || (flags & Flags::HINT_UNCACHED) != 0) { @@ -129,7 +123,7 @@ void RamCachingFileLoader::Cancel() { aheadCancel_ = true; } - backend_->Cancel(); + ProxiedFileLoader::Cancel(); } size_t RamCachingFileLoader::ReadFromCache(s64 pos, size_t bytes, void *data) { @@ -270,7 +264,3 @@ u32 RamCachingFileLoader::NextAheadBlock() { return 0xFFFFFFFF; } - -bool RamCachingFileLoader::IsRemote() { - return backend_->IsRemote(); -} diff --git a/Core/FileLoaders/RamCachingFileLoader.h b/Core/FileLoaders/RamCachingFileLoader.h index 624305155a96..60ca2afc91ca 100644 --- a/Core/FileLoaders/RamCachingFileLoader.h +++ b/Core/FileLoaders/RamCachingFileLoader.h @@ -23,17 +23,15 @@ #include "Common/CommonTypes.h" #include "Core/Loaders.h" -class RamCachingFileLoader : public FileLoader { +class RamCachingFileLoader : public ProxiedFileLoader { public: RamCachingFileLoader(FileLoader *backend); ~RamCachingFileLoader() override; - bool IsRemote() override; bool Exists() override; bool ExistsFast() override; bool IsDirectory() override; s64 FileSize() override; - std::string Path() const override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { return ReadAt(absolutePos, bytes * count, data, flags) / bytes; @@ -59,7 +57,6 @@ class RamCachingFileLoader : public FileLoader { }; s64 filesize_ = 0; - FileLoader *backend_; u8 *cache_ = nullptr; int exists_ = -1; int isDirectory_ = -1; diff --git a/Core/FileLoaders/RetryingFileLoader.cpp b/Core/FileLoaders/RetryingFileLoader.cpp index 4ce1da026148..b986d76bc936 100644 --- a/Core/FileLoaders/RetryingFileLoader.cpp +++ b/Core/FileLoaders/RetryingFileLoader.cpp @@ -19,47 +19,38 @@ // Takes ownership of backend. RetryingFileLoader::RetryingFileLoader(FileLoader *backend) - : backend_(backend) { -} - -RetryingFileLoader::~RetryingFileLoader() { - // Takes ownership. - delete backend_; + : ProxiedFileLoader(backend) { } bool RetryingFileLoader::Exists() { - if (!backend_->Exists()) { + if (!ProxiedFileLoader::Exists()) { // Retry once, immediately. - return backend_->Exists(); + return ProxiedFileLoader::Exists(); } return true; } bool RetryingFileLoader::ExistsFast() { - if (!backend_->ExistsFast()) { + if (!ProxiedFileLoader::ExistsFast()) { // Retry once, immediately. - return backend_->ExistsFast(); + return ProxiedFileLoader::ExistsFast(); } return true; } bool RetryingFileLoader::IsDirectory() { // Can't tell if it's an error either way. - return backend_->IsDirectory(); + return ProxiedFileLoader::IsDirectory(); } s64 RetryingFileLoader::FileSize() { - s64 filesize = backend_->FileSize(); + s64 filesize = ProxiedFileLoader::FileSize(); if (filesize == 0) { - return backend_->FileSize(); + return ProxiedFileLoader::FileSize(); } return filesize; } -std::string RetryingFileLoader::Path() const { - return backend_->Path(); -} - size_t RetryingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) { size_t readSize = backend_->ReadAt(absolutePos, bytes, data, flags); @@ -72,11 +63,3 @@ size_t RetryingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Fla return readSize; } - -bool RetryingFileLoader::IsRemote() { - return backend_->IsRemote(); -} - -void RetryingFileLoader::Cancel() { - backend_->Cancel(); -} diff --git a/Core/FileLoaders/RetryingFileLoader.h b/Core/FileLoaders/RetryingFileLoader.h index 5c8cb5929217..c13edba926b2 100644 --- a/Core/FileLoaders/RetryingFileLoader.h +++ b/Core/FileLoaders/RetryingFileLoader.h @@ -20,29 +20,22 @@ #include "Common/CommonTypes.h" #include "Core/Loaders.h" -class RetryingFileLoader : public FileLoader { +class RetryingFileLoader : public ProxiedFileLoader { public: RetryingFileLoader(FileLoader *backend); - ~RetryingFileLoader() override; - bool IsRemote() override; bool Exists() override; bool ExistsFast() override; bool IsDirectory() override; s64 FileSize() override; - std::string Path() const override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { return ReadAt(absolutePos, bytes * count, data, flags) / bytes; } size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; - void Cancel() override; - private: enum { MAX_RETRIES = 3, }; - - FileLoader *backend_; }; diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index c901a1245a95..14bc31973d24 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -294,7 +294,9 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) { case IdentifiedFileType::ERROR_IDENTIFYING: ERROR_LOG(LOADER, "Could not read file"); - *error_string = "Error reading file"; + *error_string = fileLoader ? fileLoader->LatestError() : ""; + if (error_string->empty()) + *error_string = "Error reading file"; break; case IdentifiedFileType::ARCHIVE_RAR: diff --git a/Core/Loaders.h b/Core/Loaders.h index f0e83adab4bd..527fa52f5bc9 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -93,6 +93,48 @@ class FileLoader { // Cancel any operations that might block, if possible. virtual void Cancel() { } + + virtual std::string LatestError() const { + return ""; + } +}; + +class ProxiedFileLoader : public FileLoader { +public: + ProxiedFileLoader(FileLoader *backend) : backend_(backend) { + } + ~ProxiedFileLoader() override { + // Takes ownership. + delete backend_; + } + + bool IsRemote() override { + return backend_->IsRemote(); + } + bool Exists() override { + return backend_->Exists(); + } + bool ExistsFast() override { + return backend_->ExistsFast(); + } + bool IsDirectory() override { + return backend_->IsDirectory(); + } + s64 FileSize() override { + return backend_->FileSize(); + } + std::string Path() const override { + return backend_->Path(); + } + void Cancel() override { + backend_->Cancel(); + } + std::string LatestError() const override { + return backend_->LatestError(); + } + +protected: + FileLoader *backend_; }; inline u32 operator & (const FileLoader::Flags &a, const FileLoader::Flags &b) { diff --git a/ext/native/base/buffer.cpp b/ext/native/base/buffer.cpp index 9b268976bb29..8e48848319c3 100644 --- a/ext/native/base/buffer.cpp +++ b/ext/native/base/buffer.cpp @@ -146,8 +146,12 @@ bool Buffer::FlushToFile(const char *filename) { return true; } -bool Buffer::FlushSocket(uintptr_t sock) { +bool Buffer::FlushSocket(uintptr_t sock, double timeout) { for (size_t pos = 0, end = data_.size(); pos < end; ) { + if (timeout >= 0.0 && !fd_util::WaitUntilReady(sock, timeout, true)) { + ELOG("FlushSocket timed out"); + return false; + } int sent = send(sock, &data_[pos], (int)(end - pos), MSG_NOSIGNAL); if (sent < 0) { ELOG("FlushSocket failed"); @@ -156,7 +160,7 @@ bool Buffer::FlushSocket(uintptr_t sock) { pos += sent; // Buffer full, don't spin. - if (sent == 0) { + if (sent == 0 && timeout < 0.0) { sleep_ms(1); } } diff --git a/ext/native/base/buffer.h b/ext/native/base/buffer.h index bca84d9ba728..b34901fb1716 100644 --- a/ext/native/base/buffer.h +++ b/ext/native/base/buffer.h @@ -59,12 +59,12 @@ class Buffer { // Simple I/O. - // Writes the entire buffer to the file descriptor. Also resets the - // size to zero. On failure, data remains in buffer and nothing is - // written. + // Writes the entire buffer to the file descriptor. Also resets the + // size to zero. On failure, data remains in buffer and nothing is + // written. bool Flush(int fd); bool FlushToFile(const char *filename); - bool FlushSocket(uintptr_t sock); // Windows portability + bool FlushSocket(uintptr_t sock, double timeout = -1.0); // Windows portability bool ReadAll(int fd, int hintSize = 0); bool ReadAllWithProgress(int fd, int knownSize, float *progress, bool *cancelled); diff --git a/ext/native/net/http_client.cpp b/ext/native/net/http_client.cpp index 3cb6967326dd..1954f69ce202 100644 --- a/ext/native/net/http_client.cpp +++ b/ext/native/net/http_client.cpp @@ -283,7 +283,7 @@ int Client::SendRequestWithData(const char *method, const char *resource, const userAgent_, otherHeaders ? otherHeaders : ""); buffer.Append(data); - bool flushed = buffer.FlushSocket(sock()); + bool flushed = buffer.FlushSocket(sock(), dataTimeout_); if (!flushed) { return -1; // TODO error code. } @@ -292,6 +292,10 @@ int Client::SendRequestWithData(const char *method, const char *resource, const int Client::ReadResponseHeaders(Buffer *readbuf, std::vector &responseHeaders, float *progress) { // Snarf all the data we can into RAM. A little unsafe but hey. + if (dataTimeout_ >= 0.0 && fd_util::WaitUntilReady(sock(), dataTimeout_, false)) { + ELOG("HTTP headers timed out"); + return -1; + } if (readbuf->Read(sock(), 4096) < 0) { ELOG("Failed to read HTTP headers :("); return -1; diff --git a/ext/native/net/http_client.h b/ext/native/net/http_client.h index ac3d26ed974f..348cf33d931a 100644 --- a/ext/native/net/http_client.h +++ b/ext/native/net/http_client.h @@ -75,8 +75,14 @@ class Client : public net::Connection { // If your response contains a response, you must read it. int ReadResponseEntity(Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr); + void SetDataTimeout(double t) { + dataTimeout_ = t; + } + +protected: const char *userAgent_; const char *httpVersion_; + double dataTimeout_ = -1.0; }; // Not particularly efficient, but hey - it's a background download, that's pretty cool :P