Changeset - r28737:b9cd8ad9a210
[Not reviewed]
master
0 5 0
Patric Stout - 10 months ago 2024-02-11 19:24:28
truebrain@openttd.org
Fix fdfcb09: for content service, fallback to TCP downloads when HTTP stalls (#12056)
5 files changed with 14 insertions and 6 deletions:
0 comments (0 inline, 0 general)
src/network/core/http_curl.cpp
Show inline comments
 
@@ -181,25 +181,25 @@ void HttpThread()
 
			} else {
 
				headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
 
			}
 

	
 
			curl_easy_setopt(curl, CURLOPT_POST, 1L);
 
			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->data.c_str());
 
			curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
 
		}
 
		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);
 
			Debug(net, 6, "HTTP callback: {} bytes", 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);
 

	
 
		/* Create a callback from which we can cancel. Sadly, there is no other
src/network/core/http_winhttp.cpp
Show inline comments
 
@@ -156,25 +156,25 @@ 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 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);
 
			Debug(net, 6, "HTTP callback: {} bytes", length);
 

	
 
			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 {
 
				/* Next step: query for more data. */
 
				WinHttpQueryDataAvailable(this->request, nullptr);
 
			}
 

	
src/network/network_content.cpp
Show inline comments
 
@@ -602,26 +602,24 @@ void ClientNetworkContentSocketHandler::
 
	}
 
}
 

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

	
 
	/* Ignore any latent data coming from a connection we closed. */
 
	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.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;
 
		}
 
@@ -790,43 +788,52 @@ void ClientNetworkContentSocketHandler::
 

	
 
	this->isCancelled = false;
 
	this->isConnecting = true;
 

	
 
	TCPConnecter::Create<NetworkContentConnecter>(NetworkContentServerConnectionString());
 
}
 

	
 
/**
 
 * Disconnect from the content server.
 
 */
 
NetworkRecvStatus ClientNetworkContentSocketHandler::CloseConnection(bool)
 
{
 
	this->isCancelled = true;
 
	NetworkContentSocketHandler::CloseConnection();
 

	
 
	if (this->sock == INVALID_SOCKET) return NETWORK_RECV_STATUS_OKAY;
 

	
 
	this->CloseSocket();
 
	this->OnDisconnect();
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Cancel the current download.
 
 */
 
void ClientNetworkContentSocketHandler::Cancel(void)
 
{
 
	this->isCancelled = true;
 
	this->CloseConnection();
 
}
 

	
 
/**
 
 * Check whether we received/can send some data from/to the content server and
 
 * when that's the case handle it appropriately
 
 */
 
void ClientNetworkContentSocketHandler::SendReceive()
 
{
 
	if (this->sock == INVALID_SOCKET || this->isConnecting) return;
 

	
 
	/* Close the connection to the content server after inactivity; there can still be downloads pending via HTTP. */
 
	if (std::chrono::steady_clock::now() > this->lastActivity + IDLE_TIMEOUT) {
 
		this->CloseConnection();
 
		return;
 
	}
 

	
 
	if (this->CanSendReceive()) {
 
		if (this->ReceivePackets()) {
 
			/* Only update activity once a packet is received, instead of every time we try it. */
 
			this->lastActivity = std::chrono::steady_clock::now();
 
		}
 
	}
 

	
src/network/network_content.h
Show inline comments
 
@@ -104,24 +104,25 @@ protected:
 
	void DownloadSelectedContentHTTP(const ContentIDList &content);
 
	void DownloadSelectedContentFallback(const ContentIDList &content);
 
public:
 
	/** The idle timeout; when to close the connection because it's idle. */
 
	static constexpr std::chrono::seconds IDLE_TIMEOUT = std::chrono::seconds(60);
 

	
 
	ClientNetworkContentSocketHandler();
 
	~ClientNetworkContentSocketHandler();
 

	
 
	void Connect();
 
	void SendReceive();
 
	NetworkRecvStatus CloseConnection(bool error = true) override;
 
	void Cancel();
 

	
 
	void RequestContentList(ContentType type);
 
	void RequestContentList(uint count, const ContentID *content_ids);
 
	void RequestContentList(ContentVector *cv, bool send_md5sum = true);
 

	
 
	void DownloadSelectedContent(uint &files, uint &bytes, bool fallback = false);
 

	
 
	void Select(ContentID cid);
 
	void Unselect(ContentID cid);
 
	void SelectAll();
 
	void SelectUpgrade();
 
	void UnselectAll();
src/network/network_content_gui.cpp
Show inline comments
 
@@ -282,25 +282,25 @@ public:
 
		}
 

	
 
		/* Always invalidate the download window; tell it we are going to be gone */
 
		InvalidateWindowData(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST, 2);
 

	
 
		this->BaseNetworkContentDownloadStatusWindow::Close();
 
	}
 

	
 
	void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
 
	{
 
		if (widget == WID_NCDS_CANCELOK) {
 
			if (this->downloaded_bytes != this->total_bytes) {
 
				_network_content_client.CloseConnection();
 
				_network_content_client.Cancel();
 
				this->Close();
 
			} else {
 
				/* If downloading succeeded, close the online content window. This will close
 
				 * the current window as well. */
 
				CloseWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST);
 
			}
 
		}
 
	}
 

	
 
	void OnDownloadProgress(const ContentInfo *ci, int bytes) override
 
	{
 
		BaseNetworkContentDownloadStatusWindow::OnDownloadProgress(ci, bytes);
0 comments (0 inline, 0 general)