12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 #include "../../rev.h"
15 #include "../network_internal.h"
23 #include "../../safeguards.h"
25 static HINTERNET _winhttp_session =
nullptr;
30 const std::wstring
uri;
32 const std::string
data;
49 static std::vector<NetworkHTTPRequest *> _http_requests;
50 static std::vector<NetworkHTTPRequest *> _new_http_requests;
51 static std::mutex _new_http_requests_mutex;
53 static std::vector<HTTPThreadSafeCallback *> _http_callbacks;
54 static std::vector<HTTPThreadSafeCallback *> _new_http_callbacks;
55 static std::mutex _http_callback_mutex;
56 static std::mutex _new_http_callback_mutex;
70 std::lock_guard<std::mutex>
lock(_new_http_callback_mutex);
71 _new_http_callbacks.push_back(&this->callback);
74 static std::string GetLastErrorAsString()
78 DWORD error_code = GetLastError();
80 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle(L
"winhttp.dll"), error_code,
81 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer,
static_cast<DWORD
>(std::size(buffer)),
nullptr) == 0) {
82 return fmt::format(
"unknown error {}", error_code);
105 case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
106 case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
107 case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
108 case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
109 case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
110 case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
111 case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
112 case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
113 case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
114 case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
115 case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
116 case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
120 case WINHTTP_CALLBACK_STATUS_REDIRECT:
122 if (this->
depth++ > 5) {
123 Debug(net, 0,
"HTTP request failed: too many redirects");
130 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
132 WinHttpReceiveResponse(this->
request,
nullptr);
135 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
138 DWORD status_code = 0;
139 DWORD status_code_size =
sizeof(status_code);
140 WinHttpQueryHeaders(this->
request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX);
141 Debug(net, 3,
"HTTP request status code: {}", status_code);
144 if (status_code >= 400) {
146 Debug(net, status_code == HTTP_429_TOO_MANY_REQUESTS ? 1 : 0,
"HTTP request failed: status-code {}", status_code);
153 WinHttpQueryDataAvailable(this->
request,
nullptr);
156 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
159 DWORD size = *(DWORD *)info;
163 char *buffer = size == 0 ? nullptr :
new char[size];
164 WinHttpReadData(this->
request, buffer, size, 0);
167 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
168 Debug(net, 6,
"HTTP callback: {} bytes", length);
175 Debug(net, 1,
"HTTP request succeeded");
178 WinHttpQueryDataAvailable(this->
request,
nullptr);
183 case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
184 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
185 Debug(net, 0,
"HTTP request failed: {}", GetLastErrorAsString());
191 Debug(net, 0,
"HTTP request failed: unexepected callback code 0x{:x}", code);
198 static void CALLBACK StaticWinHttpCallback(HINTERNET, DWORD_PTR context, DWORD code,
void *info, DWORD length)
200 if (context == 0)
return;
215 Debug(net, 1,
"HTTP request to {}", std::string(
uri.begin(),
uri.end()));
217 URL_COMPONENTS url_components = {};
219 wchar_t hostname[128];
220 wchar_t url_path[4096];
223 url_components.dwStructSize =
sizeof(url_components);
224 url_components.lpszScheme = scheme;
225 url_components.dwSchemeLength =
static_cast<DWORD
>(std::size(scheme));
226 url_components.lpszHostName = hostname;
227 url_components.dwHostNameLength =
static_cast<DWORD
>(std::size(hostname));
228 url_components.lpszUrlPath = url_path;
229 url_components.dwUrlPathLength =
static_cast<DWORD
>(std::size(url_path));
230 WinHttpCrackUrl(this->
uri.c_str(), 0, 0, &url_components);
233 this->
connection = WinHttpConnect(_winhttp_session, url_components.lpszHostName, url_components.nPort, 0);
235 Debug(net, 0,
"HTTP request failed: {}", GetLastErrorAsString());
241 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);
242 if (this->request ==
nullptr) {
245 Debug(net, 0,
"HTTP request failed: {}", GetLastErrorAsString());
253 WinHttpSendRequest(this->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0,
reinterpret_cast<DWORD_PTR
>(
this));
256 LPCWSTR content_type =
data.starts_with(
"{") ? L
"Content-Type: application/json\r\n" : L
"Content-Type: application/x-www-form-urlencoded\r\n";
257 WinHttpSendRequest(this->request, content_type, -1,
const_cast<char *
>(
data.c_str()),
static_cast<DWORD
>(
data.size()),
static_cast<DWORD
>(
data.size()),
reinterpret_cast<DWORD_PTR
>(
this));
268 if (this->
callback.cancelled && !this->finished) {
269 Debug(net, 1,
"HTTP request failed: cancelled by user");
286 WinHttpCloseHandle(this->request);
290 std::lock_guard<std::mutex>
lock(_http_callback_mutex);
291 _http_callbacks.erase(std::remove(_http_callbacks.begin(), _http_callbacks.end(), &this->callback), _http_callbacks.end());
296 auto request =
new NetworkHTTPRequest(std::wstring(uri.begin(), uri.end()), callback, data);
299 std::lock_guard<std::mutex>
lock(_new_http_requests_mutex);
300 _new_http_requests.push_back(request);
307 std::lock_guard<std::mutex>
lock(_http_callback_mutex);
310 std::lock_guard<std::mutex>
lock(_new_http_callback_mutex);
311 if (!_new_http_callbacks.empty()) {
313 _http_callbacks.insert(_http_callbacks.end(), _new_http_callbacks.begin(), _new_http_callbacks.end());
314 _new_http_callbacks.clear();
318 for (
auto &callback : _http_callbacks) {
319 callback->HandleQueue();
325 std::lock_guard<std::mutex>
lock(_new_http_requests_mutex);
326 if (!_new_http_requests.empty()) {
328 _http_requests.insert(_http_requests.end(), _new_http_requests.begin(), _new_http_requests.end());
329 _new_http_requests.clear();
333 if (_http_requests.empty())
return;
335 for (
auto it = _http_requests.begin(); it != _http_requests.end(); ) {
339 it = _http_requests.erase(it);
351 std::string user_agent = fmt::format(
"OpenTTD/{}", GetNetworkRevisionString());
352 _winhttp_session = WinHttpOpen(std::wstring(user_agent.begin(), user_agent.end()).c_str(), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
355 WinHttpSetStatusCallback(_winhttp_session, StaticWinHttpCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
358 WinHttpSetTimeouts(_winhttp_session, 10000, 10000, 10000, 10000);
363 WinHttpCloseHandle(_winhttp_session);