1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: wx/msw/webrequest_winhttp.h
3 // Purpose: wxWebRequest WinHTTP implementation
4 // Author: Tobias Taschner
5 // Created: 2018-10-17
6 // Copyright: (c) 2018 wxWidgets development team
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #include "wx/webrequest.h"
14
15 #if wxUSE_WEBREQUEST && wxUSE_WEBREQUEST_WINHTTP
16
17 #include "wx/mstream.h"
18 #include "wx/dynlib.h"
19 #include "wx/msw/private.h"
20 #include "wx/msw/private/webrequest_winhttp.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/log.h"
24 #include "wx/utils.h"
25 #include "wx/translation.h"
26 #endif
27
28 // Helper class used to dynamically load the required symbols from winhttp.dll
29 class wxWinHTTP
30 {
31 public:
LoadLibrary()32 static bool LoadLibrary()
33 {
34 bool result = m_winhttp.Load("winhttp.dll", wxDL_VERBATIM | wxDL_QUIET);
35 if ( !result )
36 return result;
37
38 #define wxLOAD_FUNC(name) \
39 wxDL_INIT_FUNC(, name, m_winhttp); \
40 result &= (name != NULL);
41
42 wxLOAD_FUNC(WinHttpQueryOption)
43 wxLOAD_FUNC(WinHttpQueryHeaders)
44 wxLOAD_FUNC(WinHttpSetOption)
45 wxLOAD_FUNC(WinHttpWriteData)
46 wxLOAD_FUNC(WinHttpCloseHandle)
47 wxLOAD_FUNC(WinHttpReceiveResponse)
48 wxLOAD_FUNC(WinHttpCrackUrl)
49 wxLOAD_FUNC(WinHttpConnect)
50 wxLOAD_FUNC(WinHttpOpenRequest)
51 wxLOAD_FUNC(WinHttpSetStatusCallback)
52 wxLOAD_FUNC(WinHttpSendRequest)
53 wxLOAD_FUNC(WinHttpReadData)
54 wxLOAD_FUNC(WinHttpQueryAuthSchemes)
55 wxLOAD_FUNC(WinHttpSetCredentials)
56 wxLOAD_FUNC(WinHttpOpen)
57
58 if ( !result )
59 m_winhttp.Unload();
60
61 return result;
62 }
63
64 typedef BOOL(WINAPI* WinHttpQueryOption_t)(HINTERNET, DWORD, LPVOID, LPDWORD);
65 static WinHttpQueryOption_t WinHttpQueryOption;
66 typedef BOOL(WINAPI* WinHttpQueryHeaders_t)(HINTERNET, DWORD, LPCWSTR, LPVOID, LPDWORD, LPDWORD);
67 static WinHttpQueryHeaders_t WinHttpQueryHeaders;
68 typedef BOOL(WINAPI* WinHttpSetOption_t)(HINTERNET, DWORD, LPVOID, DWORD);
69 static WinHttpSetOption_t WinHttpSetOption;
70 typedef BOOL(WINAPI* WinHttpWriteData_t)(HINTERNET, LPCVOID, DWORD, LPDWORD);
71 static WinHttpWriteData_t WinHttpWriteData;
72 typedef BOOL(WINAPI* WinHttpCloseHandle_t)(HINTERNET);
73 static WinHttpCloseHandle_t WinHttpCloseHandle;
74 typedef BOOL(WINAPI* WinHttpReceiveResponse_t)(HINTERNET, LPVOID);
75 static WinHttpReceiveResponse_t WinHttpReceiveResponse;
76 typedef BOOL(WINAPI* WinHttpCrackUrl_t)(LPCWSTR, DWORD, DWORD, LPURL_COMPONENTS);
77 static WinHttpCrackUrl_t WinHttpCrackUrl;
78 typedef HINTERNET(WINAPI* WinHttpConnect_t)(HINTERNET, LPCWSTR, INTERNET_PORT, DWORD);
79 static WinHttpConnect_t WinHttpConnect;
80 typedef HINTERNET(WINAPI* WinHttpOpenRequest_t)(HINTERNET, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR*, DWORD);
81 static WinHttpOpenRequest_t WinHttpOpenRequest;
82 typedef WINHTTP_STATUS_CALLBACK(WINAPI* WinHttpSetStatusCallback_t)(HINTERNET, WINHTTP_STATUS_CALLBACK, DWORD, DWORD_PTR);
83 static WinHttpSetStatusCallback_t WinHttpSetStatusCallback;
84 typedef BOOL(WINAPI* WinHttpSendRequest_t)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD, DWORD, DWORD_PTR);
85 static WinHttpSendRequest_t WinHttpSendRequest;
86 typedef BOOL(WINAPI* WinHttpReadData_t)(HINTERNET, LPVOID, DWORD, LPDWORD);
87 static WinHttpReadData_t WinHttpReadData;
88 typedef BOOL(WINAPI* WinHttpQueryAuthSchemes_t)(HINTERNET, LPDWORD, LPDWORD, LPDWORD);
89 static WinHttpQueryAuthSchemes_t WinHttpQueryAuthSchemes;
90 typedef BOOL(WINAPI* WinHttpSetCredentials_t)(HINTERNET, DWORD, DWORD, LPCWSTR, LPCWSTR, LPVOID);
91 static WinHttpSetCredentials_t WinHttpSetCredentials;
92 typedef HINTERNET(WINAPI* WinHttpOpen_t)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
93 static WinHttpOpen_t WinHttpOpen;
94
95 private:
96 static wxDynamicLibrary m_winhttp;
97 };
98
99 wxDynamicLibrary wxWinHTTP::m_winhttp;
100 wxWinHTTP::WinHttpQueryOption_t wxWinHTTP::WinHttpQueryOption;
101 wxWinHTTP::WinHttpQueryHeaders_t wxWinHTTP::WinHttpQueryHeaders;
102 wxWinHTTP::WinHttpSetOption_t wxWinHTTP::WinHttpSetOption;
103 wxWinHTTP::WinHttpWriteData_t wxWinHTTP::WinHttpWriteData;
104 wxWinHTTP::WinHttpCloseHandle_t wxWinHTTP::WinHttpCloseHandle;
105 wxWinHTTP::WinHttpReceiveResponse_t wxWinHTTP::WinHttpReceiveResponse;
106 wxWinHTTP::WinHttpCrackUrl_t wxWinHTTP::WinHttpCrackUrl;
107 wxWinHTTP::WinHttpConnect_t wxWinHTTP::WinHttpConnect;
108 wxWinHTTP::WinHttpOpenRequest_t wxWinHTTP::WinHttpOpenRequest;
109 wxWinHTTP::WinHttpSetStatusCallback_t wxWinHTTP::WinHttpSetStatusCallback;
110 wxWinHTTP::WinHttpSendRequest_t wxWinHTTP::WinHttpSendRequest;
111 wxWinHTTP::WinHttpReadData_t wxWinHTTP::WinHttpReadData;
112 wxWinHTTP::WinHttpQueryAuthSchemes_t wxWinHTTP::WinHttpQueryAuthSchemes;
113 wxWinHTTP::WinHttpSetCredentials_t wxWinHTTP::WinHttpSetCredentials;
114 wxWinHTTP::WinHttpOpen_t wxWinHTTP::WinHttpOpen;
115
116
117 // Define constants potentially missing in old SDKs
118 #ifndef WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
119 #define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
120 #endif
121 #ifndef WINHTTP_PROTOCOL_FLAG_HTTP2
122 #define WINHTTP_PROTOCOL_FLAG_HTTP2 0x1
123 #endif
124 #ifndef WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL
125 #define WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL 133
126 #endif
127 #ifndef WINHTTP_DECOMPRESSION_FLAG_ALL
128 #define WINHTTP_DECOMPRESSION_FLAG_GZIP 0x00000001
129 #define WINHTTP_DECOMPRESSION_FLAG_DEFLATE 0x00000002
130 #define WINHTTP_DECOMPRESSION_FLAG_ALL ( \
131 WINHTTP_DECOMPRESSION_FLAG_GZIP | \
132 WINHTTP_DECOMPRESSION_FLAG_DEFLATE)
133 #endif
134 #ifndef WINHTTP_OPTION_DECOMPRESSION
135 #define WINHTTP_OPTION_DECOMPRESSION 118
136 #endif
137 #ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1
138 #define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 0x00000200
139 #endif
140 #ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
141 #define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800
142 #endif
143
144 // Helper functions
145
wxWinHTTPQueryHeaderString(HINTERNET hRequest,DWORD dwInfoLevel,LPCWSTR pwszName=WINHTTP_HEADER_NAME_BY_INDEX)146 static wxString wxWinHTTPQueryHeaderString(HINTERNET hRequest, DWORD dwInfoLevel,
147 LPCWSTR pwszName = WINHTTP_HEADER_NAME_BY_INDEX)
148 {
149 wxString result;
150 DWORD bufferLen = 0;
151 wxWinHTTP::WinHttpQueryHeaders(hRequest, dwInfoLevel, pwszName, NULL, &bufferLen,
152 WINHTTP_NO_HEADER_INDEX);
153 if ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
154 {
155 wxWCharBuffer resBuf(bufferLen);
156 if ( wxWinHTTP::WinHttpQueryHeaders(hRequest, dwInfoLevel, pwszName,
157 resBuf.data(), &bufferLen,
158 WINHTTP_NO_HEADER_INDEX) )
159 {
160 result.assign(resBuf);
161 }
162 }
163
164 return result;
165 }
166
wxWinHTTPQueryOptionString(HINTERNET hInternet,DWORD dwOption)167 static wxString wxWinHTTPQueryOptionString(HINTERNET hInternet, DWORD dwOption)
168 {
169 wxString result;
170 DWORD bufferLen = 0;
171 wxWinHTTP::WinHttpQueryOption(hInternet, dwOption, NULL, &bufferLen);
172 if ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
173 {
174 wxWCharBuffer resBuf(bufferLen);
175 if ( wxWinHTTP::WinHttpQueryOption(hInternet, dwOption, resBuf.data(), &bufferLen) )
176 result.assign(resBuf);
177 }
178
179 return result;
180 }
181
182 static inline
wxWinHTTPSetOption(HINTERNET hInternet,DWORD dwOption,DWORD dwValue)183 void wxWinHTTPSetOption(HINTERNET hInternet, DWORD dwOption, DWORD dwValue)
184 {
185 wxWinHTTP::WinHttpSetOption(hInternet, dwOption, &dwValue, sizeof(dwValue));
186 }
187
188 static
wxWinHTTPCloseHandle(HINTERNET hInternet)189 void wxWinHTTPCloseHandle(HINTERNET hInternet)
190 {
191 if ( !wxWinHTTP::WinHttpCloseHandle(hInternet) )
192 {
193 wxLogLastError("WinHttpCloseHandle");
194 }
195 }
196
wxRequestStatusCallback(HINTERNET WXUNUSED (hInternet),DWORD_PTR dwContext,DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength)197 static void CALLBACK wxRequestStatusCallback(
198 HINTERNET WXUNUSED(hInternet),
199 DWORD_PTR dwContext,
200 DWORD dwInternetStatus,
201 LPVOID lpvStatusInformation,
202 DWORD dwStatusInformationLength
203 )
204 {
205 if ( dwContext )
206 {
207 wxWebRequestWinHTTP* request =
208 reinterpret_cast<wxWebRequestWinHTTP*>(dwContext);
209 request->HandleCallback(dwInternetStatus, lpvStatusInformation,
210 dwStatusInformationLength);
211 }
212 }
213
214 //
215 // wxWebRequestWinHTTP
216 //
217
wxWebRequestWinHTTP(wxWebSession & session,wxWebSessionWinHTTP & sessionImpl,wxEvtHandler * handler,const wxString & url,int id)218 wxWebRequestWinHTTP::wxWebRequestWinHTTP(wxWebSession& session,
219 wxWebSessionWinHTTP& sessionImpl,
220 wxEvtHandler* handler,
221 const wxString& url,
222 int id):
223 wxWebRequestImpl(session, sessionImpl, handler, id),
224 m_sessionImpl(sessionImpl),
225 m_url(url),
226 m_connect(NULL),
227 m_request(NULL),
228 m_dataWritten(0)
229 {
230 }
231
~wxWebRequestWinHTTP()232 wxWebRequestWinHTTP::~wxWebRequestWinHTTP()
233 {
234 if ( m_request )
235 wxWinHTTPCloseHandle(m_request);
236 if ( m_connect )
237 wxWinHTTPCloseHandle(m_connect);
238 }
239
240 void
HandleCallback(DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength)241 wxWebRequestWinHTTP::HandleCallback(DWORD dwInternetStatus,
242 LPVOID lpvStatusInformation,
243 DWORD dwStatusInformationLength)
244 {
245 wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: callback %08x, len=%lu",
246 this, dwInternetStatus, dwStatusInformationLength);
247
248 switch ( dwInternetStatus )
249 {
250 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
251 if ( m_dataSize )
252 WriteData();
253 else
254 CreateResponse();
255 break;
256
257 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
258 if ( dwStatusInformationLength > 0 )
259 {
260 if ( !m_response->ReportAvailableData(dwStatusInformationLength)
261 && !WasCancelled() )
262 SetFailedWithLastError("Reading data");
263 }
264 else
265 {
266 SetFinalStateFromStatus();
267 }
268 break;
269
270 case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
271 {
272 DWORD written = *(reinterpret_cast<LPDWORD>(lpvStatusInformation));
273 m_dataWritten += written;
274 WriteData();
275 break;
276 }
277
278 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
279 {
280 LPWINHTTP_ASYNC_RESULT
281 asyncResult = reinterpret_cast<LPWINHTTP_ASYNC_RESULT>(lpvStatusInformation);
282
283 // "Failing" with "cancelled" error is not actually an error if
284 // we're expecting it, i.e. if our Cancel() had been called.
285 if ( asyncResult->dwError == ERROR_WINHTTP_OPERATION_CANCELLED &&
286 WasCancelled() )
287 SetState(wxWebRequest::State_Cancelled);
288 else
289 SetFailed("Async request", asyncResult->dwError);
290 break;
291 }
292 }
293 }
294
WriteData()295 void wxWebRequestWinHTTP::WriteData()
296 {
297 wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: writing data", this);
298
299 int dataWriteSize = wxWEBREQUEST_BUFFER_SIZE;
300 if ( m_dataWritten + dataWriteSize > m_dataSize )
301 dataWriteSize = m_dataSize - m_dataWritten;
302 if ( !dataWriteSize )
303 {
304 CreateResponse();
305 return;
306 }
307
308 m_dataWriteBuffer.Clear();
309 void* buffer = m_dataWriteBuffer.GetWriteBuf(dataWriteSize);
310 m_dataStream->Read(buffer, dataWriteSize);
311
312 if ( !wxWinHTTP::WinHttpWriteData
313 (
314 m_request,
315 m_dataWriteBuffer.GetData(),
316 dataWriteSize,
317 NULL // [out] bytes written, must be null in async mode
318 ) )
319 {
320 SetFailedWithLastError("Writing data");
321 }
322 }
323
CreateResponse()324 void wxWebRequestWinHTTP::CreateResponse()
325 {
326 wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: creating response", this);
327
328 if ( !wxWinHTTP::WinHttpReceiveResponse(m_request, NULL) )
329 {
330 SetFailedWithLastError("Receiving response");
331 return;
332 }
333
334 m_response.reset(new wxWebResponseWinHTTP(*this));
335 // wxWebResponseWinHTTP ctor could have changed the state if its
336 // initialization failed, so check for this.
337 if ( GetState() == wxWebRequest::State_Failed )
338 return;
339
340 int status = m_response->GetStatus();
341 if ( status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ )
342 {
343 m_authChallenge.reset(new wxWebAuthChallengeWinHTTP
344 (
345 status == HTTP_STATUS_PROXY_AUTH_REQ
346 ? wxWebAuthChallenge::Source_Proxy
347 : wxWebAuthChallenge::Source_Server,
348 *this
349 ));
350
351 if ( m_authChallenge->Init() )
352 SetState(wxWebRequest::State_Unauthorized, m_response->GetStatusText());
353 else
354 SetFailedWithLastError("Initializing authentication challenge");
355 }
356 else
357 {
358 // Start reading the response
359 if ( !m_response->ReadData() )
360 SetFailedWithLastError("Reading response data");
361 }
362 }
363
SetFailed(const wxString & operation,DWORD errorCode)364 void wxWebRequestWinHTTP::SetFailed(const wxString& operation, DWORD errorCode)
365 {
366 wxString failMessage = wxMSWFormatMessage(errorCode,
367 GetModuleHandle(TEXT("WINHTTP")));
368 SetState(wxWebRequest::State_Failed,
369 wxString::Format("%s failed with error %08x (%s)",
370 operation, errorCode, failMessage));
371 }
372
Start()373 void wxWebRequestWinHTTP::Start()
374 {
375 wxString method;
376 if ( !m_method.empty() )
377 method = m_method;
378 else if ( m_dataSize )
379 method = "POST";
380 else
381 method = "GET";
382
383 wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: start \"%s %s\"",
384 this, method, m_url);
385
386 // Parse the URL
387 URL_COMPONENTS urlComps;
388 wxZeroMemory(urlComps);
389 urlComps.dwStructSize = sizeof(urlComps);
390 urlComps.dwSchemeLength =
391 urlComps.dwHostNameLength =
392 urlComps.dwUrlPathLength =
393 urlComps.dwExtraInfoLength = (DWORD)-1;
394
395 if ( !wxWinHTTP::WinHttpCrackUrl(m_url.wc_str(), m_url.length(), 0, &urlComps) )
396 {
397 SetFailedWithLastError("Parsing URL");
398 return;
399 }
400
401 // Open a connection
402 m_connect = wxWinHTTP::WinHttpConnect
403 (
404 m_sessionImpl.GetHandle(),
405 wxString(urlComps.lpszHostName, urlComps.dwHostNameLength).wc_str(),
406 urlComps.nPort,
407 wxRESERVED_PARAM
408 );
409 if ( m_connect == NULL )
410 {
411 SetFailedWithLastError("Connecting");
412 return;
413 }
414
415 wxString objectName(urlComps.lpszUrlPath, urlComps.dwUrlPathLength);
416 if ( urlComps.dwExtraInfoLength )
417 objectName += wxString(urlComps.lpszExtraInfo, urlComps.dwExtraInfoLength);
418
419 // Open a request
420 static const wchar_t* acceptedTypes[] = { L"*/*", NULL };
421 m_request = wxWinHTTP::WinHttpOpenRequest
422 (
423 m_connect,
424 method.wc_str(), objectName.wc_str(),
425 NULL, // protocol version: use default, i.e. HTTP/1.1
426 WINHTTP_NO_REFERER,
427 acceptedTypes,
428 urlComps.nScheme == INTERNET_SCHEME_HTTPS
429 ? WINHTTP_FLAG_SECURE
430 : 0
431 );
432 if ( m_request == NULL )
433 {
434 SetFailedWithLastError("Opening request");
435 return;
436 }
437
438 // Register callback
439 if ( wxWinHTTP::WinHttpSetStatusCallback
440 (
441 m_request,
442 wxRequestStatusCallback,
443 WINHTTP_CALLBACK_FLAG_READ_COMPLETE |
444 WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE |
445 WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE |
446 WINHTTP_CALLBACK_FLAG_REQUEST_ERROR,
447 wxRESERVED_PARAM
448 ) == WINHTTP_INVALID_STATUS_CALLBACK )
449 {
450 SetFailedWithLastError("Setting up callbacks");
451 return;
452 }
453
454 if ( IsPeerVerifyDisabled() )
455 {
456 wxWinHTTPSetOption(m_request, WINHTTP_OPTION_SECURITY_FLAGS,
457 SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
458 SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
459 SECURITY_FLAG_IGNORE_UNKNOWN_CA |
460 SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE
461 );
462 }
463
464 SendRequest();
465 }
466
SendRequest()467 void wxWebRequestWinHTTP::SendRequest()
468 {
469 // Combine all headers to a string
470 wxString allHeaders;
471 for ( wxWebRequestHeaderMap::const_iterator header = m_headers.begin();
472 header != m_headers.end();
473 ++header )
474 {
475 allHeaders.append(wxString::Format("%s: %s\n", header->first, header->second));
476 }
477
478 if ( m_dataSize )
479 m_dataWritten = 0;
480
481 SetState(wxWebRequest::State_Active);
482
483 // Send request
484 if ( !wxWinHTTP::WinHttpSendRequest
485 (
486 m_request,
487 allHeaders.wc_str(), allHeaders.length(),
488 NULL, 0, // No extra optional data right now
489 m_dataSize,
490 (DWORD_PTR)this
491 ) )
492 {
493 SetFailedWithLastError("Sending request");
494 return;
495 }
496 }
497
DoCancel()498 void wxWebRequestWinHTTP::DoCancel()
499 {
500 wxWinHTTPCloseHandle(m_request);
501 m_request = NULL;
502 }
503
504 //
505 // wxWebResponseWinHTTP
506 //
507
wxWebResponseWinHTTP(wxWebRequestWinHTTP & request)508 wxWebResponseWinHTTP::wxWebResponseWinHTTP(wxWebRequestWinHTTP& request):
509 wxWebResponseImpl(request),
510 m_requestHandle(request.GetHandle())
511 {
512 const wxString contentLengthStr =
513 wxWinHTTPQueryHeaderString(m_requestHandle, WINHTTP_QUERY_CONTENT_LENGTH);
514 if ( contentLengthStr.empty() ||
515 !contentLengthStr.ToLongLong(&m_contentLength) )
516 m_contentLength = -1;
517
518 wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: receiving %llu bytes",
519 &request, m_contentLength);
520
521 Init();
522 }
523
GetURL() const524 wxString wxWebResponseWinHTTP::GetURL() const
525 {
526 return wxWinHTTPQueryOptionString(m_requestHandle, WINHTTP_OPTION_URL);
527 }
528
GetHeader(const wxString & name) const529 wxString wxWebResponseWinHTTP::GetHeader(const wxString& name) const
530 {
531 return wxWinHTTPQueryHeaderString(m_requestHandle, WINHTTP_QUERY_CUSTOM,
532 name.wc_str());
533 }
534
GetStatus() const535 int wxWebResponseWinHTTP::GetStatus() const
536 {
537 DWORD status = 0;
538 DWORD statusSize = sizeof(status);
539 if ( !wxWinHTTP::WinHttpQueryHeaders
540 (
541 m_requestHandle,
542 WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
543 WINHTTP_HEADER_NAME_BY_INDEX,
544 &status,
545 &statusSize,
546 0 // header index, unused with status code "header"
547 ) )
548 {
549 wxLogLastError("WinHttpQueryHeaders/status");
550 }
551
552 return status;
553 }
554
GetStatusText() const555 wxString wxWebResponseWinHTTP::GetStatusText() const
556 {
557 return wxWinHTTPQueryHeaderString(m_requestHandle, WINHTTP_QUERY_STATUS_TEXT);
558 }
559
ReadData()560 bool wxWebResponseWinHTTP::ReadData()
561 {
562 wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: reading data", &m_request);
563
564 return wxWinHTTP::WinHttpReadData
565 (
566 m_requestHandle,
567 GetDataBuffer(m_readSize),
568 m_readSize,
569 NULL // [out] bytes read, must be null in async mode
570 ) == TRUE;
571 }
572
ReportAvailableData(DWORD dataLen)573 bool wxWebResponseWinHTTP::ReportAvailableData(DWORD dataLen)
574 {
575 ReportDataReceived(dataLen);
576 return ReadData();
577 }
578
579 //
580 // wxWebAuthChallengeWinHTTP
581 //
wxWebAuthChallengeWinHTTP(wxWebAuthChallenge::Source source,wxWebRequestWinHTTP & request)582 wxWebAuthChallengeWinHTTP::wxWebAuthChallengeWinHTTP(wxWebAuthChallenge::Source source,
583 wxWebRequestWinHTTP & request):
584 wxWebAuthChallengeImpl(source),
585 m_request(request),
586 m_target(0),
587 m_selectedScheme(0)
588 {
589
590 }
591
Init()592 bool wxWebAuthChallengeWinHTTP::Init()
593 {
594 DWORD supportedSchemes;
595 DWORD firstScheme;
596
597 if ( !wxWinHTTP::WinHttpQueryAuthSchemes
598 (
599 m_request.GetHandle(),
600 &supportedSchemes,
601 &firstScheme,
602 &m_target
603 ) )
604 {
605 wxLogLastError("WinHttpQueryAuthSchemes");
606 return false;
607 }
608
609 if ( supportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE )
610 m_selectedScheme = WINHTTP_AUTH_SCHEME_NEGOTIATE;
611 else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_NTLM )
612 m_selectedScheme = WINHTTP_AUTH_SCHEME_NTLM;
613 else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT )
614 m_selectedScheme = WINHTTP_AUTH_SCHEME_PASSPORT;
615 else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST )
616 m_selectedScheme = WINHTTP_AUTH_SCHEME_DIGEST;
617 else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_BASIC )
618 m_selectedScheme = WINHTTP_AUTH_SCHEME_BASIC;
619 else
620 m_selectedScheme = 0;
621
622 return m_selectedScheme != 0;
623 }
624
625 void
SetCredentials(const wxWebCredentials & cred)626 wxWebAuthChallengeWinHTTP::SetCredentials(const wxWebCredentials& cred)
627 {
628 if ( !wxWinHTTP::WinHttpSetCredentials
629 (
630 m_request.GetHandle(),
631 m_target,
632 m_selectedScheme,
633 cred.GetUser().wc_str(),
634 wxSecretString(cred.GetPassword()).wc_str(),
635 wxRESERVED_PARAM
636 ) )
637 {
638 m_request.SetFailedWithLastError("Setting credentials");
639 return;
640 }
641
642 m_request.SendRequest();
643 }
644
645
646 //
647 // wxWebSessionWinHTTP
648 //
649
wxWebSessionWinHTTP()650 wxWebSessionWinHTTP::wxWebSessionWinHTTP():
651 m_handle(NULL)
652 {
653 }
654
~wxWebSessionWinHTTP()655 wxWebSessionWinHTTP::~wxWebSessionWinHTTP()
656 {
657 if ( m_handle )
658 wxWinHTTPCloseHandle(m_handle);
659 }
660
Initialize()661 bool wxWebSessionWinHTTP::Initialize()
662 {
663 return wxWinHTTP::LoadLibrary();
664 }
665
Open()666 bool wxWebSessionWinHTTP::Open()
667 {
668 DWORD accessType;
669 if ( wxCheckOsVersion(6, 3) )
670 accessType = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY;
671 else
672 accessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
673
674 m_handle = wxWinHTTP::WinHttpOpen
675 (
676 GetHeaders().find("User-Agent")->second.wc_str(),
677 accessType,
678 WINHTTP_NO_PROXY_NAME,
679 WINHTTP_NO_PROXY_BYPASS,
680 WINHTTP_FLAG_ASYNC
681 );
682 if ( !m_handle )
683 {
684 wxLogLastError("WinHttpOpen");
685 return false;
686 }
687
688 // Try to enable HTTP/2 (available since Win 10 1607)
689 wxWinHTTPSetOption(m_handle, WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL,
690 WINHTTP_PROTOCOL_FLAG_HTTP2);
691
692 // Try to enable GZIP and DEFLATE (available since Win 8.1)
693 wxWinHTTPSetOption(m_handle, WINHTTP_OPTION_DECOMPRESSION,
694 WINHTTP_DECOMPRESSION_FLAG_ALL);
695
696 // Try to enable modern TLS for older Windows versions
697 if ( !wxCheckOsVersion(6, 3) )
698 {
699 wxWinHTTPSetOption(m_handle, WINHTTP_OPTION_SECURE_PROTOCOLS,
700 WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 |
701 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
702 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
703 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2);
704 }
705
706 return true;
707 }
708
709 wxWebRequestImplPtr
CreateRequest(wxWebSession & session,wxEvtHandler * handler,const wxString & url,int id)710 wxWebSessionWinHTTP::CreateRequest(wxWebSession& session,
711 wxEvtHandler* handler,
712 const wxString& url,
713 int id)
714 {
715 if ( !m_handle )
716 {
717 if ( !Open() )
718 return wxWebRequestImplPtr();
719 }
720
721 return wxWebRequestImplPtr(
722 new wxWebRequestWinHTTP(session, *this, handler, url, id));
723 }
724
GetLibraryVersionInfo()725 wxVersionInfo wxWebSessionWinHTTP::GetLibraryVersionInfo()
726 {
727 int verMaj, verMin, verMicro;
728 wxGetOsVersion(&verMaj, &verMin, &verMicro);
729 return wxVersionInfo("WinHTTP", verMaj, verMin, verMicro);
730 }
731
732
733 #endif // wxUSE_WEBREQUEST_WINHTTP
734