Changeset - r28382:50bd98948184
[Not reviewed]
master
0 7 1
Patric Stout - 11 months ago 2024-01-02 21:05:25
truebrain@openttd.org
Fix: race-conditions in GUI updates when downloading HTTP files (#11639)
8 files changed with 224 insertions and 39 deletions:
0 comments (0 inline, 0 general)
src/network/core/http.h
Show inline comments
 
@@ -27,13 +27,13 @@ struct HTTPCallback {
 
	/**
 
	 * We're receiving data.
 
	 * @param data   the received data, nullptr when all data has been received.
 
	 * @param length the amount of received data, 0 when all data has been received.
 
	 * @note When nullptr is sent the HTTP socket handler is closed/freed.
 
	 */
 
	virtual void OnReceiveData(const char *data, size_t length) = 0;
 
	virtual void OnReceiveData(std::unique_ptr<char[]> data, size_t length) = 0;
 

	
 
	/**
 
	 * Check if there is a request to cancel the transfer.
 
	 *
 
	 * @return true iff the connection is cancelled.
 
	 * @note Cancellations are never instant, and can take a bit of time to be processed.
src/network/core/http_curl.cpp
Show inline comments
 
@@ -14,12 +14,13 @@
 
#include "../../fileio_func.h"
 
#include "../../rev.h"
 
#include "../../thread.h"
 
#include "../network_internal.h"
 

	
 
#include "http.h"
 
#include "http_shared.h"
 

	
 
#include <atomic>
 
#include <condition_variable>
 
#include <curl/curl.h>
 
#include <mutex>
 
#include <queue>
 
@@ -41,12 +42,17 @@ static auto _certificate_directories = {
 
	"/etc/ssl/certs",                                    // SLES10/SLES11, https://golang.org/issue/12139
 
	"/etc/pki/tls/certs",                                // Fedora/RHEL
 
	"/system/etc/security/cacerts",                      // Android
 
};
 
#endif /* UNIX */
 

	
 
static std::vector<HTTPThreadSafeCallback *> _http_callbacks;
 
static std::vector<HTTPThreadSafeCallback *> _new_http_callbacks;
 
static std::mutex _http_callback_mutex;
 
static std::mutex _new_http_callback_mutex;
 

	
 
/** Single HTTP request. */
 
class NetworkHTTPRequest {
 
public:
 
	/**
 
	 * Create a new HTTP request.
 
	 *
 
@@ -56,17 +62,25 @@ public:
 
	 */
 
	NetworkHTTPRequest(const std::string &uri, HTTPCallback *callback, const std::string &data) :
 
		uri(uri),
 
		callback(callback),
 
		data(data)
 
	{
 
		std::lock_guard<std::mutex> lock(_new_http_callback_mutex);
 
		_new_http_callbacks.push_back(&this->callback);
 
	}
 

	
 
	const std::string uri;        ///< URI to connect to.
 
	HTTPCallback *callback;       ///< Callback to send data back on.
 
	const std::string data;       ///< Data to send, if any.
 
	~NetworkHTTPRequest()
 
	{
 
		std::lock_guard<std::mutex> lock(_http_callback_mutex);
 
		_http_callbacks.erase(std::remove(_http_callbacks.begin(), _http_callbacks.end(), &this->callback), _http_callbacks.end());
 
	}
 

	
 
	const std::string uri; ///< URI to connect to.
 
	HTTPThreadSafeCallback callback; ///< Callback to send data back on.
 
	const std::string data; ///< Data to send, if any.
 
};
 

	
 
static std::thread _http_thread;
 
static std::atomic<bool> _http_thread_exit = false;
 
static std::queue<std::unique_ptr<NetworkHTTPRequest>> _http_requests;
 
static std::mutex _http_mutex;
 
@@ -89,12 +103,26 @@ static std::string _http_ca_path = "";
 
	_http_requests.push(std::make_unique<NetworkHTTPRequest>(uri, callback, data));
 
	_http_cv.notify_one();
 
}
 

	
 
/* static */ void NetworkHTTPSocketHandler::HTTPReceive()
 
{
 
	std::lock_guard<std::mutex> lock(_http_callback_mutex);
 

	
 
	{
 
		std::lock_guard<std::mutex> lock_new(_new_http_callback_mutex);
 
		if (!_new_http_callbacks.empty()) {
 
			/* We delay adding new callbacks, as HandleQueue() below might add a new callback. */
 
			_http_callbacks.insert(_http_callbacks.end(), _new_http_callbacks.begin(), _new_http_callbacks.end());
 
			_new_http_callbacks.clear();
 
		}
 
	}
 

	
 
	for (auto &callback : _http_callbacks) {
 
		callback->HandleQueue();
 
	}
 
}
 

	
 
void HttpThread()
 
{
 
	CURL *curl = curl_easy_init();
 
	assert(curl != nullptr);
 
@@ -160,45 +188,53 @@ void HttpThread()
 
		}
 
		curl_easy_setopt(curl, CURLOPT_URL, request->uri.c_str());
 

	
 
		/* Setup our (C-style) callback function which we pipe back into the callback. */
 
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, +[](char *ptr, size_t size, size_t nmemb, void *userdata) -> size_t {
 
			Debug(net, 4, "HTTP callback: {} bytes", size * nmemb);
 
			HTTPCallback *callback = static_cast<HTTPCallback *>(userdata);
 
			callback->OnReceiveData(ptr, size * nmemb);
 
			HTTPThreadSafeCallback *callback = static_cast<HTTPThreadSafeCallback *>(userdata);
 

	
 
			/* Copy the buffer out of CURL. OnReceiveData() will free it when done. */
 
			std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size * nmemb);
 
			memcpy(buffer.get(), ptr, size * nmemb);
 
			callback->OnReceiveData(std::move(buffer), size * nmemb);
 

	
 
			return size * nmemb;
 
		});
 
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, request->callback);
 
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->callback);
 

	
 
		/* Create a callback from which we can cancel. Sadly, there is no other
 
		 * thread-safe way to do this. If the connection went idle, it can take
 
		 * up to a second before this callback is called. There is little we can
 
		 * do about this. */
 
		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
 
		curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, +[](void *userdata, curl_off_t /*dltotal*/, curl_off_t /*dlnow*/, curl_off_t /*ultotal*/, curl_off_t /*ulnow*/) -> int {
 
			const HTTPCallback *callback = static_cast<HTTPCallback *>(userdata);
 
			return (callback->IsCancelled() || _http_thread_exit) ? 1 : 0;
 
			const HTTPThreadSafeCallback *callback = static_cast<HTTPThreadSafeCallback *>(userdata);
 
			return (callback->cancelled || _http_thread_exit) ? 1 : 0;
 
		});
 
		curl_easy_setopt(curl, CURLOPT_XFERINFODATA, request->callback);
 
		curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &request->callback);
 

	
 
		/* Perform the request. */
 
		CURLcode res = curl_easy_perform(curl);
 

	
 
		curl_slist_free_all(headers);
 

	
 
		if (res == CURLE_OK) {
 
			Debug(net, 1, "HTTP request succeeded");
 
			request->callback->OnReceiveData(nullptr, 0);
 
			request->callback.OnReceiveData(nullptr, 0);
 
		} else {
 
			long status_code = 0;
 
			curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
 

	
 
			/* No need to be verbose about rate limiting. */
 
			Debug(net, (request->callback->IsCancelled() || _http_thread_exit || status_code == HTTP_429_TOO_MANY_REQUESTS) ? 1 : 0, "HTTP request failed: status_code: {}, error: {}", status_code, curl_easy_strerror(res));
 
			request->callback->OnFailure();
 
			Debug(net, (request->callback.cancelled || _http_thread_exit || status_code == HTTP_429_TOO_MANY_REQUESTS) ? 1 : 0, "HTTP request failed: status_code: {}, error: {}", status_code, curl_easy_strerror(res));
 
			request->callback.OnFailure();
 
		}
 

	
 
		/* Wait till the callback tells us all data is dequeued. */
 
		request->callback.WaitTillEmpty();
 
	}
 

	
 
	curl_easy_cleanup(curl);
 
}
 

	
 
