1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/webrequest.cpp
3 // Purpose:     wxWebRequest base class implementations
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 #if wxUSE_WEBREQUEST
14 
15 #include "wx/webrequest.h"
16 #include "wx/mstream.h"
17 #include "wx/module.h"
18 #include "wx/uri.h"
19 #include "wx/filefn.h"
20 #include "wx/filename.h"
21 #include "wx/stdpaths.h"
22 #include "wx/wfstream.h"
23 
24 #ifndef WX_PRECOMP
25     #include "wx/app.h"
26     #include "wx/translation.h"
27     #include "wx/utils.h"
28 #endif
29 
30 #include "wx/private/webrequest.h"
31 
32 #if wxUSE_WEBREQUEST_WINHTTP
33 #include "wx/msw/private/webrequest_winhttp.h"
34 #endif
35 #if wxUSE_WEBREQUEST_URLSESSION
36 #include "wx/osx/private/webrequest_urlsession.h"
37 #endif
38 #if wxUSE_WEBREQUEST_CURL
39 #include "wx/private/webrequest_curl.h"
40 #endif
41 
42 extern WXDLLIMPEXP_DATA_NET(const char) wxWebSessionBackendWinHTTP[] = "WinHTTP";
43 extern WXDLLIMPEXP_DATA_NET(const char) wxWebSessionBackendURLSession[] = "URLSession";
44 extern WXDLLIMPEXP_DATA_NET(const char) wxWebSessionBackendCURL[] = "CURL";
45 
46 wxDEFINE_EVENT(wxEVT_WEBREQUEST_STATE, wxWebRequestEvent);
47 wxDEFINE_EVENT(wxEVT_WEBREQUEST_DATA, wxWebRequestEvent);
48 
49 #ifdef __WXDEBUG__
50 static const wxStringCharType* wxNO_IMPL_MSG
51     = wxS("can't be used with an invalid/uninitialized object");
52 #endif
53 
54 #define wxCHECK_IMPL(rc) wxCHECK_MSG( m_impl, (rc), wxNO_IMPL_MSG )
55 #define wxCHECK_IMPL_VOID() wxCHECK_RET( m_impl, wxNO_IMPL_MSG )
56 
57 //
58 // wxWebRequestImpl
59 //
wxWebRequestImpl(wxWebSession & session,wxWebSessionImpl & sessionImpl,wxEvtHandler * handler,int id)60 wxWebRequestImpl::wxWebRequestImpl(wxWebSession& session,
61                                    wxWebSessionImpl& sessionImpl,
62                                    wxEvtHandler* handler,
63                                    int id)
64     : m_storage(wxWebRequest::Storage_Memory),
65       m_headers(sessionImpl.GetHeaders()),
66       m_dataSize(0),
67       m_peerVerifyDisabled(false),
68       m_session(session),
69       m_handler(handler),
70       m_id(id),
71       m_state(wxWebRequest::State_Idle),
72       m_bytesReceived(0),
73       m_cancelled(false)
74 {
75 }
76 
Cancel()77 void wxWebRequestImpl::Cancel()
78 {
79     if ( m_cancelled )
80     {
81         // Nothing to do, don't even assert -- it's ok to call Cancel()
82         // multiple times, but calling DoCancel() once is enough.
83         return;
84     }
85 
86     wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: cancelling", this);
87 
88     m_cancelled = true;
89     DoCancel();
90 }
91 
SetFinalStateFromStatus()92 void wxWebRequestImpl::SetFinalStateFromStatus()
93 {
94     const wxWebResponseImplPtr& resp = GetResponse();
95     if ( !resp || resp->GetStatus() >= 400 )
96     {
97         wxString err;
98         if ( resp )
99         {
100             err.Printf(_("Error: %s (%d)"),
101                        resp->GetStatusText(), resp->GetStatus());
102         }
103 
104         SetState(wxWebRequest::State_Failed, err);
105     }
106     else
107     {
108         SetState(wxWebRequest::State_Completed);
109     }
110 }
111 
SetData(const wxString & text,const wxString & contentType,const wxMBConv & conv)112 void wxWebRequestImpl::SetData(const wxString& text, const wxString& contentType, const wxMBConv& conv)
113 {
114     m_dataText = text.mb_str(conv);
115 
116     wxScopedPtr<wxInputStream>
117         stream(new wxMemoryInputStream(m_dataText, m_dataText.length()));
118     SetData(stream, contentType);
119 }
120 
121 bool
SetData(wxScopedPtr<wxInputStream> & dataStream,const wxString & contentType,wxFileOffset dataSize)122 wxWebRequestImpl::SetData(wxScopedPtr<wxInputStream>& dataStream,
123                           const wxString& contentType,
124                           wxFileOffset dataSize)
125 {
126     m_dataStream.reset(dataStream.release());
127 
128     if ( m_dataStream )
129     {
130         wxCHECK_MSG( m_dataStream->IsOk(), false, "can't use invalid stream" );
131 
132         if ( dataSize == wxInvalidOffset )
133         {
134             // Determine data size
135             m_dataSize = m_dataStream->SeekI(0, wxFromEnd);
136             if ( m_dataSize == wxInvalidOffset )
137                 return false;
138 
139             m_dataStream->SeekI(0);
140         }
141         else
142             m_dataSize = dataSize;
143     }
144     else
145         m_dataSize = 0;
146 
147     SetHeader("Content-Type", contentType);
148 
149     return true;
150 }
151 
GetBytesReceived() const152 wxFileOffset wxWebRequestImpl::GetBytesReceived() const
153 {
154     return m_bytesReceived;
155 }
156 
GetBytesExpectedToReceive() const157 wxFileOffset wxWebRequestImpl::GetBytesExpectedToReceive() const
158 {
159     if ( GetResponse() )
160         return GetResponse()->GetContentLength();
161     else
162         return -1;
163 }
164 
165 namespace
166 {
167 
168 // Functor used with CallAfter() below.
169 //
170 // TODO-C++11: Replace with a lambda.
171 struct StateEventProcessor
172 {
StateEventProcessor__anon21feeb9f0111::StateEventProcessor173     StateEventProcessor(wxWebRequestImpl& request,
174                         wxWebRequest::State state,
175                         const wxString& failMsg)
176         : m_request(request), m_state(state), m_failMsg(failMsg)
177     {
178     }
179 
operator ()__anon21feeb9f0111::StateEventProcessor180     void operator()()
181     {
182         m_request.ProcessStateEvent(m_state, m_failMsg);
183     }
184 
185     wxWebRequestImpl& m_request;
186     const wxWebRequest::State m_state;
187     const wxString m_failMsg;
188 };
189 
190 #if wxUSE_LOG_TRACE
191 
192 // Tiny helper to log states as strings rather than meaningless numbers.
StateName(wxWebRequest::State state)193 wxString StateName(wxWebRequest::State state)
194 {
195     switch ( state )
196     {
197         case wxWebRequest::State_Idle:            return wxS("IDLE");
198         case wxWebRequest::State_Unauthorized:    return wxS("UNAUTHORIZED");
199         case wxWebRequest::State_Active:          return wxS("ACTIVE");
200         case wxWebRequest::State_Completed:       return wxS("COMPLETED");
201         case wxWebRequest::State_Failed:          return wxS("FAILED");
202         case wxWebRequest::State_Cancelled:       return wxS("CANCELLED");
203     }
204 
205     return wxString::Format("invalid state %d", state);
206 }
207 
208 #endif // wxUSE_LOG_TRACE
209 
210 } // anonymous namespace
211 
SetState(wxWebRequest::State state,const wxString & failMsg)212 void wxWebRequestImpl::SetState(wxWebRequest::State state, const wxString & failMsg)
213 {
214     wxCHECK_RET( state != m_state, "shouldn't switch to the same state" );
215 
216     wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: state %s => %s",
217                this, StateName(m_state), StateName(state));
218 
219     if ( state == wxWebRequest::State_Active )
220     {
221         // The request is now in progress (maybe not for the first time if the
222         // old state was State_Unauthorized and not State_Idle), ensure that it
223         // stays alive until it terminates, even if wxWebRequest object itself
224         // is deleted.
225         //
226         // Note that matching DecRef() is done by ProcessStateEvent() later.
227         IncRef();
228     }
229 
230     m_state = state;
231 
232     // Trigger the event in the main thread except when switching to the active
233     // state because this always happens in the main thread anyhow and it's
234     // important to process it synchronously, before the request actually
235     // starts (this gives the possibility to modify the request using native
236     // functions, for example, as its GetNativeHandle() is already valid).
237     if ( state == wxWebRequest::State_Active )
238     {
239         wxASSERT( wxIsMainThread() );
240 
241         ProcessStateEvent(state, failMsg);
242     }
243     else
244     {
245         m_handler->CallAfter(StateEventProcessor(*this, state, failMsg));
246     }
247 }
248 
ReportDataReceived(size_t sizeReceived)249 void wxWebRequestImpl::ReportDataReceived(size_t sizeReceived)
250 {
251     m_bytesReceived += sizeReceived;
252 }
253 
254 // The SplitParamaters implementation is adapted to wxWidgets
255 // from Poco::Net::MessageHeader::splitParameters
256 
257 // This function is used in a unit test, so define it inside wxPrivate
258 // namespace and an anonymous one.
259 namespace wxPrivate
260 {
261 
262 WXDLLIMPEXP_NET wxString
SplitParameters(const wxString & s,wxWebRequestHeaderMap & parameters)263 SplitParameters(const wxString& s, wxWebRequestHeaderMap& parameters)
264 {
265     wxString value;
266     wxString::const_iterator it = s.begin();
267     wxString::const_iterator end = s.end();
268     while ( it != end && wxIsspace(*it) )
269         ++it;
270     while ( it != end && *it != ';' )
271         value += *it++;
272     value.Trim();
273     if ( it != end )
274         ++it;
275 
276     parameters.clear();
277     wxString pname;
278     wxString pvalue;
279     pname.reserve(32);
280     pvalue.reserve(64);
281     while ( it != end )
282     {
283         pname.clear();
284         pvalue.clear();
285         while ( it != end && wxIsspace(*it) )
286             ++it;
287         while ( it != end && *it != '=' && *it != ';' )
288             pname += *it++;
289         pname.Trim();
290         if ( it != end && *it != ';' )
291             ++it;
292         while ( it != end && wxIsspace(*it) )
293             ++it;
294         while ( it != end && *it != ';' )
295         {
296             if ( *it == '"' )
297             {
298                 ++it;
299                 while ( it != end && *it != '"' )
300                 {
301                     if ( *it == '\\' )
302                     {
303                         ++it;
304                         if ( it != end )
305                             pvalue += *it++;
306                     }
307                     else
308                         pvalue += *it++;
309                 }
310                 if ( it != end )
311                     ++it;
312             }
313             else if ( *it == '\\' )
314             {
315                 ++it;
316                 if ( it != end )
317                     pvalue += *it++;
318             }
319             else
320                 pvalue += *it++;
321         }
322         pvalue.Trim();
323         if ( !pname.empty() )
324             parameters[pname] = pvalue;
325         if ( it != end )
326             ++it;
327     }
328 
329     return value;
330 }
331 
332 } // namespace wxPrivate
333 
ProcessStateEvent(wxWebRequest::State state,const wxString & failMsg)334 void wxWebRequestImpl::ProcessStateEvent(wxWebRequest::State state, const wxString& failMsg)
335 {
336     wxString dataFile;
337 
338     const wxWebResponseImplPtr& response = GetResponse();
339 
340     wxWebRequestEvent evt(wxEVT_WEBREQUEST_STATE, GetId(), state,
341                           wxWebResponse(response), failMsg);
342 
343     bool release = false;
344     switch ( state )
345     {
346         case wxWebRequest::State_Idle:
347             wxFAIL_MSG("unexpected");
348             break;
349 
350         case wxWebRequest::State_Active:
351             break;
352 
353         case wxWebRequest::State_Unauthorized:
354             // This one is tricky: we might not be done with the request yet,
355             // but we don't know if this is the case or not, i.e. if the
356             // application will call wxWebAuthChallenge::SetCredentials() or
357             // not later. So we release it now, as we assume that it still
358             // keeps a reference to the original request if it intends to do it
359             // anyhow, i.e. this won't actually destroy the request object in
360             // this case.
361             release = true;
362             break;
363 
364         case wxWebRequest::State_Completed:
365             if ( m_storage == wxWebRequest::Storage_File )
366             {
367                 dataFile = response->GetDataFile();
368                 evt.SetDataFile(dataFile);
369             }
370             wxFALLTHROUGH;
371 
372         case wxWebRequest::State_Failed:
373         case wxWebRequest::State_Cancelled:
374             if ( response )
375                 response->Finalize();
376 
377             release = true;
378             break;
379     }
380 
381     m_handler->ProcessEvent(evt);
382 
383     // Remove temporary file if we're using one and if it still exists: it
384     // could have been deleted or moved away by the event handler.
385     if ( !dataFile.empty() && wxFileExists(dataFile) )
386         wxRemoveFile(dataFile);
387 
388     // This may destroy this object if it's not used from elsewhere any longer.
389     if ( release )
390         DecRef();
391 }
392 
393 //
394 // wxWebRequest
395 //
396 
wxWebRequest()397 wxWebRequest::wxWebRequest()
398 {
399 }
400 
wxWebRequest(const wxWebRequestImplPtr & impl)401 wxWebRequest::wxWebRequest(const wxWebRequestImplPtr& impl)
402     : m_impl(impl)
403 {
404 }
405 
wxWebRequest(const wxWebRequest & other)406 wxWebRequest::wxWebRequest(const wxWebRequest& other)
407     : m_impl(other.m_impl)
408 {
409 }
410 
operator =(const wxWebRequest & other)411 wxWebRequest& wxWebRequest::operator=(const wxWebRequest& other)
412 {
413     m_impl = other.m_impl;
414     return *this;
415 }
416 
~wxWebRequest()417 wxWebRequest::~wxWebRequest()
418 {
419 }
420 
SetHeader(const wxString & name,const wxString & value)421 void wxWebRequest::SetHeader(const wxString& name, const wxString& value)
422 {
423     wxCHECK_IMPL_VOID();
424 
425     m_impl->SetHeader(name, value);
426 }
427 
SetMethod(const wxString & method)428 void wxWebRequest::SetMethod(const wxString& method)
429 {
430     wxCHECK_IMPL_VOID();
431 
432     m_impl->SetMethod(method);
433 }
434 
SetData(const wxString & text,const wxString & contentType,const wxMBConv & conv)435 void wxWebRequest::SetData(const wxString& text, const wxString& contentType, const wxMBConv& conv)
436 {
437     wxCHECK_IMPL_VOID();
438 
439     m_impl->SetData(text, contentType, conv);
440 }
441 
442 bool
SetData(wxInputStream * dataStream,const wxString & contentType,wxFileOffset dataSize)443 wxWebRequest::SetData(wxInputStream* dataStream,
444                       const wxString& contentType,
445                       wxFileOffset dataSize)
446 {
447     // Ensure that the stream is destroyed even we return below.
448     wxScopedPtr<wxInputStream> streamPtr(dataStream);
449 
450     wxCHECK_IMPL( false );
451 
452     return m_impl->SetData(streamPtr, contentType, dataSize);
453 }
454 
SetStorage(Storage storage)455 void wxWebRequest::SetStorage(Storage storage)
456 {
457     wxCHECK_IMPL_VOID();
458 
459     m_impl->SetStorage(storage);
460 }
461 
GetStorage() const462 wxWebRequest::Storage wxWebRequest::GetStorage() const
463 {
464     wxCHECK_IMPL( Storage_None );
465 
466     return m_impl->GetStorage();
467 }
468 
Start()469 void wxWebRequest::Start()
470 {
471     wxCHECK_IMPL_VOID();
472 
473     wxCHECK_RET( m_impl->GetState() == wxWebRequest::State_Idle,
474                  "Completed requests can not be restarted" );
475 
476     m_impl->Start();
477 }
478 
Cancel()479 void wxWebRequest::Cancel()
480 {
481     wxCHECK_IMPL_VOID();
482 
483     wxCHECK_RET( m_impl->GetState() != wxWebRequest::State_Idle,
484                  "Not yet started requests can't be cancelled" );
485 
486     m_impl->Cancel();
487 }
488 
GetResponse() const489 wxWebResponse wxWebRequest::GetResponse() const
490 {
491     wxCHECK_IMPL( wxWebResponse() );
492 
493     return wxWebResponse(m_impl->GetResponse());
494 }
495 
GetAuthChallenge() const496 wxWebAuthChallenge wxWebRequest::GetAuthChallenge() const
497 {
498     wxCHECK_IMPL( wxWebAuthChallenge() );
499 
500     return wxWebAuthChallenge(m_impl->GetAuthChallenge());
501 }
502 
GetId() const503 int wxWebRequest::GetId() const
504 {
505     wxCHECK_IMPL( wxID_ANY );
506 
507     return m_impl->GetId();
508 }
509 
GetSession() const510 wxWebSession& wxWebRequest::GetSession() const
511 {
512     wxCHECK_IMPL( wxWebSession::GetDefault() );
513 
514     return m_impl->GetSession();
515 }
516 
GetState() const517 wxWebRequest::State wxWebRequest::GetState() const
518 {
519     wxCHECK_IMPL( State_Failed );
520 
521     return m_impl->GetState();
522 }
523 
GetBytesSent() const524 wxFileOffset wxWebRequest::GetBytesSent() const
525 {
526     wxCHECK_IMPL( wxInvalidOffset );
527 
528     return m_impl->GetBytesSent();
529 }
530 
GetBytesExpectedToSend() const531 wxFileOffset wxWebRequest::GetBytesExpectedToSend() const
532 {
533     wxCHECK_IMPL( wxInvalidOffset );
534 
535     return m_impl->GetBytesExpectedToSend();
536 }
537 
GetBytesReceived() const538 wxFileOffset wxWebRequest::GetBytesReceived() const
539 {
540     wxCHECK_IMPL( wxInvalidOffset );
541 
542     return m_impl->GetBytesReceived();
543 }
544 
GetBytesExpectedToReceive() const545 wxFileOffset wxWebRequest::GetBytesExpectedToReceive() const
546 {
547     wxCHECK_IMPL( wxInvalidOffset );
548 
549     return m_impl->GetBytesExpectedToReceive();
550 }
551 
GetNativeHandle() const552 wxWebRequestHandle wxWebRequest::GetNativeHandle() const
553 {
554     return m_impl ? m_impl->GetNativeHandle() : NULL;
555 }
556 
DisablePeerVerify(bool disable)557 void wxWebRequest::DisablePeerVerify(bool disable)
558 {
559     m_impl->DisablePeerVerify(disable);
560 }
561 
IsPeerVerifyDisabled() const562 bool wxWebRequest::IsPeerVerifyDisabled() const
563 {
564     return m_impl->IsPeerVerifyDisabled();
565 }
566 
567 
568 
569 
570 //
571 // wxWebAuthChallenge
572 //
573 
wxWebAuthChallenge()574 wxWebAuthChallenge::wxWebAuthChallenge()
575 {
576 }
577 
wxWebAuthChallenge(const wxWebAuthChallengeImplPtr & impl)578 wxWebAuthChallenge::wxWebAuthChallenge(const wxWebAuthChallengeImplPtr& impl)
579     : m_impl(impl)
580 {
581 }
582 
wxWebAuthChallenge(const wxWebAuthChallenge & other)583 wxWebAuthChallenge::wxWebAuthChallenge(const wxWebAuthChallenge& other)
584     : m_impl(other.m_impl)
585 {
586 }
587 
operator =(const wxWebAuthChallenge & other)588 wxWebAuthChallenge& wxWebAuthChallenge::operator=(const wxWebAuthChallenge& other)
589 {
590     m_impl = other.m_impl;
591     return *this;
592 }
593 
~wxWebAuthChallenge()594 wxWebAuthChallenge::~wxWebAuthChallenge()
595 {
596 }
597 
GetSource() const598 wxWebAuthChallenge::Source wxWebAuthChallenge::GetSource() const
599 {
600     wxCHECK_IMPL( Source_Server );
601 
602     return m_impl->GetSource();
603 }
604 
605 void
SetCredentials(const wxWebCredentials & cred)606 wxWebAuthChallenge::SetCredentials(const wxWebCredentials& cred)
607 {
608     wxCHECK_IMPL_VOID();
609 
610     m_impl->SetCredentials(cred);
611 }
612 
613 //
614 // wxWebResponseImpl
615 //
616 
wxWebResponseImpl(wxWebRequestImpl & request)617 wxWebResponseImpl::wxWebResponseImpl(wxWebRequestImpl& request) :
618     m_request(request),
619     m_readSize(wxWEBREQUEST_BUFFER_SIZE)
620 {
621 }
622 
~wxWebResponseImpl()623 wxWebResponseImpl::~wxWebResponseImpl()
624 {
625     if ( wxFileExists(m_file.GetName()) )
626         wxRemoveFile(m_file.GetName());
627 }
628 
Init()629 void wxWebResponseImpl::Init()
630 {
631     if ( m_request.GetStorage() == wxWebRequest::Storage_File )
632     {
633         wxFileName tmpPrefix;
634         tmpPrefix.AssignDir(m_request.GetSession().GetTempDir());
635         if ( GetContentLength() > 0 )
636         {
637             // Check available disk space
638             wxLongLong freeSpace;
639             if ( wxGetDiskSpace(tmpPrefix.GetFullPath(), NULL, &freeSpace) &&
640                 GetContentLength() > freeSpace )
641             {
642                 m_request.SetState(wxWebRequest::State_Failed, _("Not enough free disk space for download."));
643                 return;
644             }
645         }
646 
647         tmpPrefix.SetName("wxd");
648         wxFileName::CreateTempFileName(tmpPrefix.GetFullPath(), &m_file);
649     }
650 }
651 
GetMimeType() const652 wxString wxWebResponseImpl::GetMimeType() const
653 {
654     return GetHeader("Mime-Type");
655 }
656 
GetStream() const657 wxInputStream * wxWebResponseImpl::GetStream() const
658 {
659     if ( !m_stream.get() )
660     {
661         // Create stream
662         switch ( m_request.GetStorage() )
663         {
664             case wxWebRequest::Storage_Memory:
665                 m_stream.reset(new wxMemoryInputStream(m_readBuffer.GetData(), m_readBuffer.GetDataLen()));
666                 break;
667             case wxWebRequest::Storage_File:
668                 m_stream.reset(new wxFFileInputStream(m_file));
669                 m_stream->SeekI(0);
670                 break;
671             case wxWebRequest::Storage_None:
672                 // No stream available
673                 break;
674         }
675 
676     }
677 
678     return m_stream.get();
679 }
680 
GetSuggestedFileName() const681 wxString wxWebResponseImpl::GetSuggestedFileName() const
682 {
683     wxString suggestedFilename;
684 
685     // Try to determine from Content-Disposition header
686     wxString contentDisp = GetHeader("Content-Disposition");
687     wxWebRequestHeaderMap params;
688     const wxString disp = wxPrivate::SplitParameters(contentDisp, params);
689     if ( disp == "attachment" )
690     {
691         // Parse as filename to filter potential path names
692         wxFileName fn(params["filename"]);
693         suggestedFilename = fn.GetFullName();
694     }
695 
696     if ( suggestedFilename.empty() )
697     {
698         wxURI uri(GetURL());
699         if ( uri.HasPath() )
700         {
701             wxFileName fn(uri.GetPath());
702             suggestedFilename = fn.GetFullName();
703         }
704         else
705             suggestedFilename = uri.GetServer();
706     }
707 
708     return suggestedFilename;
709 }
710 
AsString() const711 wxString wxWebResponseImpl::AsString() const
712 {
713     if ( m_request.GetStorage() == wxWebRequest::Storage_Memory )
714     {
715         // TODO: try to determine encoding type from content-type header
716         size_t outLen = 0;
717         return wxConvWhateverWorks.cMB2WC((const char*)m_readBuffer.GetData(), m_readBuffer.GetDataLen(), &outLen);
718     }
719     else
720         return wxString();
721 }
722 
GetDataBuffer(size_t sizeNeeded)723 void* wxWebResponseImpl::GetDataBuffer(size_t sizeNeeded)
724 {
725     return m_readBuffer.GetAppendBuf(sizeNeeded);
726 }
727 
PreAllocBuffer(size_t sizeNeeded)728 void wxWebResponseImpl::PreAllocBuffer(size_t sizeNeeded)
729 {
730     m_readBuffer.SetBufSize(sizeNeeded);
731 }
732 
ReportDataReceived(size_t sizeReceived)733 void wxWebResponseImpl::ReportDataReceived(size_t sizeReceived)
734 {
735     m_readBuffer.UngetAppendBuf(sizeReceived);
736     m_request.ReportDataReceived(sizeReceived);
737 
738     switch ( m_request.GetStorage() )
739     {
740         case wxWebRequest::Storage_Memory:
741             // Nothing to do, just keep appending data to the buffer.
742             break;
743 
744         case wxWebRequest::Storage_File:
745             m_file.Write(m_readBuffer.GetData(), m_readBuffer.GetDataLen());
746             m_readBuffer.Clear();
747             break;
748 
749         case wxWebRequest::Storage_None:
750             wxWebRequestEvent* const evt = new wxWebRequestEvent
751                                                (
752                                                 wxEVT_WEBREQUEST_DATA,
753                                                 m_request.GetId(),
754                                                 wxWebRequest::State_Active
755                                                );
756             evt->SetDataBuffer(m_readBuffer);
757 
758             m_request.GetHandler()->QueueEvent(evt);
759 
760             // Make sure we switch to a different buffer instead of just
761             // clearing the current one, which will be needed by the event
762             // handler when it's finally called in the main thread.
763             m_readBuffer = wxMemoryBuffer();
764             break;
765     }
766 }
767 
GetDataFile() const768 wxString wxWebResponseImpl::GetDataFile() const
769 {
770     return m_file.GetName();
771 }
772 
Finalize()773 void wxWebResponseImpl::Finalize()
774 {
775     if ( m_request.GetStorage() == wxWebRequest::Storage_File )
776         m_file.Close();
777 }
778 
779 //
780 // wxWebResponse
781 //
782 
wxWebResponse()783 wxWebResponse::wxWebResponse()
784 {
785 }
786 
wxWebResponse(const wxWebResponseImplPtr & impl)787 wxWebResponse::wxWebResponse(const wxWebResponseImplPtr& impl)
788     : m_impl(impl)
789 {
790 }
791 
wxWebResponse(const wxWebResponse & other)792 wxWebResponse::wxWebResponse(const wxWebResponse& other)
793     : m_impl(other.m_impl)
794 {
795 }
796 
operator =(const wxWebResponse & other)797 wxWebResponse& wxWebResponse::operator=(const wxWebResponse& other)
798 {
799     m_impl = other.m_impl;
800     return *this;
801 }
802 
~wxWebResponse()803 wxWebResponse::~wxWebResponse()
804 {
805 }
806 
GetContentLength() const807 wxFileOffset wxWebResponse::GetContentLength() const
808 {
809     wxCHECK_IMPL( -1 );
810 
811     return m_impl->GetContentLength();
812 }
813 
GetURL() const814 wxString wxWebResponse::GetURL() const
815 {
816     wxCHECK_IMPL( wxString() );
817 
818     return m_impl->GetURL();
819 }
820 
GetHeader(const wxString & name) const821 wxString wxWebResponse::GetHeader(const wxString& name) const
822 {
823     wxCHECK_IMPL( wxString() );
824 
825     return m_impl->GetHeader(name);
826 }
827 
GetMimeType() const828 wxString wxWebResponse::GetMimeType() const
829 {
830     wxCHECK_IMPL( wxString() );
831 
832     return m_impl->GetMimeType();
833 }
834 
GetStatus() const835 int wxWebResponse::GetStatus() const
836 {
837     wxCHECK_IMPL( -1 );
838 
839     return m_impl->GetStatus();
840 }
841 
GetStatusText() const842 wxString wxWebResponse::GetStatusText() const
843 {
844     wxCHECK_IMPL( wxString() );
845 
846     return m_impl->GetStatusText();
847 }
848 
GetStream() const849 wxInputStream* wxWebResponse::GetStream() const
850 {
851     wxCHECK_IMPL( NULL );
852 
853     return m_impl->GetStream();
854 }
855 
GetSuggestedFileName() const856 wxString wxWebResponse::GetSuggestedFileName() const
857 {
858     wxCHECK_IMPL( wxString() );
859 
860     return m_impl->GetSuggestedFileName();
861 }
862 
AsString() const863 wxString wxWebResponse::AsString() const
864 {
865     wxCHECK_IMPL( wxString() );
866 
867     return m_impl->AsString();
868 }
869 
GetDataFile() const870 wxString wxWebResponse::GetDataFile() const
871 {
872     wxCHECK_IMPL( wxString() );
873 
874     return m_impl->GetDataFile();
875 }
876 
877 
878 //
879 // wxWebSessionImpl
880 //
881 
882 WX_DECLARE_STRING_HASH_MAP(wxWebSessionFactory*, wxStringWebSessionFactoryMap);
883 
884 namespace
885 {
886 
887 wxWebSession gs_defaultSession;
888 wxStringWebSessionFactoryMap gs_factoryMap;
889 
890 } // anonymous namespace
891 
wxWebSessionImpl()892 wxWebSessionImpl::wxWebSessionImpl()
893 {
894     // Initialize the user-Agent header with a reasonable default
895     AddCommonHeader("User-Agent", wxString::Format("%s/1 wxWidgets/%d.%d.%d",
896         wxTheApp->GetAppName(),
897         wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER));
898 }
899 
GetTempDir() const900 wxString wxWebSessionImpl::GetTempDir() const
901 {
902     if ( m_tempDir.empty() )
903         return wxStandardPaths::Get().GetTempDir();
904     else
905         return m_tempDir;
906 }
907 
908 //
909 // wxWebSession
910 //
911 
wxWebSession()912 wxWebSession::wxWebSession()
913 {
914 }
915 
wxWebSession(const wxWebSessionImplPtr & impl)916 wxWebSession::wxWebSession(const wxWebSessionImplPtr& impl)
917     : m_impl(impl)
918 {
919 }
920 
wxWebSession(const wxWebSession & other)921 wxWebSession::wxWebSession(const wxWebSession& other)
922     : m_impl(other.m_impl)
923 {
924 }
925 
operator =(const wxWebSession & other)926 wxWebSession& wxWebSession::operator=(const wxWebSession& other)
927 {
928     m_impl = other.m_impl;
929     return *this;
930 }
931 
~wxWebSession()932 wxWebSession::~wxWebSession()
933 {
934 }
935 
936 // static
GetDefault()937 wxWebSession& wxWebSession::GetDefault()
938 {
939     if ( !gs_defaultSession.IsOpened() )
940         gs_defaultSession = wxWebSession::New();
941 
942     return gs_defaultSession;
943 }
944 
945 // static
New(const wxString & backendOrig)946 wxWebSession wxWebSession::New(const wxString& backendOrig)
947 {
948     if ( gs_factoryMap.empty() )
949         InitFactoryMap();
950 
951     wxString backend = backendOrig;
952     if ( backend.empty() )
953     {
954         if ( !wxGetEnv("WXWEBREQUEST_BACKEND", &backend) )
955         {
956 #if wxUSE_WEBREQUEST_WINHTTP
957             backend = wxWebSessionBackendWinHTTP;
958 #elif wxUSE_WEBREQUEST_URLSESSION
959             backend = wxWebSessionBackendURLSession;
960 #elif wxUSE_WEBREQUEST_CURL
961             backend = wxWebSessionBackendCURL;
962 #endif
963         }
964     }
965 
966     wxStringWebSessionFactoryMap::iterator factory = gs_factoryMap.find(backend);
967 
968     wxWebSessionImplPtr impl;
969     if ( factory != gs_factoryMap.end() )
970         impl = factory->second->Create();
971 
972     return wxWebSession(impl);
973 }
974 
975 // static
976 void
RegisterFactory(const wxString & backend,wxWebSessionFactory * factory)977 wxWebSession::RegisterFactory(const wxString& backend,
978                               wxWebSessionFactory* factory)
979 {
980     if ( !factory->Initialize() )
981     {
982         delete factory;
983         factory = NULL;
984         return;
985     }
986 
987     // Note that we don't have to check here that there is no registered
988     // backend with the same name yet because we're only called from
989     // InitFactoryMap() below. If this function becomes public, we'd need to
990     // free the previous pointer stored for this backend first here.
991     gs_factoryMap[backend] = factory;
992 }
993 
994 // static
InitFactoryMap()995 void wxWebSession::InitFactoryMap()
996 {
997 #if wxUSE_WEBREQUEST_WINHTTP
998     RegisterFactory(wxWebSessionBackendWinHTTP, new wxWebSessionFactoryWinHTTP());
999 #endif
1000 #if wxUSE_WEBREQUEST_URLSESSION
1001     RegisterFactory(wxWebSessionBackendURLSession, new wxWebSessionFactoryURLSession());
1002 #endif
1003 #if wxUSE_WEBREQUEST_CURL
1004     RegisterFactory(wxWebSessionBackendCURL, new wxWebSessionFactoryCURL());
1005 #endif
1006 }
1007 
1008 // static
IsBackendAvailable(const wxString & backend)1009 bool wxWebSession::IsBackendAvailable(const wxString& backend)
1010 {
1011     if ( gs_factoryMap.empty() )
1012         InitFactoryMap();
1013 
1014     wxStringWebSessionFactoryMap::iterator factory = gs_factoryMap.find(backend);
1015     return factory != gs_factoryMap.end();
1016 }
1017 
1018 wxWebRequest
CreateRequest(wxEvtHandler * handler,const wxString & url,int id)1019 wxWebSession::CreateRequest(wxEvtHandler* handler, const wxString& url, int id)
1020 {
1021     wxCHECK_IMPL( wxWebRequest() );
1022 
1023     return wxWebRequest(m_impl->CreateRequest(*this, handler, url, id));
1024 }
1025 
GetLibraryVersionInfo()1026 wxVersionInfo wxWebSession::GetLibraryVersionInfo()
1027 {
1028     wxCHECK_IMPL( wxVersionInfo() );
1029 
1030     return m_impl->GetLibraryVersionInfo();
1031 }
1032 
AddCommonHeader(const wxString & name,const wxString & value)1033 void wxWebSession::AddCommonHeader(const wxString& name, const wxString& value)
1034 {
1035     wxCHECK_IMPL_VOID();
1036 
1037     m_impl->AddCommonHeader(name, value);
1038 }
1039 
SetTempDir(const wxString & dir)1040 void wxWebSession::SetTempDir(const wxString& dir)
1041 {
1042     wxCHECK_IMPL_VOID();
1043 
1044     m_impl->SetTempDir(dir);
1045 }
1046 
GetTempDir() const1047 wxString wxWebSession::GetTempDir() const
1048 {
1049     wxCHECK_IMPL( wxString() );
1050 
1051     return m_impl->GetTempDir();
1052 }
1053 
IsOpened() const1054 bool wxWebSession::IsOpened() const
1055 {
1056     return m_impl.get() != NULL;
1057 }
1058 
Close()1059 void wxWebSession::Close()
1060 {
1061     m_impl.reset(NULL);
1062 }
1063 
GetNativeHandle() const1064 wxWebSessionHandle wxWebSession::GetNativeHandle() const
1065 {
1066     return m_impl ? m_impl->GetNativeHandle() : NULL;
1067 }
1068 
1069 // ----------------------------------------------------------------------------
1070 // Module ensuring all global/singleton objects are destroyed on shutdown.
1071 // ----------------------------------------------------------------------------
1072 
1073 class WebRequestModule : public wxModule
1074 {
1075 public:
WebRequestModule()1076     WebRequestModule()
1077     {
1078     }
1079 
OnInit()1080     virtual bool OnInit() wxOVERRIDE
1081     {
1082         return true;
1083     }
1084 
OnExit()1085     virtual void OnExit() wxOVERRIDE
1086     {
1087         for ( wxStringWebSessionFactoryMap::iterator it = gs_factoryMap.begin();
1088               it != gs_factoryMap.end();
1089               ++it )
1090         {
1091             delete it->second;
1092         }
1093 
1094         gs_factoryMap.clear();
1095         gs_defaultSession.Close();
1096     }
1097 
1098 private:
1099     wxDECLARE_DYNAMIC_CLASS(WebRequestModule);
1100 };
1101 
1102 wxIMPLEMENT_DYNAMIC_CLASS(WebRequestModule, wxModule);
1103 
1104 #endif // wxUSE_WEBREQUEST
1105