PageRenderTime 83ms CodeModel.GetById 60ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llcurl.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 456 lines | 279 code | 100 blank | 77 comment | 3 complexity | c77417dc6e9c9d1858fb092bfbcef16e MD5 | raw file
  1/** 
  2 * @file llcurl.h
  3 * @author Zero / Donovan
  4 * @date 2006-10-15
  5 * @brief A wrapper around libcurl.
  6 *
  7 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28 
 29#ifndef LL_LLCURL_H
 30#define LL_LLCURL_H
 31
 32#include "linden_common.h"
 33
 34#include <sstream>
 35#include <string>
 36#include <vector>
 37
 38#include <boost/intrusive_ptr.hpp>
 39#include <curl/curl.h> // TODO: remove dependency
 40
 41#include "llbuffer.h"
 42#include "lliopipe.h"
 43#include "llsd.h"
 44#include "llthread.h"
 45#include "llqueuedthread.h"
 46#include "llframetimer.h"
 47
 48class LLMutex;
 49class LLCurlThread;
 50
 51// For whatever reason, this is not typedef'd in curl.h
 52typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
 53
 54class LLCurl
 55{
 56	LOG_CLASS(LLCurl);
 57	
 58public:
 59	class Easy;
 60	class Multi;
 61
 62	struct TransferInfo
 63	{
 64		TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
 65		F64 mSizeDownload;
 66		F64 mTotalTime;
 67		F64 mSpeedDownload;
 68	};
 69	
 70	class Responder
 71	{
 72	//LOG_CLASS(Responder);
 73	public:
 74
 75		Responder();
 76		virtual ~Responder();
 77
 78		/**
 79		 * @brief return true if the status code indicates success.
 80		 */
 81		static bool isGoodStatus(U32 status)
 82		{
 83			return((200 <= status) && (status < 300));
 84		}
 85		
 86		virtual void errorWithContent(
 87			U32 status,
 88			const std::string& reason,
 89			const LLSD& content);
 90			//< called by completed() on bad status 
 91
 92		virtual void error(U32 status, const std::string& reason);
 93			//< called by default error(status, reason, content)
 94		
 95		virtual void result(const LLSD& content);
 96			//< called by completed for good status codes.
 97
 98		virtual void completedRaw(
 99			U32 status,
100			const std::string& reason,
101			const LLChannelDescriptors& channels,
102			const LLIOPipe::buffer_ptr_t& buffer);
103			/**< Override point for clients that may want to use this
104			   class when the response is some other format besides LLSD
105			*/
106
107		virtual void completed(
108			U32 status,
109			const std::string& reason,
110			const LLSD& content);
111			/**< The default implemetnation calls
112				either:
113				* result(), or
114				* error() 
115			*/
116			
117			// Override to handle parsing of the header only.  Note: this is the only place where the contents
118			// of the header can be parsed.  In the ::completed call above only the body is contained in the LLSD.
119			virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content);
120
121			// Used internally to set the url for debugging later.
122			void setURL(const std::string& url);
123
124			virtual bool followRedir() 
125			{
126				return false;
127			}
128
129	public: /* but not really -- don't touch this */
130		U32 mReferenceCount;
131
132	private:
133		std::string mURL;
134	};
135	typedef boost::intrusive_ptr<Responder>	ResponderPtr;
136
137
138	/**
139	 * @ brief Set certificate authority file used to verify HTTPS certs.
140	 */
141	static void setCAFile(const std::string& file);
142
143	/**
144	 * @ brief Set certificate authority path used to verify HTTPS certs.
145	 */
146	static void setCAPath(const std::string& path);
147
148	/**
149	 * @ brief Return human-readable string describing libcurl version.
150	 */
151	static std::string getVersionString();
152	
153	/**
154	 * @ brief Get certificate authority file used to verify HTTPS certs.
155	 */
156	static const std::string& getCAFile() { return sCAFile; }
157
158	/**
159	 * @ brief Get certificate authority path used to verify HTTPS certs.
160	 */
161	static const std::string& getCAPath() { return sCAPath; }
162
163	/**
164	 * @ brief Initialize LLCurl class
165	 */
166	static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false);
167
168	/**
169	 * @ brief Cleanup LLCurl class
170	 */
171	static void cleanupClass();
172
173	/**
174	 * @ brief curl error code -> string
175	 */
176	static std::string strerror(CURLcode errorcode);
177	
178	// For OpenSSL callbacks
179	static std::vector<LLMutex*> sSSLMutex;
180
181	// OpenSSL callbacks
182	static void ssl_locking_callback(int mode, int type, const char *file, int line);
183	static unsigned long ssl_thread_id(void);
184
185	static LLCurlThread* getCurlThread() { return sCurlThread ;}
186
187	static CURLM* newMultiHandle() ;
188	static CURLMcode deleteMultiHandle(CURLM* handle) ;
189	static CURL*  newEasyHandle() ;
190	static void   deleteEasyHandle(CURL* handle) ;
191
192private:
193	static std::string sCAPath;
194	static std::string sCAFile;
195	static const unsigned int MAX_REDIRECTS;
196	static LLCurlThread* sCurlThread;
197
198	static LLMutex* sHandleMutexp ;
199	static S32      sTotalHandles ;
200	static S32      sMaxHandles;
201public:
202	static bool     sNotQuitting;
203	static F32      sCurlRequestTimeOut;	
204};
205
206class LLCurl::Easy
207{
208	LOG_CLASS(Easy);
209
210private:
211	Easy();
212
213public:
214	static Easy* getEasy();
215	~Easy();
216
217	CURL* getCurlHandle() const { return mCurlEasyHandle; }
218
219	void setErrorBuffer();
220	void setCA();
221
222	void setopt(CURLoption option, S32 value);
223	// These assume the setter does not free value!
224	void setopt(CURLoption option, void* value);
225	void setopt(CURLoption option, char* value);
226	// Copies the string so that it is guaranteed to stick around
227	void setoptString(CURLoption option, const std::string& value);
228
229	void slist_append(const char* str);
230	void setHeaders();
231
232	U32 report(CURLcode);
233	void getTransferInfo(LLCurl::TransferInfo* info);
234
235	void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false);
236
237	const char* getErrorBuffer();
238
239	std::stringstream& getInput() { return mInput; }
240	std::stringstream& getHeaderOutput() { return mHeaderOutput; }
241	LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
242	const LLChannelDescriptors& getChannels() { return mChannels; }
243
244	void resetState();
245
246	static CURL* allocEasyHandle();
247	static void releaseEasyHandle(CURL* handle);
248
249private:
250	friend class LLCurl;
251	friend class LLCurl::Multi;
252
253	CURL*				mCurlEasyHandle;
254	struct curl_slist*	mHeaders;
255
256	std::stringstream	mRequest;
257	LLChannelDescriptors mChannels;
258	LLIOPipe::buffer_ptr_t mOutput;
259	std::stringstream	mInput;
260	std::stringstream	mHeaderOutput;
261	char				mErrorBuffer[CURL_ERROR_SIZE];
262
263	// Note: char*'s not strings since we pass pointers to curl
264	std::vector<char*>	mStrings;
265
266	LLCurl::ResponderPtr		mResponder;
267
268	static std::set<CURL*> sFreeHandles;
269	static std::set<CURL*> sActiveHandles;
270	static LLMutex*        sHandleMutexp ;
271};
272
273class LLCurl::Multi
274{
275	LOG_CLASS(Multi);
276
277	friend class LLCurlThread ;
278
279private:
280	~Multi();
281
282	void markDead() ;
283	bool doPerform();
284
285public:
286
287	typedef enum
288	{
289		STATE_READY=0,
290		STATE_PERFORMING=1,
291		STATE_COMPLETED=2
292	} ePerformState;
293
294	Multi(F32 idle_time_out = 0.f);	
295
296	LLCurl::Easy* allocEasy();
297	bool addEasy(LLCurl::Easy* easy);	
298	void removeEasy(LLCurl::Easy* easy);
299	
300	void lock() ;
301	void unlock() ;
302
303	void setState(ePerformState state) ;
304	ePerformState getState() ;
305	
306	bool isCompleted() ;
307	bool isValid() {return mCurlMultiHandle != NULL ;}
308	bool isDead() {return mDead;}
309
310	bool waitToComplete() ;
311
312	S32 process();
313	
314	CURLMsg* info_read(S32* msgs_in_queue);
315
316	S32 mQueued;
317	S32 mErrorCount;
318	
319private:
320	void easyFree(LLCurl::Easy*);
321	void cleanup() ;
322	
323	CURLM* mCurlMultiHandle;
324
325	typedef std::set<LLCurl::Easy*> easy_active_list_t;
326	easy_active_list_t mEasyActiveList;
327	typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t;
328	easy_active_map_t mEasyActiveMap;
329	typedef std::set<LLCurl::Easy*> easy_free_list_t;
330	easy_free_list_t mEasyFreeList;
331
332	LLQueuedThread::handle_t mHandle ;
333	ePerformState mState;
334
335	BOOL mDead ;
336	LLMutex* mMutexp ;
337	LLMutex* mDeletionMutexp ;
338	LLMutex* mEasyMutexp ;
339	LLFrameTimer mIdleTimer ;
340	F32 mIdleTimeOut;
341};
342
343class LLCurlThread : public LLQueuedThread
344{
345public:
346
347	class CurlRequest : public LLQueuedThread::QueuedRequest
348	{
349	protected:
350		virtual ~CurlRequest(); // use deleteRequest()
351		
352	public:
353		CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread);
354
355		/*virtual*/ bool processRequest();
356		/*virtual*/ void finishRequest(bool completed);
357
358	private:
359		// input
360		LLCurl::Multi* mMulti;
361		LLCurlThread*  mCurlThread;
362	};
363	friend class CurlRequest;
364
365public:
366	LLCurlThread(bool threaded = true) ;
367	virtual ~LLCurlThread() ;
368
369	S32 update(F32 max_time_ms);
370
371	void addMulti(LLCurl::Multi* multi) ;
372	void killMulti(LLCurl::Multi* multi) ;
373
374private:
375	bool doMultiPerform(LLCurl::Multi* multi) ;
376	void deleteMulti(LLCurl::Multi* multi) ;
377	void cleanupMulti(LLCurl::Multi* multi) ;
378} ;
379
380namespace boost
381{
382	void intrusive_ptr_add_ref(LLCurl::Responder* p);
383	void intrusive_ptr_release(LLCurl::Responder* p);
384};
385
386
387class LLCurlRequest
388{
389public:
390	typedef std::vector<std::string> headers_t;
391	
392	LLCurlRequest();
393	~LLCurlRequest();
394
395	void get(const std::string& url, LLCurl::ResponderPtr responder);
396	bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
397	bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
398	bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
399	
400	S32  process();
401	S32  getQueued();
402
403private:
404	void addMulti();
405	LLCurl::Easy* allocEasy();
406	bool addEasy(LLCurl::Easy* easy);
407	
408private:
409	typedef std::set<LLCurl::Multi*> curlmulti_set_t;
410	curlmulti_set_t mMultiSet;
411	LLCurl::Multi* mActiveMulti;
412	S32 mActiveRequestCount;
413	BOOL mProcessing;
414};
415
416class LLCurlEasyRequest
417{
418public:
419	LLCurlEasyRequest();
420	~LLCurlEasyRequest();
421	void setopt(CURLoption option, S32 value);
422	void setoptString(CURLoption option, const std::string& value);
423	void setPost(char* postdata, S32 size);
424	void setHeaderCallback(curl_header_callback callback, void* userdata);
425	void setWriteCallback(curl_write_callback callback, void* userdata);
426	void setReadCallback(curl_read_callback callback, void* userdata);
427	void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
428	void slist_append(const char* str);
429	void sendRequest(const std::string& url);
430	void requestComplete();
431	bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
432	std::string getErrorString();
433	bool isCompleted() {return mMulti->isCompleted() ;}
434	bool wait() { return mMulti->waitToComplete(); }
435	bool isValid() {return mMulti && mMulti->isValid(); }
436
437	LLCurl::Easy* getEasy() const { return mEasy; }
438
439private:
440	CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info);
441	
442private:
443	LLCurl::Multi* mMulti;
444	LLCurl::Easy* mEasy;
445	bool mRequestSent;
446	bool mResultReturned;
447};
448
449// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace.
450namespace LLCurlFF
451{
452	void check_easy_code(CURLcode code);
453	void check_multi_code(CURLMcode code);
454}
455
456#endif // LL_LLCURL_H