void NetworkHTTPInitialize()
src/network/core/http_shared.h
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file http_shared.h Shared functions for implementations of HTTP requests.
 
 */
 

	
 
#ifndef NETWORK_CORE_HTTP_SHARED_H
 
#define NETWORK_CORE_HTTP_SHARED_H
 

	
 
#include "http.h"
 

	
 
#include <condition_variable>
 
#include <mutex>
 
#include <vector>
 

	
 
/** Converts a HTTPCallback to a Thread-Safe variant. */
 
class HTTPThreadSafeCallback {
 
private:
 
	/** Entries on the queue for later handling. */
 
	class Callback {
 
	public:
 
		Callback(std::unique_ptr<char[]> data, size_t length) : data(std::move(data)), length(length), failure(false) {}
 
		Callback() : data(nullptr), length(0), failure(true) {}
 

	
 
		std::unique_ptr<char[]> data;
 
		size_t length;
 
		bool failure;
 
	};
 

	
 
public:
 
	/**
 
	 * Similar to HTTPCallback::OnFailure, but thread-safe.
 
	 */
 
	void OnFailure()
 
	{
 
		std::lock_guard<std::mutex> lock(this->mutex);
 
		this->queue.emplace_back();
 
	}
 

	
 
	/**
 
	 * Similar to HTTPCallback::OnReceiveData, but thread-safe.
 
	 */
 
