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