/////////////////////////////////////////////////////////////////////////////// // Name: wx/private/webrequest.h // Purpose: wxWebRequest implementation classes // Author: Vadim Zeitlin // Created: 2020-12-26 // Copyright: (c) 2020 Vadim Zeitlin <vadim@wxwidgets.org> // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// #ifndef _WX_PRIVATE_WEBREQUEST_H_ #define _WX_PRIVATE_WEBREQUEST_H_ #include "wx/ffile.h" #include "wx/private/refcountermt.h" #include <memory> #include <unordered_map> class WXDLLIMPEXP_FWD_BASE wxURI; using wxWebRequestHeaderMap = std::unordered_map<wxString, wxString>; // Trace mask used for the messages in wxWebRequest code. #define wxTRACE_WEBREQUEST "webrequest" // ---------------------------------------------------------------------------- // wxWebAuthChallengeImpl // ---------------------------------------------------------------------------- class wxWebAuthChallengeImpl : public wxRefCounterMT { public: virtual ~wxWebAuthChallengeImpl() = default; wxWebAuthChallenge::Source GetSource() const { return m_source; } virtual void SetCredentials(const wxWebCredentials& cred) = 0; protected: explicit wxWebAuthChallengeImpl(wxWebAuthChallenge::Source source) : m_source(source) { } private: const wxWebAuthChallenge::Source m_source; wxDECLARE_NO_COPY_CLASS(wxWebAuthChallengeImpl); }; // ---------------------------------------------------------------------------- // wxWebRequestImpl // ---------------------------------------------------------------------------- class wxWebRequestImpl : public wxRefCounterMT { public: using Result = wxWebRequest::Result; // Return true if this is an async request, false if it's synchronous. bool IsAsync() const { return m_session != nullptr; } virtual ~wxWebRequestImpl() = default; void SetHeader(const wxString& name, const wxString& value) { m_headers[name] = value; } void SetMethod(const wxString& method) { m_method = method; } void SetData(const wxString& text, const wxString& contentType, const wxMBConv& conv = wxConvUTF8); bool SetData(std::unique_ptr<wxInputStream> dataStream, const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset); void SetStorage(wxWebRequest::Storage storage) { m_storage = storage; } wxWebRequest::Storage GetStorage() const { return m_storage; } // This method is called to execute the request in a synchronous way. virtual Result Execute() = 0; // This method is called to start execution of an asynchronous request. // // Precondition for this method checked by caller: current state is idle. virtual void Start() = 0; // This method can be called to cancel execution of an asynchronous request. // // Precondition for this method checked by caller: not idle and not already // cancelled. void Cancel(); virtual wxWebResponseImplPtr GetResponse() const = 0; virtual wxWebAuthChallengeImplPtr GetAuthChallenge() const = 0; int GetId() const { return m_id; } // This one is only valid for async requests. wxWebSession& GetSession() const { return *m_session; } // This one can be always called. wxWebSessionImpl& GetSessionImpl() const { return *m_sessionImpl; } wxWebRequest::State GetState() const { return m_state; } virtual wxFileOffset GetBytesSent() const = 0; virtual wxFileOffset GetBytesExpectedToSend() const = 0; virtual wxFileOffset GetBytesReceived() const; virtual wxFileOffset GetBytesExpectedToReceive() const; virtual wxWebRequestHandle GetNativeHandle() const = 0; void MakeInsecure(int flags) { m_securityFlags = flags; } int GetSecurityFlags() const { return m_securityFlags; } void SetState(wxWebRequest::State state, const wxString& failMsg = wxString()); void ReportDataReceived(size_t sizeReceived); wxEvtHandler* GetHandler() const { return m_handler; } protected: wxString m_method; wxWebRequest::Storage m_storage = wxWebRequest::Storage_Memory; wxWebRequestHeaderMap m_headers; wxFileOffset m_dataSize = 0; std::unique_ptr<wxInputStream> m_dataStream; int m_securityFlags = 0; // Ctor for async requests. wxWebRequestImpl(wxWebSession& session, wxWebSessionImpl& sessionImpl, wxEvtHandler* handler, int id); // Ctor for sync requests. explicit wxWebRequestImpl(wxWebSessionImpl& sessionImpl); bool WasCancelled() const { return m_cancelled; } // Get the HTTP method to use: this will be m_method if it's non-empty, // POST is we have any data to send, and GET otherwise. // // Returned string is always in upper case. wxString GetHTTPMethod() const; // Get wxWebRequest::State and, optionally, error message corresponding to // the given response (response must be valid here). static Result GetResultFromHTTPStatus(const wxWebResponseImplPtr& response); // Call SetState() with either State_Failed or State_Completed appropriate // for the response status. void SetFinalStateFromStatus() { HandleResult(GetResultFromHTTPStatus(GetResponse())); } // Unconditionally call SetState() with the parameters corresponding to the // given result. void HandleResult(const Result& result) { SetState(result.state, result.error); } // Call SetState() if the result is an error (State_Failed) and return // false in this case, otherwise just return true. bool CheckResult(const Result& result) { if ( !result ) { HandleResult(result); return false; } return true; } private: // Called from public Cancel() at most once per object. virtual void DoCancel() = 0; // Called to notify about the state change in the main thread by SetState() // (which can itself be called from a different one). // // It also releases a reference added when switching to the active state by // SetState() when leaving it. void ProcessStateEvent(wxWebRequest::State state, const wxString& failMsg); // This is a shared pointer and not just a reference to ensure that the // session stays alive as long as there are any requests using it, as // allowing it to die first would result in a crash when destroying the // request later. wxWebSessionImplPtr m_sessionImpl; // These parameters are only valid for async requests. wxWebSession* const m_session; wxEvtHandler* const m_handler; const int m_id; wxWebRequest::State m_state = wxWebRequest::State_Idle; wxFileOffset m_bytesReceived = 0; wxCharBuffer m_dataText; // Initially false, set to true after the first call to Cancel(). bool m_cancelled = false; wxDECLARE_NO_COPY_CLASS(wxWebRequestImpl); }; // ---------------------------------------------------------------------------- // wxWebResponseImpl // ---------------------------------------------------------------------------- class wxWebResponseImpl : public wxRefCounterMT { public: virtual ~wxWebResponseImpl(); virtual wxFileOffset GetContentLength() const = 0; virtual wxString GetURL() const = 0; virtual wxString GetHeader(const wxString& name) const = 0; virtual std::vector<wxString> GetAllHeaderValues(const wxString& name) const = 0; virtual wxString GetMimeType() const; virtual wxString GetContentType() const; virtual int GetStatus() const = 0; virtual wxString GetStatusText() const = 0; virtual wxInputStream* GetStream() const; virtual wxString GetSuggestedFileName() const; wxString AsString() const; virtual wxString GetDataFile() const; // Open data file if necessary, i.e. if using wxWebRequest::Storage_File. // // Returns result with State_Failed if the file is needed but couldn't be // opened. wxNODISCARD wxWebRequest::Result InitFileStorage(); void ReportDataReceived(size_t sizeReceived); protected: wxWebRequestImpl& m_request; explicit wxWebResponseImpl(wxWebRequestImpl& request); void* GetDataBuffer(size_t sizeNeeded); // This function can optionally be called to preallocate the read buffer, // if the total amount of data to be downloaded is known in advance. void PreAllocBuffer(size_t sizeNeeded); private: // Called by wxWebRequestImpl only. friend class wxWebRequestImpl; void Finalize(); wxMemoryBuffer m_readBuffer; mutable wxFFile m_file; mutable std::unique_ptr<wxInputStream> m_stream; wxDECLARE_NO_COPY_CLASS(wxWebResponseImpl); }; // ---------------------------------------------------------------------------- // wxWebSessionFactory // ---------------------------------------------------------------------------- class wxWebSessionFactory { public: virtual wxWebSessionImpl* Create() = 0; virtual wxWebSessionImpl* CreateSync() = 0; virtual bool Initialize() { return true; } virtual ~wxWebSessionFactory() = default; }; // ---------------------------------------------------------------------------- // wxWebSessionImpl // ---------------------------------------------------------------------------- class wxWebSessionImpl : public wxRefCounterMT { public: // This session class can be used either synchronously or asynchronously, // but the mode must be chosen at the time of the object creation and // cannot be changed later. enum class Mode { Async, Sync }; virtual ~wxWebSessionImpl(); // Only one of these functions is actually implemented in async/sync // session implementation classes respectively. This is ugly, but allows to // add support for sync requests/sessions without completely rewriting // wxWebRequest code. virtual wxWebRequestImplPtr CreateRequest(wxWebSession& session, wxEvtHandler* handler, const wxString& url, int id) = 0; virtual wxWebRequestImplPtr CreateRequestSync(wxWebSessionSync& session, const wxString& url) = 0; virtual wxVersionInfo GetLibraryVersionInfo() const = 0; bool SetBaseURL(const wxString& url); const wxURI* GetBaseURL() const; void AddCommonHeader(const wxString& name, const wxString& value) { m_headers[name] = value; } void SetTempDir(const wxString& dir) { m_tempDir = dir; } wxString GetTempDir() const; virtual bool SetProxy(const wxWebProxy& proxy) { m_proxy = proxy; return true; } const wxWebProxy& GetProxy() const { return m_proxy; } const wxWebRequestHeaderMap& GetHeaders() const { return m_headers; } virtual wxWebSessionHandle GetNativeHandle() const = 0; virtual bool EnablePersistentStorage(bool WXUNUSED(enable)) { return false; } protected: explicit wxWebSessionImpl(Mode mode); bool IsAsync() const { return m_mode == Mode::Async; } private: // Make it a friend to allow accessing our m_headers. friend class wxWebRequest; const Mode m_mode; std::unique_ptr<wxURI> m_baseURL; wxWebRequestHeaderMap m_headers; wxString m_tempDir; wxWebProxy m_proxy{wxWebProxy::Default()}; wxDECLARE_NO_COPY_CLASS(wxWebSessionImpl); }; #endif // _WX_PRIVATE_WEBREQUEST_H_