diff --git a/openrct2.common.props b/openrct2.common.props
index 6c38bd49b4..205501951b 100644
--- a/openrct2.common.props
+++ b/openrct2.common.props
@@ -53,7 +53,7 @@
C4549: 'operator': operator before comma has no effect; did you intend 'operator'?
C4555: expression has no effect; expected expression with side-effect
-->
- __AVX2__;__SSE4_1__;OPENGL_NO_LINK;_CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;CURL_STATICLIB;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions)
+ __AVX2__;__SSE4_1__;OPENGL_NO_LINK;_CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions)
MultiThreaded
MultiThreadedDLL
true
@@ -61,7 +61,7 @@
/utf-8 /std:c++17 /permissive- /Zc:externConstexpr
- wininet.lib;imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;shlwapi.lib;setupapi.lib;bcrypt.lib;%(AdditionalDependencies)
+ wininet.lib;imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;shlwapi.lib;setupapi.lib;bcrypt.lib;winhttp.lib;%(AdditionalDependencies)
/OPT:NOLBR /ignore:4099 %(AdditionalOptions)
@@ -76,7 +76,7 @@
DebugFull
- benchmarkd.lib;libbreakpadd.lib;libbreakpad_clientd.lib;bz2d.lib;discord-rpc.lib;freetyped.lib;jansson_d.lib;libcurl-d.lib;libpng16d.lib;libspeexdsp.lib;SDL2d.lib;zip.lib;zlibd.lib;%(AdditionalDependencies)
+ benchmarkd.lib;libbreakpadd.lib;libbreakpad_clientd.lib;bz2d.lib;discord-rpc.lib;freetyped.lib;jansson_d.lib;libpng16d.lib;libspeexdsp.lib;SDL2d.lib;zip.lib;zlibd.lib;%(AdditionalDependencies)
@@ -94,7 +94,7 @@
DebugFull
true
true
- benchmark.lib;libbreakpad.lib;libbreakpad_client.lib;bz2.lib;discord-rpc.lib;freetype.lib;jansson.lib;libcurl.lib;libpng16.lib;libspeexdsp.lib;SDL2.lib;zip.lib;zlib.lib;%(AdditionalDependencies)
+ benchmark.lib;libbreakpad.lib;libbreakpad_client.lib;bz2.lib;discord-rpc.lib;freetype.lib;jansson.lib;libpng16.lib;libspeexdsp.lib;SDL2.lib;zip.lib;zlib.lib;%(AdditionalDependencies)
diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp
index 9cd93d4af9..ca799c2f6a 100644
--- a/src/openrct2-ui/windows/ObjectLoadError.cpp
+++ b/src/openrct2-ui/windows/ObjectLoadError.cpp
@@ -11,10 +11,10 @@
#include
#include
#include
+#include
#include
#include
#include
-#include
#include
#include
#include
@@ -151,7 +151,6 @@ private:
void DownloadObject(const rct_object_entry& entry, const std::string name, const std::string url)
{
- using namespace OpenRCT2::Networking;
try
{
std::printf("Downloading %s\n", url.c_str());
@@ -190,8 +189,6 @@ private:
void NextDownload()
{
- using namespace OpenRCT2::Networking;
-
if (!_downloadingObjects || _currentDownloadIndex >= _entries.size())
{
// Finished...
diff --git a/src/openrct2-ui/windows/ServerList.cpp b/src/openrct2-ui/windows/ServerList.cpp
index fcf7e20e74..8f9bc01b1e 100644
--- a/src/openrct2-ui/windows/ServerList.cpp
+++ b/src/openrct2-ui/windows/ServerList.cpp
@@ -21,7 +21,6 @@
# include
# include
# include
-# include
# include
# include
# include
diff --git a/src/openrct2/CMakeLists.txt b/src/openrct2/CMakeLists.txt
index 5353df0d5f..f9a5ecb195 100644
--- a/src/openrct2/CMakeLists.txt
+++ b/src/openrct2/CMakeLists.txt
@@ -50,19 +50,18 @@ if (NOT DISABLE_NETWORK AND WIN32)
endif ()
if (NOT DISABLE_HTTP)
- if (MSVC)
- find_package(curl REQUIRED)
- set(LIBCURL_LIBRARIES ${CURL_LIBRARIES})
+ if (WIN32)
+ target_link_libraries(${PROJECT_NAME} winhttp)
else ()
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
- endif ()
-
- target_include_directories(${PROJECT_NAME} PRIVATE ${LIBCURL_INCLUDE_DIRS})
-
- if (STATIC)
- target_link_libraries(${PROJECT_NAME} ${LIBCURL_STATIC_LIBRARIES})
- else ()
- target_link_libraries(${PROJECT_NAME} ${LIBCURL_LIBRARIES})
+
+ target_include_directories(${PROJECT_NAME} PRIVATE ${LIBCURL_INCLUDE_DIRS})
+
+ if (STATIC)
+ target_link_libraries(${PROJECT_NAME} ${LIBCURL_STATIC_LIBRARIES})
+ else ()
+ target_link_libraries(${PROJECT_NAME} ${LIBCURL_LIBRARIES})
+ endif ()
endif ()
endif ()
diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp
index 34acc7788d..72de7259f7 100644
--- a/src/openrct2/Context.cpp
+++ b/src/openrct2/Context.cpp
@@ -33,6 +33,7 @@
#include "core/FileScanner.h"
#include "core/FileStream.hpp"
#include "core/Guard.hpp"
+#include "core/Http.h"
#include "core/MemoryStream.h"
#include "core/Path.hpp"
#include "core/String.hpp"
@@ -45,7 +46,6 @@
#include "localisation/Localisation.h"
#include "localisation/LocalisationService.h"
#include "network/DiscordService.h"
-#include "network/Http.h"
#include "network/Twitch.h"
#include "network/network.h"
#include "object/ObjectManager.h"
@@ -99,9 +99,6 @@ namespace OpenRCT2
std::unique_ptr _discordService;
#endif
StdInOutConsole _stdInOutConsole;
-#ifndef DISABLE_HTTP
- Networking::Http::Http _http;
-#endif
// Game states
std::unique_ptr _titleScreen;
@@ -721,15 +718,14 @@ namespace OpenRCT2
{
#ifndef DISABLE_HTTP
// Download park and open it using its temporary filename
- void* data;
- size_t dataSize = Networking::Http::DownloadPark(gOpenRCT2StartupActionPath, &data);
- if (dataSize == 0)
+ auto data = DownloadPark(gOpenRCT2StartupActionPath);
+ if (data.empty())
{
title_load();
break;
}
- auto ms = MemoryStream(data, dataSize, MEMORY_ACCESS::OWNER);
+ auto ms = MemoryStream(data.data(), data.size(), MEMORY_ACCESS::READ);
if (!LoadParkFromStream(&ms, gOpenRCT2StartupActionPath, true))
{
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
@@ -1083,6 +1079,34 @@ namespace OpenRCT2
}
delete scanner;
}
+
+#ifndef DISABLE_HTTP
+ std::vector DownloadPark(const std::string& url)
+ {
+ // Download park to buffer in memory
+ Http::Request request;
+ request.url = url;
+ request.method = Http::Method::GET;
+
+ Http::Response res;
+ try
+ {
+ res = Do(request);
+ if (res.status != Http::Status::OK)
+ throw std::runtime_error("bad http status");
+ }
+ catch (std::exception& e)
+ {
+ Console::Error::WriteLine("Failed to download '%s', cause %s", request.url.c_str(), e.what());
+ return {};
+ }
+
+ std::vector parkData;
+ parkData.resize(res.body.size());
+ std::memcpy(parkData.data(), res.body.c_str(), parkData.size());
+ return parkData;
+ }
+#endif
};
Context* Context::Instance = nullptr;
diff --git a/src/openrct2/core/Http.WinHttp.cpp b/src/openrct2/core/Http.WinHttp.cpp
new file mode 100644
index 0000000000..9d30365cbc
--- /dev/null
+++ b/src/openrct2/core/Http.WinHttp.cpp
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2020 OpenRCT2 developers
+ *
+ * For a complete list of all authors, please refer to contributors.md
+ * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is licensed under the GNU General Public License version 3.
+ *****************************************************************************/
+
+#if !defined(DISABLE_HTTP) && defined(_WIN32)
+
+# include "Http.h"
+
+# include "../Version.h"
+# include "String.hpp"
+
+# include
+# include
+# define WIN32_LEAN_AND_MEAN
+# include
+# include
+
+namespace Http
+{
+ static constexpr char OPENRCT2_USER_AGENT[] = "OpenRCT2/" OPENRCT2_VERSION;
+
+ static void ThrowWin32Exception(const char* methodName)
+ {
+ auto errorCode = GetLastError();
+ auto msg = String::StdFormat("%s failed, error: %d.", methodName, errorCode);
+ throw std::runtime_error(msg);
+ }
+
+ static const wchar_t* GetVerb(Method method)
+ {
+ switch (method)
+ {
+ case Method::GET:
+ return L"GET";
+ case Method::POST:
+ return L"POST";
+ case Method::PUT:
+ return L"PUT";
+ default:
+ throw std::runtime_error("Unsupported verb.");
+ }
+ }
+
+ static std::wstring ReadHeader(HINTERNET hRequest, DWORD infoLevel, const wchar_t* name)
+ {
+ wchar_t headerBuffer[256]{};
+ auto headerBufferLen = (DWORD)std::size(headerBuffer);
+ if (!WinHttpQueryHeaders(hRequest, infoLevel, name, headerBuffer, &headerBufferLen, WINHTTP_NO_HEADER_INDEX))
+ ThrowWin32Exception("WinHttpQueryHeaders");
+ return std::wstring(headerBuffer, headerBufferLen);
+ }
+
+ static int32_t ReadStatusCode(HINTERNET hRequest)
+ {
+ auto wStatusCode = ReadHeader(hRequest, WINHTTP_QUERY_STATUS_CODE, L"StatusCode");
+ return std::stoi(wStatusCode);
+ }
+
+ static std::map ReadHeaders(HINTERNET hRequest)
+ {
+ DWORD dwSize{};
+ WinHttpQueryHeaders(
+ hRequest, WINHTTP_QUERY_RAW_HEADERS, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX);
+
+ std::wstring buffer;
+ buffer.resize(dwSize);
+ if (!WinHttpQueryHeaders(
+ hRequest, WINHTTP_QUERY_RAW_HEADERS, WINHTTP_HEADER_NAME_BY_INDEX, buffer.data(), &dwSize,
+ WINHTTP_NO_HEADER_INDEX))
+ ThrowWin32Exception("WinHttpQueryHeaders");
+
+ std::map headers;
+ std::wstring wKey, wValue;
+ int state = 0;
+ int index = 0;
+ for (auto c : buffer)
+ {
+ if (c == '\0')
+ {
+ state = 0;
+ if (index != 0 && wKey.size() != 0)
+ {
+ auto key = String::ToUtf8(wKey);
+ auto value = String::ToUtf8(wValue);
+ headers[key] = value;
+ }
+ wKey.clear();
+ wValue.clear();
+ index++;
+ continue;
+ }
+ if (state == 0 && c == ':')
+ {
+ state = 1;
+ }
+ else if (state == 1 && c == ' ')
+ {
+ state = 2;
+ }
+ else if (state == 0)
+ {
+ wKey.push_back(c);
+ }
+ else
+ {
+ state = 2;
+ wValue.push_back(c);
+ }
+ }
+ return headers;
+ }
+
+ static std::string ReadBody(HINTERNET hRequest)
+ {
+ DWORD dwSize{};
+ std::string body;
+ do
+ {
+ // Check for available data.
+ if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
+ ThrowWin32Exception("WinHttpQueryDataAvailable");
+
+ // No more available data.
+ if (dwSize == 0)
+ break;
+
+ auto offset = body.size();
+ body.resize(offset + dwSize);
+ auto dst = (LPVOID)&body[offset];
+
+ DWORD dwDownloaded{};
+ if (!WinHttpReadData(hRequest, dst, dwSize, &dwDownloaded))
+ ThrowWin32Exception("WinHttpReadData");
+
+ // This condition should never be reached since WinHttpQueryDataAvailable
+ // reported that there are bits to read.
+ if (dwDownloaded == 0)
+ break;
+ } while (dwSize > 0);
+ return body;
+ }
+
+ Response Do(const Request& req)
+ {
+ HINTERNET hSession{}, hConnect{}, hRequest{};
+ try
+ {
+ URL_COMPONENTS url;
+ ZeroMemory(&url, sizeof(url));
+ url.dwStructSize = sizeof(url);
+ url.dwSchemeLength = (DWORD)-1;
+ url.dwHostNameLength = (DWORD)-1;
+ url.dwUrlPathLength = (DWORD)-1;
+ url.dwExtraInfoLength = (DWORD)-1;
+
+ auto wUrl = String::ToWideChar(req.url);
+ if (!WinHttpCrackUrl(wUrl.c_str(), 0, 0, &url))
+ throw std::invalid_argument("Unable to parse URI.");
+
+ auto userAgent = String::ToWideChar(OPENRCT2_USER_AGENT);
+ hSession = WinHttpOpen(
+ userAgent.c_str(), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
+ if (hSession == nullptr)
+ ThrowWin32Exception("WinHttpOpen");
+
+ auto wHostName = std::wstring(url.lpszHostName, url.dwHostNameLength);
+ hConnect = WinHttpConnect(hSession, wHostName.c_str(), url.nPort, 0);
+ if (hConnect == nullptr)
+ ThrowWin32Exception("WinHttpConnect");
+
+ auto wVerb = GetVerb(req.method);
+ auto wQuery = std::wstring(url.lpszUrlPath, url.dwUrlPathLength);
+ hRequest = WinHttpOpenRequest(
+ hConnect, wVerb, wQuery.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
+ if (hRequest == nullptr)
+ ThrowWin32Exception("WinHttpOpenRequest");
+
+ for (auto header : req.header)
+ {
+ auto fullHeader = String::ToWideChar(header.first) + L": " + String::ToWideChar(header.second);
+ if (!WinHttpAddRequestHeaders(hRequest, fullHeader.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD))
+ ThrowWin32Exception("WinHttpAddRequestHeaders");
+ }
+
+ auto reqBody = (LPVOID)req.body.data();
+ auto reqBodyLen = (DWORD)req.body.size();
+ if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, reqBody, reqBodyLen, reqBodyLen, 0))
+ ThrowWin32Exception("WinHttpSendRequest");
+
+ if (!WinHttpReceiveResponse(hRequest, NULL))
+ ThrowWin32Exception("WinHttpReceiveResponse");
+
+ auto statusCode = ReadStatusCode(hRequest);
+ auto contentType = ReadHeader(hRequest, WINHTTP_QUERY_CONTENT_TYPE, L"Content-Type");
+ auto headers = ReadHeaders(hRequest);
+ auto body = ReadBody(hRequest);
+
+ Response response;
+ response.body = std::move(body);
+ response.status = (Status)statusCode;
+ response.content_type = String::ToUtf8(contentType);
+ response.header = headers;
+ return response;
+ }
+ catch ([[maybe_unused]] const std::exception& e)
+ {
+# ifdef DEBUG
+ std::fprintf(stderr, "HTTP request failed: %s\n", e.what());
+# endif
+ WinHttpCloseHandle(hSession);
+ WinHttpCloseHandle(hConnect);
+ WinHttpCloseHandle(hRequest);
+ throw;
+ }
+ }
+} // namespace Http
+
+#endif
diff --git a/src/openrct2/network/Http.cpp b/src/openrct2/core/Http.cURL.cpp
similarity index 73%
rename from src/openrct2/network/Http.cpp
rename to src/openrct2/core/Http.cURL.cpp
index 926f097423..3a1f690851 100644
--- a/src/openrct2/network/Http.cpp
+++ b/src/openrct2/core/Http.cURL.cpp
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014-2019 OpenRCT2 developers
+ * Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
@@ -7,17 +7,16 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
-#include "Http.h"
-
-#include
-#include
-#include
-#include
-
-#ifndef DISABLE_HTTP
+#if !defined(DISABLE_HTTP) && !defined(_WIN32)
# include "../Version.h"
# include "../core/Console.hpp"
+# include "Http.h"
+
+# include
+# include
+# include
+# include
# ifdef _WIN32
// cURL includes windows.h, but we don't need all of it.
@@ -27,18 +26,8 @@
# define OPENRCT2_USER_AGENT "OpenRCT2/" OPENRCT2_VERSION
-namespace OpenRCT2::Networking::Http
+namespace Http
{
- Http::Http()
- {
- curl_global_init(CURL_GLOBAL_DEFAULT);
- }
-
- Http::~Http()
- {
- curl_global_cleanup();
- }
-
static size_t writeData(const char* src, size_t size, size_t nmemb, void* userdata)
{
size_t realsize = size * nmemb;
@@ -168,59 +157,6 @@ namespace OpenRCT2::Networking::Http
return res;
}
- void DoAsync(const Request& req, std::function fn)
- {
- auto thread = std::thread([=]() {
- Response res;
- try
- {
- res = Do(req);
- }
- catch (std::exception& e)
- {
- res.error = e.what();
- return;
- }
- fn(res);
- });
- thread.detach();
- }
-
- size_t DownloadPark(const char* url, void** outData)
- {
- // Download park to buffer in memory
- Request request;
- request.url = url;
- request.method = Method::GET;
-
- Response res;
- try
- {
- res = Do(request);
- if (res.status != Status::OK)
- std::runtime_error("bad http status");
- }
- catch (std::exception& e)
- {
- Console::Error::WriteLine("Failed to download '%s', cause %s", request.url.c_str(), e.what());
- *outData = nullptr;
- return 0;
- }
-
- size_t dataSize = res.body.size() - 1;
- void* data = malloc(dataSize);
- if (data == nullptr)
- {
- Console::Error::WriteLine("Failed to allocate memory for downloaded park.");
- return 0;
- }
-
- std::memcpy(data, res.body.c_str(), dataSize);
- *outData = data;
-
- return dataSize;
- }
-
-} // namespace OpenRCT2::Networking::Http
+} // namespace Http
#endif
diff --git a/src/openrct2/network/Http.h b/src/openrct2/core/Http.h
similarity index 65%
rename from src/openrct2/network/Http.h
rename to src/openrct2/core/Http.h
index 1d6f08eb8a..969446a362 100644
--- a/src/openrct2/network/Http.h
+++ b/src/openrct2/core/Http.h
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014-2019 OpenRCT2 developers
+ * Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
@@ -16,8 +16,9 @@
# include
# include