1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        thread.h
3 // Purpose:     wxCurlDownloadThread, wxCurlUploadThread
4 // Author:      Francesco Montorsi
5 // Created:     2007/04/14
6 // RCS-ID:      $Id: thread.h 1237 2010-03-10 21:52:47Z frm $
7 // Copyright:   (c) 2007 Francesco Montorsi
8 // Licence:     wxWidgets licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #ifndef _WXCURL_THREAD_H_
12 #define _WXCURL_THREAD_H_
13 
14 // wxWidgets headers
15 #include "wx/defs.h"
16 #include "wx/thread.h"
17 
18 #include "wx/curl/base.h"
19 
20 #include <memory>
21 
22 //! One of the protocols supported by wxCurl.
23 enum wxCurlProtocol
24 {
25     wxCP_INVALID = -1,
26 
27     wxCP_HTTP,
28     wxCP_FTP
29 };
30 
31 //! One of the possible errors code returned by wxCurl.
32 enum wxCurlThreadError
33 {
34     wxCTE_NO_ERROR = wxTHREAD_NO_ERROR,          //!< There was no error.
35 
36     wxCTE_NO_RESOURCE = wxTHREAD_NO_RESOURCE,    //!< There were insufficient resources to create a new thread.
37     wxCTE_ALREADY_RUNNING = wxTHREAD_RUNNING,    //!< The thread is already running.
38     wxCTE_INVALID_PROTOCOL,                      //!< The given URL requires an unspported protocol.
39     wxCTE_NO_VALID_STREAM,                       //!< The input/output stream could not be created or is invalid.
40     wxCTE_ABORTED,                               //!< The thread was user-aborted through wxCurlBaseThread::Abort().
41     wxCTE_CURL_ERROR
42             //!< LibCURL failed. You can use thread->GetCurlSession()->GetErrorString() to get more info.
43 };
44 
45 //! The stack size for wxCurl threads.
46 #define wxCURL_THREAD_STACK_SIZE            1024
47 
48 
49 // ----------------------------------------------------------------------------
50 // wxCurlBaseThread
51 // ----------------------------------------------------------------------------
52 
53 //! Base class for wxCurl threads.
54 class WXDLLIMPEXP_CURL wxCurlBaseThread : public wxThread
55 {
56 protected:
57 
58     //! The URL identifying the resource to download/upload.
59     wxString m_url;
60 
61     //! The libcurl handle being used for the transfer.
62     std::shared_ptr<wxCurlBase> m_pCurl;
63 
64     //! The protocol being used for the transfer.
65     wxCurlProtocol m_protocol;
66 
67     //! The event handler which gets m_pCurl's events.
68     wxEvtHandler *m_pHandler;
69     int m_nId;
70 
71     //! This flag is set to true when the thread has been cancelled using Delete().
72     //! Since it's continuosly read by the running thread and maybe written from
73     //! other threads, it needs a mutex.
74     bool m_bAbort;
75     wxMutex m_bAbortMutex;
76 
77 public:
78     wxCurlBaseThread(wxEvtHandler *handler = NULL,
79                      int id = wxID_ANY)
wxThread(wxTHREAD_JOINABLE)80         : wxThread(wxTHREAD_JOINABLE)
81     {
82         m_protocol = wxCP_INVALID;
83         m_pCurl = 0;
84 
85         m_nId = id;
86         m_pHandler = handler;
87 
88         m_bAbort = false;
89     }
90 
~wxCurlBaseThread()91     ~wxCurlBaseThread()
92     {
93         m_pCurl = 0;
94     }
95 
96 public:     // thread execution management
97 
98     //! Returns true if this thread is ready to be started using e.g. #StartTransfer.
IsOk()99     virtual bool IsOk() const
100         { return !m_url.empty() && m_pCurl != 0; }
101 
102     //! Starts the transfer. This is equivalent to call wxCurlDownloadThread::Download or
103     //! wxCurlUploadThread::Upload.
104     virtual wxCurlThreadError StartTransfer() = 0;
105 
106     //! Aborts this thread.
107     virtual void Abort();
108 
109     //! Waits for the completion of the transfer.
110     virtual wxCurlThreadError Wait();
111 
112     //! Pauses the transfer.
113     virtual wxCurlThreadError Pause();
114 
115     //! Resumes the transfer.
116     virtual wxCurlThreadError Resume();
117 
118 
119 public:     // setters
120 
121     //! Sets the event handler to which wxCurlBeginPerformEvent, wxCurlEndPerformEvent
122     //! and wxCurlDownloadEvent/wxCurlUploadEvents will be posted.
123     void SetEvtHandler(wxEvtHandler *handler, int id = wxID_ANY)
124         {
125             wxCHECK_RET(!IsAlive(), wxS("Cannot use this function after the tranfer has begun"));
126             m_pHandler=handler; m_nId=id;
127         }
128 
129     //! Sets the URL to download/upload from/to.
130     wxCurlThreadError SetURL(const wxString &url);
131     wxCurlThreadError SetURL(const wxString &url, std::shared_ptr<wxCurlBase> pCurl);
132 
133 public:     // getters
134 
GetEvtHandler()135     wxEvtHandler *GetEvtHandler() const
136         { return m_pHandler; }
GetId()137     int GetId() const
138         { return m_nId; }
139 
IsAborting()140     bool IsAborting()
141         { return m_bAbort; }
142 
143 
144     //! Returns the wxCurlBase-derived object which is being used for the transfer.
145     //! Note that the returned value will be NULL if you've not called #SetURL yet.
146     //! You can cast it to the wxCurlBase-derived class associated with the return
147     //! value of GetProtocol() (e.g. if GetProtocol() returns wxCP_HTTP, you can cast
148     //! GetCurlSession() to wxCurlHTTP).
GetCurlSession()149     wxCurlBase *GetCurlSession() const
150         { return m_pCurl.get(); }
151 
GetCurlSharedPtr()152     std::shared_ptr<wxCurlBase> &GetCurlSharedPtr()
153         { return m_pCurl; }
154 
155     //! Returns the protocol used for the transfer.
156     //! This function will return something different from wxCP_INVALID only after
157     //! a call to #SetURL with a valid URL.
GetProtocol()158     wxCurlProtocol GetProtocol() const
159         { return m_protocol; }
160 
161     //! Returns the URL of current transfer.
GetURL()162     wxString GetURL() const
163         { return m_url; }
164 
165 public:     // public utils
166 
167     //! Returns the protocol which should be used to download the resource
168     //! associated with the given URL.
169     static wxCurlProtocol GetProtocolFromURL(const wxString &url);
170 
171     //! Returns a pointer to a wxCurlBase-derived class suitable for handling
172     //! transfers on the given protocol.
173     //! You'll need to wx_static_cast the return value to the
174     //! right class in order to be able to set/get further options
175     //! (e.g. url/username/password/proxy/etc etc).
176     static std::shared_ptr<wxCurlBase>CreateHandlerFor(wxCurlProtocol prot);
177 
178 protected:
179 
180     virtual bool TestDestroy();
181     virtual void OnExit();
182 
183 
184     // change the access type of some wxThread functions which should not
185     // be used on wxCurlBaseThread-derived classes.
186 
Create(unsigned int stackSize)187     virtual wxCurlThreadError Create(unsigned int stackSize)
188         { return (wxCurlThreadError)wxThread::Create(stackSize); }
Run()189     virtual wxCurlThreadError Run()
190         { return (wxCurlThreadError)wxThread::Run(); }
Delete()191     virtual wxCurlThreadError Delete()
192         { return (wxCurlThreadError)wxThread::Delete(); }
193 };
194 
195 
196 // ----------------------------------------------------------------------------
197 // wxCurlDownloadThreadOutputFilter
198 // ----------------------------------------------------------------------------
199 
200 class WXDLLIMPEXP_CURL wxCurlDownloadThread;
201 
202 // private class used by wxCurlDownloadThread
203 class wxCurlDownloadThreadOutputFilter : public wxOutputStream
204 {
205 protected:
206     wxCurlDownloadThread *m_thread;
207     wxOutputStream *m_stream;
208 
209 public:
wxCurlDownloadThreadOutputFilter(wxCurlDownloadThread * thread)210     wxCurlDownloadThreadOutputFilter(wxCurlDownloadThread *thread)
211         { m_thread = thread; m_stream = NULL; }
212 
SetStream(wxOutputStream * realStream)213     void SetStream(wxOutputStream *realStream)
214         { m_stream = realStream; }
215 
216     virtual size_t OnSysWrite(const void *buffer, size_t bufsize);
217 
IsOk()218     virtual bool IsOk() const
219         { return m_thread && m_stream && m_stream->IsOk(); }
220 
GetRealStream()221     wxOutputStream *GetRealStream() const
222         { return m_stream; }
223 
GetLength()224     wxFileOffset GetLength() const
225         { return m_stream->GetLength(); }
226 
Close()227     bool Close()
228         { return m_stream->Close(); }
229 };
230 
231 
232 // ----------------------------------------------------------------------------
233 // wxCurlDownloadThread
234 // ----------------------------------------------------------------------------
235 
236 //! A simple joinable thread which allows downloading
237 //! resources from the net without blocking the GUI of your app.
238 class WXDLLIMPEXP_CURL wxCurlDownloadThread : public wxCurlBaseThread
239 {
240     friend class wxCurlDownloadThreadOutputFilter;      // needs to access our TestDestroy()
241 
242 protected:
243 
244     //! The output stream for downloaded data.
245     wxCurlDownloadThreadOutputFilter m_output;
246 
247 public:
248     wxCurlDownloadThread(wxEvtHandler *handler = NULL,
249                          int id = wxID_ANY,
250                          const wxString &url = wxEmptyString,
251                          wxOutputStream *out = NULL)
wxCurlBaseThread(handler,id)252         : wxCurlBaseThread(handler, id),
253           m_output(this)
254     {
255         if (!url.IsEmpty())
256             Download(url, out);
257     }
258 
259 public:     // public API
260 
261     //! Sets the output stream where the downloaded data are written.
262     //! If you pass NULL to this function, then a new temporary file will be used.
263     wxCurlThreadError SetOutputStream(wxOutputStream *out = NULL);
264 
265     //! Returns the output stream for downloaded data.
GetOutputStream()266     wxOutputStream *GetOutputStream() const
267         {
268             wxCHECK_MSG(!IsRunning(), NULL,
269                         wxS("You cannot access the output stream while the thread is running!"));
270             return m_output.GetRealStream();
271         }
272 
273     //! Returns true if this thread is ready to be started using #Download.
IsOk()274     virtual bool IsOk() const
275         { return wxCurlBaseThread::IsOk() && m_output.IsOk(); }
276 
277     //! Creates and runs this thread for download of the given URL in the given
278     //! output stream (internally calls #SetURL and #SetOutputStream).
279     wxCurlThreadError Download(const wxString &url, wxOutputStream *out = NULL);
280 
281     //! Downloads the URL previously set with #SetURL using the output stream
282     //! previously set with #SetOutputStream.
283     wxCurlThreadError Download();
284 
285 protected:
286 
287     // change access policy to force the user of the better-readable Download() method:
StartTransfer()288     virtual wxCurlThreadError StartTransfer()
289         { return Download(); }
290 
291     virtual void *Entry();
292 };
293 
294 
295 // ----------------------------------------------------------------------------
296 // wxCurlUploadThreadInputFilter
297 // ----------------------------------------------------------------------------
298 
299 class WXDLLIMPEXP_CURL wxCurlUploadThread;
300 
301 // private class
302 class wxCurlUploadThreadInputFilter : public wxInputStream
303 {
304 protected:
305     wxCurlUploadThread *m_thread;
306     wxInputStream *m_stream;
307 
308 public:
wxCurlUploadThreadInputFilter(wxCurlUploadThread * thread)309     wxCurlUploadThreadInputFilter(wxCurlUploadThread *thread)
310         { m_thread = thread; m_stream = NULL; }
311 
SetStream(wxInputStream * realStream)312     void SetStream(wxInputStream *realStream)
313         { m_stream = realStream; }
314 
315     virtual size_t OnSysRead(void *buffer, size_t bufsize);
316 
IsOk()317     virtual bool IsOk() const
318         { return m_thread && m_stream && m_stream->IsOk(); }
319 
GetRealStream()320     wxInputStream *GetRealStream() const
321         { return m_stream; }
322 
GetLength()323     wxFileOffset GetLength() const
324         { return m_stream->GetLength(); }
325 
Peek()326     char Peek()
327         { return m_stream->Peek(); }
328 };
329 
330 
331 // ----------------------------------------------------------------------------
332 // wxCurlUploadThread
333 // ----------------------------------------------------------------------------
334 
335 //! A simple joinable thread which allows uploading
336 //! resources to the net without blocking the GUI of your app.
337 class WXDLLIMPEXP_CURL wxCurlUploadThread : public wxCurlBaseThread
338 {
339     friend class wxCurlUploadThreadInputFilter;      // needs to access our TestDestroy()
340 
341 protected:
342 
343     //! The input stream for uploaded data.
344     wxCurlUploadThreadInputFilter m_input;
345 
346 public:
347     wxCurlUploadThread(wxEvtHandler *handler = NULL,
348                          int id = wxID_ANY,
349                          const wxString &url = wxEmptyString,
350                          wxInputStream *in = NULL)
wxCurlBaseThread(handler,id)351         : wxCurlBaseThread(handler, id),
352           m_input(this)
353     {
354         if (!url.IsEmpty())
355             Upload(url, in);
356     }
357 
358 public:     // public API
359 
360     //! Sets the output stream where the downloaded data are written.
361     //! If you pass NULL to this function, then a new temporary file will be used.
362     wxCurlThreadError SetInputStream(wxInputStream *in = NULL);
363 
364     //! Returns the output stream for downloaded data.
GetInputStream()365     wxInputStream *GetInputStream() const
366         {
367             wxCHECK_MSG(!IsRunning(), NULL,
368                         wxS("You cannot access the output stream while the thread is running!"));
369             return m_input.GetRealStream();
370         }
371 
372     //! Returns true if this thread is ready to be started using #Upload.
IsOk()373     virtual bool IsOk() const
374         { return wxCurlBaseThread::IsOk() && m_input.IsOk(); }
375 
376     //! Creates and runs this thread for upload to the given URL of the given
377     //! input stream (internally calls #SetURL and #SetOutputStream).
378     wxCurlThreadError Upload(const wxString &url, wxInputStream *in = NULL);
379 
380     //! Uploads the URL previously set with #SetURL using the input stream
381     //! previously set with #SetInputStream.
382     wxCurlThreadError Upload();
383 
384 protected:
385 
386     // change access policy to force the user of the better-readable Upload() method:
StartTransfer()387     virtual wxCurlThreadError StartTransfer()
388         { return Upload(); }
389 
390     virtual void *Entry();
391 };
392 
393 
394 #endif // _WXCURL_THREAD_H_
395 
396