	void OnReceiveData(std::unique_ptr<char[]> data, size_t length)
 
	{
 
		std::lock_guard<std::mutex> lock(this->mutex);
 
		this->queue.emplace_back(std::move(data), length);
 
	}
 

	
 
	/**
 
	 * Process everything on the queue.
 
	 *
 
	 * Should be called from the Game Thread.
 
	 */
 
	void HandleQueue()
 
	{
 
		this->cancelled = callback->IsCancelled();
 

	
 
		std::lock_guard<std::mutex> lock(this->mutex);
 

	
 
		for (auto &item : this->queue) {
 
			if (item.failure) {
 
				this->callback->OnFailure();
 
			} else {
 
				this->callback->OnReceiveData(std::move(item.data), item.length);
 
			}
 
		}
 

	
 
		this->queue.clear();
 
		this->queue_cv.notify_all();
 
	}
 

	
 
	/**
 
	 * Wait till the queue is dequeued.
 
	 */
 
	void WaitTillEmpty()
 
	{
 
		std::unique_lock<std::mutex> lock(this->mutex);
 

	
 
		while (!queue.empty()) {
 
			this->queue_cv.wait(lock);
 
		}
 
	}
 

	
 
	/**
 
	 * Check if the queue is empty.
 
	 */
 
	bool IsQueueEmpty()
 
	{
 
		std::lock_guard<std::mutex> lock(this->mutex);
 
		return this->queue.empty();
 
	}
 

	
 
	HTTPThreadSafeCallback(HTTPCallback *callback) : callback(callback) {}
 

	
 
	~HTTPThreadSafeCallback()
 
	{
 
		std::lock_guard<std::mutex> lock(this->mutex);
 

	
 
		/* Clear the list and notify explicitly. */
 
		queue.clear();
 
		queue_cv.notify_all();
 
	}
 

	
 
	std::atomic<bool> cancelled = false;
 

	
 
private:
 
	HTTPCallback *callback; ///< The callback to send data back on.
 
	std::mutex mutex; ///< Mutex to protect the queue.
 
	std::vector<Callback> queue; ///< Queue of data to send back.
 
	std::condition_variable queue_cv; ///< Condition variable to wait for the queue to be empty.
 
};
 

	
 
#endif /* NETWORK_CORE_HTTP_SHARED_H */
src/network/core/http_winhttp.cpp
Show inline comments
 
@@ -12,26 +12,27 @@
 
#include "../../stdafx.h"
 
#include "../../debug.h"
 
#include "../../rev.h"
 
#include "../network_internal.h"
 

	
 
#include "http.h"
 
#include "http_shared.h"
 

	
 
#include <mutex>
 
#include <winhttp.h>
 

	
 
#include "../../safeguards.h"
 

	
 
static HINTERNET _winhttp_session = nullptr;
 

	
 
/** Single HTTP request. */
 
class NetworkHTTPRequest {
 
private:
 
	const std::wstring uri;       ///< URI to connect to.
 
	HTTPCallback *callback;       ///< Callback to send data back on.
 
	const std::string data;       ///< Data to send, if any.
 
	const std::wstring uri; ///< URI to connect to.
 
	HTTPThreadSafeCallback callback; ///< Callback to send data back on.
 
	const std::string data; ///< Data to send, if any.
 

	
 
	HINTERNET connection = nullptr;      ///< Current connection object.
 
	HINTERNET request = nullptr;         ///< Current request object.
 
	std::atomic<bool> finished = false;  ///< Whether we are finished with the request.
 
	int depth = 0;                       ///< Current redirect depth we are in.
 

	
 
@@ -46,24 +47,31 @@ public:
 
};
 

	
 
