summaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/download.cpp104
-rw-r--r--src/net/download.h82
-rw-r--r--src/net/tmwa/tradehandler.cpp6
3 files changed, 117 insertions, 75 deletions
diff --git a/src/net/download.cpp b/src/net/download.cpp
index 2cfdc3a1..92656ac8 100644
--- a/src/net/download.cpp
+++ b/src/net/download.cpp
@@ -59,29 +59,25 @@ unsigned long Download::fadler32(FILE *file)
return adler;
}
-Download::Download(void *ptr,
- const std::string &url,
- DownloadUpdate updateFunction)
- : mPtr(ptr)
- , mUrl(url)
- , mUpdateFunction(updateFunction)
+Download::Download(const std::string &url)
+ : mUrl(url)
{
- mError = (char*) malloc(CURL_ERROR_SIZE);
mError[0] = 0;
-
- mOptions.cancel = false;
}
Download::~Download()
{
+ mCancel = true;
SDL_WaitThread(mThread, nullptr);
curl_slist_free_all(mHeaders);
- free(mError);
+ free(mBuffer);
}
void Download::addHeader(const char *header)
{
+ assert(!mThread); // Cannot add headers after starting download
+
mHeaders = curl_slist_append(mHeaders, header);
}
@@ -94,19 +90,24 @@ void Download::noCache()
void Download::setFile(const std::string &filename,
std::optional<unsigned long> adler32)
{
- mOptions.memoryWrite = false;
+ assert(!mThread); // Cannot set file after starting download
+
+ mMemoryWrite = false;
mFileName = filename;
mAdler = adler32;
}
-void Download::setWriteFunction(curl_write_callback write)
+void Download::setUseBuffer()
{
- mOptions.memoryWrite = true;
- mWriteFunction = write;
+ assert(!mThread); // Cannot set write function after starting download
+
+ mMemoryWrite = true;
}
bool Download::start()
{
+ assert(!mThread); // Download already started
+
logger->log("Starting download: %s", mUrl.c_str());
mThread = SDL_CreateThread(downloadThread, "Download", this);
@@ -115,8 +116,7 @@ bool Download::start()
{
logger->log("%s", DOWNLOAD_ERROR_MESSAGE_THREAD);
strncpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD, CURL_ERROR_SIZE - 1);
- mUpdateFunction(mPtr, DOWNLOAD_STATUS_THREAD_ERROR, 0, 0);
-
+ mState.lock()->status = DownloadStatus::ERROR;
return false;
}
@@ -126,23 +126,49 @@ bool Download::start()
void Download::cancel()
{
logger->log("Canceling download: %s", mUrl.c_str());
- mOptions.cancel = true;
+ mCancel = true;
}
-const char *Download::getError() const
+std::string_view Download::getBuffer() const
{
- return mError;
+ assert(mMemoryWrite); // Buffer not used
+ return std::string_view(mBuffer, mDownloadedBytes);
}
+/**
+ * A libcurl callback for reporting progress.
+ */
int Download::downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
auto *d = reinterpret_cast<Download*>(clientp);
- DownloadStatus status = d->mOptions.cancel ? DOWNLOAD_STATUS_CANCELLED
- : DOWNLOAD_STATUS_IN_PROGRESS;
- return d->mUpdateFunction(d->mPtr, status, (size_t) dltotal, (size_t) dlnow);
+ auto state = d->mState.lock();
+ state->status = DownloadStatus::IN_PROGRESS;
+ state->progress = 0.0f;
+ if (dltotal > 0)
+ state->progress = static_cast<float>(dlnow) / dltotal;
+
+ return d->mCancel;
+}
+
+/**
+ * A libcurl callback for writing to memory.
+ */
+size_t Download::writeBuffer(char *ptr, size_t size, size_t nmemb, void *stream)
+{
+ auto *d = reinterpret_cast<Download *>(stream);
+
+ const size_t totalMem = size * nmemb;
+ d->mBuffer = (char *) realloc(d->mBuffer, d->mDownloadedBytes + totalMem);
+ if (d->mBuffer)
+ {
+ memcpy(d->mBuffer + d->mDownloadedBytes, ptr, totalMem);
+ d->mDownloadedBytes += totalMem;
+ }
+
+ return totalMem;
}
int Download::downloadThread(void *ptr)
@@ -151,13 +177,11 @@ int Download::downloadThread(void *ptr)
bool complete = false;
std::string outFilename;
- if (!d->mOptions.memoryWrite)
+ if (!d->mMemoryWrite)
outFilename = d->mFileName + ".part";
- for (int attempts = 0; attempts < 3 && !complete && !d->mOptions.cancel; ++attempts)
+ for (int attempts = 0; attempts < 3 && !complete && !d->mCancel; ++attempts)
{
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_STARTING, 0, 0);
-
CURL *curl = curl_easy_init();
if (!curl)
break;
@@ -169,11 +193,11 @@ int Download::downloadThread(void *ptr)
FILE *file = nullptr;
- if (d->mOptions.memoryWrite)
+ if (d->mMemoryWrite)
{
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, d->mWriteFunction);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, d->mPtr);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Download::writeBuffer);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
}
else
{
@@ -189,7 +213,7 @@ int Download::downloadThread(void *ptr)
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, d->mError);
curl_easy_setopt(curl, CURLOPT_URL, d->mUrl.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
- curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress);
+ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &Download::downloadProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, ptr);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
@@ -199,7 +223,7 @@ int Download::downloadThread(void *ptr)
if (res == CURLE_ABORTED_BY_CALLBACK)
{
- d->mOptions.cancel = true;
+ d->mCancel = true;
if (file)
{
@@ -224,9 +248,9 @@ int Download::downloadThread(void *ptr)
break;
}
- if (!d->mOptions.memoryWrite)
+ if (!d->mMemoryWrite)
{
- // Don't check resources.xml checksum
+ // Check the checksum if available
if (d->mAdler)
{
unsigned long adler = fadler32(file);
@@ -274,13 +298,13 @@ int Download::downloadThread(void *ptr)
fclose(file);
}
- if (!d->mOptions.cancel)
- {
- if (complete)
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0);
- else
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
- }
+ auto state = d->mState.lock();
+ if (d->mCancel)
+ state->status = DownloadStatus::CANCELED;
+ else if (complete)
+ state->status = DownloadStatus::COMPLETE;
+ else
+ state->status = DownloadStatus::ERROR;
return 0;
}
diff --git a/src/net/download.h b/src/net/download.h
index be2e374f..20a7fc74 100644
--- a/src/net/download.h
+++ b/src/net/download.h
@@ -18,22 +18,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "utils/mutex.h"
+
#include <cstdio>
-#include <string>
#include <optional>
+#include <string>
#include <curl/curl.h>
#pragma once
-enum DownloadStatus
+enum class DownloadStatus
{
- DOWNLOAD_STATUS_CANCELLED = -3,
- DOWNLOAD_STATUS_THREAD_ERROR = -2,
- DOWNLOAD_STATUS_ERROR = -1,
- DOWNLOAD_STATUS_STARTING = 0,
- DOWNLOAD_STATUS_IN_PROGRESS,
- DOWNLOAD_STATUS_COMPLETE
+ IN_PROGRESS,
+ CANCELED,
+ ERROR,
+ COMPLETE
};
struct SDL_Thread;
@@ -43,17 +43,13 @@ namespace Net {
class Download
{
public:
- /**
- * Callback function for download updates.
- *
- * @param ptr Pointer passed to Download constructor
- * @param status Current download status
- * @param dltotal Total number of bytes to download
- * @param dlnow Number of bytes downloaded so far
- */
- using DownloadUpdate = int (*)(void *, DownloadStatus, size_t, size_t);
+ struct State
+ {
+ DownloadStatus status = DownloadStatus::IN_PROGRESS;
+ float progress = 0.0f;
+ };
- Download(void *ptr, const std::string &url, DownloadUpdate updateFunction);
+ Download(const std::string &url);
~Download();
void addHeader(const char *header);
@@ -66,44 +62,66 @@ class Download
void setFile(const std::string &filename,
std::optional<unsigned long> adler32 = {});
- void setWriteFunction(curl_write_callback write);
+ void setUseBuffer();
/**
* Starts the download thread.
- * @returns true if thread was created
- * false if the thread could not be made or download wasn't
- * properly setup
+ * @returns whether the thread could be created
*/
bool start();
/**
- * Cancels the download. Returns immediately, the cancelled status will
+ * Cancels the download. Returns immediately, the canceled status will
* be noted in the next available update call.
*/
void cancel();
+ /**
+ * Returns a view on the downloaded data.
+ */
+ std::string_view getBuffer() const;
+
+ State getState();
+
const char *getError() const;
static unsigned long fadler32(FILE *file);
private:
- static int downloadThread(void *ptr);
static int downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
- void *mPtr;
+
+ static size_t writeBuffer(char *ptr, size_t size, size_t nmemb,
+ void *stream);
+
+ static int downloadThread(void *ptr);
+
+ ThreadSafe<State> mState;
std::string mUrl;
- struct {
- unsigned cancel : 1;
- unsigned memoryWrite: 1;
- } mOptions;
+ bool mCancel = false;
+ bool mMemoryWrite = false;
std::string mFileName;
- curl_write_callback mWriteFunction = nullptr;
std::optional<unsigned long> mAdler;
- DownloadUpdate mUpdateFunction;
SDL_Thread *mThread = nullptr;
curl_slist *mHeaders = nullptr;
- char *mError;
+ char mError[CURL_ERROR_SIZE];
+
+ /** Byte count currently downloaded in mMemoryBuffer. */
+ size_t mDownloadedBytes = 0;
+
+ /** Buffer for files downloaded to memory. */
+ char *mBuffer = nullptr;
};
+inline Download::State Download::getState()
+{
+ return *mState.lock();
+}
+
+inline const char *Download::getError() const
+{
+ return mError;
+}
+
} // namespace Net
diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp
index 60732eef..c129cfd4 100644
--- a/src/net/tmwa/tradehandler.cpp
+++ b/src/net/tmwa/tradehandler.cpp
@@ -131,7 +131,7 @@ void TradeHandler::handleMessage(MessageIn &msg)
"doesn't exist."));
break;
case 2: // Invite request check failed...
- serverNotice(_("Trade cancelled due to an unknown "
+ serverNotice(_("Trade canceled due to an unknown "
"reason."));
break;
case 3: // Trade accepted
@@ -140,11 +140,11 @@ void TradeHandler::handleMessage(MessageIn &msg)
tradePartnerName.c_str()));
tradeWindow->setVisible(true);
break;
- case 4: // Trade cancelled
+ case 4: // Trade canceled
if (player_relations.hasPermission(tradePartnerName,
PlayerPermissions::SPEECH_LOG))
{
- serverNotice(strprintf(_("Trade with %s cancelled."),
+ serverNotice(strprintf(_("Trade with %s canceled."),
tradePartnerName.c_str()));
}
// otherwise ignore silently