diff --git a/Core/FileLoaders/HTTPFileLoader.cpp b/Core/FileLoaders/HTTPFileLoader.cpp index 4dda285844..f3407d1127 100644 --- a/Core/FileLoaders/HTTPFileLoader.cpp +++ b/Core/FileLoaders/HTTPFileLoader.cpp @@ -28,17 +28,20 @@ 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()); 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()); 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()); Disconnect(); return; } diff --git a/ext/native/base/buffer.cpp b/ext/native/base/buffer.cpp index 9b268976bb..8e48848319 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 bca84d9ba7..b34901fb17 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 3cb6967326..1954f69ce2 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 ac3d26ed97..348cf33d93 100644 --- a/ext/native/net/http_client.h +++ b/ext/native/net/http_client.h @@ -75,8 +75,14 @@ public: // 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