static std::vector<NetworkHTTPRequest *> _http_requests;
 
static std::vector<NetworkHTTPRequest *> _new_http_requests;
 
static std::mutex _new_http_requests_mutex;
 

	
 
static std::vector<HTTPThreadSafeCallback *> _http_callbacks;
 
static std::vector<HTTPThreadSafeCallback *> _new_http_callbacks;
 
static std::mutex _http_callback_mutex;
 
static std::mutex _new_http_callback_mutex;
 

	
 
/**
 
 * Create a new HTTP request.
 
 *
 
 * @param uri      the URI to connect to (https://.../..).
 
 * @param callback the callback to send data back on.
 
 * @param data     the data we want to send. When non-empty, this will be a POST request, otherwise a GET request.
 
 */
 
NetworkHTTPRequest::NetworkHTTPRequest(const std::wstring &uri, HTTPCallback *callback, const std::string &data) :
 
	uri(uri),
 
	callback(callback),
 
	data(data)
 
{
 
	std::lock_guard<std::mutex> lock(_new_http_callback_mutex);
 
	_new_http_callbacks.push_back(&this->callback);
 
}
 

	
 
static std::string GetLastErrorAsString()
 
{
 
	char buffer[512];
 
	DWORD error_code = GetLastError();
 
@@ -110,13 +118,13 @@ void NetworkHTTPRequest::WinHttpCallback
 

	
 
		case WINHTTP_CALLBACK_STATUS_REDIRECT:
 
			/* Make sure we are not in a redirect loop. */
 
			if (this->depth++ > 5) {
 
				Debug(net, 0, "HTTP request failed: too many redirects");
 
				this->finished = true;
 
				this->callback->OnFailure();
 
				this->callback.OnFailure();
 
				return;
 
			}
 
			break;
 

	
 
		case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
 
			/* Next step: read response. */
 
@@ -133,13 +141,13 @@ void NetworkHTTPRequest::WinHttpCallback
 

	
 
			/* If there is any error, we simply abort the request. */
 
			if (status_code >= 400) {
 
				/* No need to be verbose about rate limiting. */
 
				Debug(net, status_code == HTTP_429_TOO_MANY_REQUESTS ? 1 : 0, "HTTP request failed: status-code {}", status_code);
 
				this->finished = true;
 
				this->callback->OnFailure();
 
				this->callback.OnFailure();
 
				return;
 
			}
 

	
 
			/* Next step: query for any data. */
 
			WinHttpQueryDataAvailable(this->request, nullptr);
 
		} break;
 
@@ -147,23 +155,21 @@ void NetworkHTTPRequest::WinHttpCallback
 
		case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
 
		{
 
			/* Retrieve the amount of data available to process. */
 
			DWORD size = *(DWORD *)info;
 

	
 
			/* Next step: read the data in a temporary allocated buffer.
 
			 * The buffer will be free'd in the next step. */
 
			char *buffer = size == 0 ? nullptr : MallocT<char>(size);
 
			 * The buffer will be free'd by OnReceiveData() in the next step. */
 
			char *buffer = size == 0 ? nullptr : new char[size];
 
			WinHttpReadData(this->request, buffer, size, 0);
 
		} break;
 

	
 
		case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
 
			Debug(net, 4, "HTTP callback: {} bytes", length);
 

	
 
			this->callback->OnReceiveData(static_cast<char *>(info), length);
 
			/* Free the temporary buffer that was allocated in the previous step. */
 
			free(info);
 
			this->callback.OnReceiveData(std::unique_ptr<char[]>(static_cast<char *>(info)), length);
 

	
 
			if (length == 0) {
 
				/* Next step: no more data available: request is finished. */
 
				this->finished = true;
 
				Debug(net, 1, "HTTP request succeeded");
 
			} else {
 
@@ -174,19 +180,19 @@ void NetworkHTTPRequest::WinHttpCallback
 
			break;
 

	
 
		case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
 
		case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
 
			Debug(net, 0, "HTTP request failed: {}", GetLastErrorAsString());
 
			this->finished = true;
 
			this->callback->OnFailure();
 
			this->callback.OnFailure();
 
			break;
 

	
 
		default:
 
			Debug(net, 0, "HTTP request failed: unexepected callback code 0x{:x}", code);
 
			this->finished = true;
 
			this->callback->OnFailure();
 
			this->callback.OnFailure();
 
			return;
 
	}
 
}
 

	
 
static void CALLBACK StaticWinHttpCallback(HINTERNET, DWORD_PTR context, DWORD code, void *info, DWORD length)
 
{
 
@@ -224,23 +230,23 @@ void NetworkHTTPRequest::Connect()
 

	
 
	/* Create the HTTP connection. */
 
	this->connection = WinHttpConnect(_winhttp_session, url_components.lpszHostName, url_components.nPort, 0);
 
	if (this->connection == nullptr) {
 
		Debug(net, 0, "HTTP request failed: {}", GetLastErrorAsString());
 
		this->finished = true;
 
		this->callback->OnFailure();
 
		this->callback.OnFailure();
 
		return;
 
	}
 

	
 
	this->request = WinHttpOpenRequest(connection, data.empty() ? L"GET" : L"POST", url_components.lpszUrlPath, nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, url_components.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
 
	if (this->request == nullptr) {
 
		WinHttpCloseHandle(this->connection);
 

	
 
		Debug(net, 0, "HTTP request failed: {}", GetLastErrorAsString());
 
		this->finished = true;
 
		this->callback->OnFailure();
 
		this->callback.OnFailure();
 
		return;
 
	}
 

	
 
	/* Send the request (possibly with a payload). */
 
	if (data.empty()) {
 
		WinHttpSendRequest(this->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, reinterpret_cast<DWORD_PTR>(this));
 
@@ -255,20 +261,20 @@ void NetworkHTTPRequest::Connect()
 
 * Poll and process the HTTP request/response.
 
 *
 
 * @return True iff the request is done; no call to Receive() should be done after it returns true.
 
 */
 
bool NetworkHTTPRequest::Receive()
 
{
 
	if (this->callback->IsCancelled()) {
 
	if (this->callback.cancelled && !this->finished) {
 
		Debug(net, 1, "HTTP request failed: cancelled by user");
 
		this->finished = true;
 
		this->callback->OnFailure();
 
		return true;
 
		this->callback.OnFailure();
 
		/* Fall-through, as we are waiting for IsQueueEmpty() to happen. */
 
	}
 

	
 
	return this->finished;
 
	return this->finished && this->callback.IsQueueEmpty();
 
}
 

	
 
/**
 
 * Destructor of the HTTP request.
 
 *
 
 * Makes sure all handlers are closed, and all memory is free'd.
 
@@ -276,12 +282,15 @@ bool NetworkHTTPRequest::Receive()
 
NetworkHTTPRequest::~NetworkHTTPRequest()
 
{
 
	if (this->request) {
 
		WinHttpCloseHandle(this->request);
 
		WinHttpCloseHandle(this->connection);
 
	}
 

	
 
	std::lock_guard<std::mutex> lock(_http_callback_mutex);
 
	_http_callbacks.erase(std::remove(_http_callbacks.begin(), _http_callbacks.end(), &this->callback), _http_callbacks.end());
 
}
 

	
 
/* static */ void NetworkHTTPSocketHandler::Connect(const std::string &uri, HTTPCallback *callback, const std::string data)
 
{
 
	auto request = new NetworkHTTPRequest(std::wstring(uri.begin(), uri.end()), callback, data);
 
	request->Connect();
 
@@ -289,12 +298,31 @@ NetworkHTTPRequest::~NetworkHTTPRequest(
 
	std::lock_guard<std::mutex> lock(_new_http_requests_mutex);
 
	_new_http_requests.push_back(request);
 
}
 

	
 
/* static */ void NetworkHTTPSocketHandler::HTTPReceive()
 
{
 
	/* Process all callbacks. */
 
	{
 
		std::lock_guard<std::mutex> lock(_http_callback_mutex);
 

	
 
		{
 
			std::lock_guard<std::mutex> lock(_new_http_callback_mutex);
 
			if (!_new_http_callbacks.empty()) {
 
				/* We delay adding new callbacks, as HandleQueue() below might add a new callback. */
 
				_http_callbacks.insert(_http_callbacks.end(), _new_http_callbacks.begin(), _new_http_callbacks.end());
 
				_new_http_callbacks.clear();
 
			}
 
		}
 

	
 
		for (auto &callback : _http_callbacks) {
 
			callback->HandleQueue();
 
		}
 
	}
 

	
 
	/* Process all requests. */
 
	{
 
		std::lock_guard<std::mutex> lock(_new_http_requests_mutex);
 
		if (!_new_http_requests.empty()) {
 
			/* We delay adding new requests, as Receive() below can cause a callback which adds a new requests. */
 
			_http_requests.insert(_http_requests.end(), _new_http_requests.begin(), _new_http_requests.end());
 
			_new_http_requests.clear();
src/network/network_content.cpp
Show inline comments
 
@@ -599,44 +599,47 @@ void ClientNetworkContentSocketHandler::
 
		uint files, bytes;
 

	
 
		this->DownloadSelectedContent(files, bytes, true);
 
	}
 
}
 

	
 
void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
 
void ClientNetworkContentSocketHandler::OnReceiveData(std::unique_ptr<char[]> data, size_t length)
 
{
 
	assert(data == nullptr || length != 0);
 
	assert(data.get() == nullptr || length != 0);
 

	
 
	/* Ignore any latent data coming from a connection we closed. */
 
	if (this->http_response_index == -2) return;
 
	if (this->http_response_index == -2) {
 
		return;
 
	}
 

	
 
	this->lastActivity = std::chrono::steady_clock::now();
 

	
 
	if (this->http_response_index == -1) {
 
		if (data != nullptr) {
 
			/* Append the rest of the response. */
 
			this->http_response.insert(this->http_response.end(), data, data + length);
 
			this->http_response.insert(this->http_response.end(), data.get(), data.get() + length);
 
			return;
 
		} else {
 
			/* Make sure the response is properly terminated. */
 
			this->http_response.push_back('\0');
 

	
 
			/* And prepare for receiving the rest of the data. */
 
			this->http_response_index = 0;
 
		}
 
	}
 

	
 
	if (data != nullptr) {
 
		/* We have data, so write it to the file. */
 
		if (fwrite(data, 1, length, this->curFile) != length) {
 
		if (fwrite(data.get(), 1, length, this->curFile) != length) {
 
			/* Writing failed somehow, let try via the old method. */
 
			this->OnFailure();
 
		} else {
 
			/* Just received the data. */
 
			this->OnDownloadProgress(this->curInfo, (int)length);
 
		}
 

	
 
		/* Nothing more to do now. */
 
		return;
 
	}
 

	
 
	if (this->curFile != nullptr) {
 
		/* We've finished downloading a file. */
src/network/network_content.h
Show inline comments
 
@@ -92,13 +92,13 @@ protected:
 
	void OnDisconnect() override;
 
	void OnReceiveContentInfo(const ContentInfo *ci) override;
 
	void OnDownloadProgress(const ContentInfo *ci, int bytes) override;
 
	void OnDownloadComplete(ContentID cid) override;
 

	
 
	void OnFailure() override;
 
	void OnReceiveData(const char *data, size_t length) override;
 
	void OnReceiveData(std::unique_ptr<char[]> data, size_t length) override;
 
	bool IsCancelled() const override;
 

	
 
	bool BeforeDownload();
 
	void AfterDownload();
 

	
 
	void DownloadSelectedContentHTTP(const ContentIDList &content);
src/network/network_survey.cpp
Show inline comments
 
@@ -107,13 +107,13 @@ void NetworkSurveyHandler::Transmit(Reas
 
void NetworkSurveyHandler::OnFailure()
 
{
 
	Debug(net, 1, "Survey: failed to send survey results");
 
	this->loaded.notify_all();
 
}
 

	
 
void NetworkSurveyHandler::OnReceiveData(const char *data, size_t)
 
void NetworkSurveyHandler::OnReceiveData(std::unique_ptr<char[]> data, size_t)
 
{
 
	if (data == nullptr) {
 
		Debug(net, 1, "Survey: survey results sent");
 
		this->loaded.notify_all();
 
	}
 
}
src/network/network_survey.h
Show inline comments
 
@@ -17,13 +17,13 @@
 
/**
 
 * Socket handler for the survey connection
 
 */
 
class NetworkSurveyHandler : public HTTPCallback {
 
protected:
 
	void OnFailure() override;
 
	void OnReceiveData(const char *data, size_t length) override;
 
	void OnReceiveData(std::unique_ptr<char[]> data, size_t length) override;
 
	bool IsCancelled() const override { return false; }
 

	
 
public:
 
	enum class Reason {
 
		PREVIEW, ///< User is previewing the survey result.
 
		LEAVE, ///< User is leaving the game (but not exiting the application).
0 comments (0 inline, 0 general)