PageRenderTime 95ms CodeModel.GetById 14ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llmessage/llurlrequest.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 784 lines | 586 code | 92 blank | 106 comment | 61 complexity | 1fcc1a8de3a442043466cd3e5ae3e7fe MD5 | raw file
  1/** 
  2 * @file llurlrequest.cpp
  3 * @author Phoenix
  4 * @date 2005-04-28
  5 * @brief Implementation of the URLRequest class and related classes.
  6 *
  7 * $LicenseInfo:firstyear=2005&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#include "linden_common.h"
 30#include "llurlrequest.h"
 31
 32#include <algorithm>
 33#include <openssl/x509_vfy.h>
 34#include <openssl/ssl.h>
 35#include "llcurl.h"
 36#include "llioutil.h"
 37#include "llmemtype.h"
 38#include "llproxy.h"
 39#include "llpumpio.h"
 40#include "llsd.h"
 41#include "llstring.h"
 42#include "apr_env.h"
 43#include "llapr.h"
 44static const U32 HTTP_STATUS_PIPE_ERROR = 499;
 45
 46/**
 47 * String constants
 48 */
 49const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
 50const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");
 51
 52
 53static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
 54
 55
 56
 57/**
 58 * class LLURLRequestDetail
 59 */
 60class LLURLRequestDetail
 61{
 62public:
 63	LLURLRequestDetail();
 64	~LLURLRequestDetail();
 65	std::string mURL;
 66	LLCurlEasyRequest* mCurlRequest;
 67	LLIOPipe::buffer_ptr_t mResponseBuffer;
 68	LLChannelDescriptors mChannels;
 69	U8* mLastRead;
 70	U32 mBodyLimit;
 71	S32 mByteAccumulator;
 72	bool mIsBodyLimitSet;
 73	LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback;
 74};
 75
 76LLURLRequestDetail::LLURLRequestDetail() :
 77	mCurlRequest(NULL),
 78	mLastRead(NULL),
 79	mBodyLimit(0),
 80	mByteAccumulator(0),
 81	mIsBodyLimitSet(false),
 82    mSSLVerifyCallback(NULL)
 83{
 84	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 85	mCurlRequest = new LLCurlEasyRequest();
 86	
 87	if(!mCurlRequest->isValid()) //failed.
 88	{
 89		delete mCurlRequest ;
 90		mCurlRequest = NULL ;
 91	}
 92}
 93
 94LLURLRequestDetail::~LLURLRequestDetail()
 95{
 96	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 97	delete mCurlRequest;
 98	mLastRead = NULL;
 99}
100
101void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param)
102{
103	mDetail->mSSLVerifyCallback = callback;
104	mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this);
105	mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true);
106	mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2);	
107}
108
109
110// _sslCtxFunction
111// Callback function called when an SSL Context is created via CURL
112// used to configure the context for custom cert validation
113
114CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param)
115{	
116	LLURLRequest *req = (LLURLRequest *)param;
117	if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL)
118	{
119		SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL);
120		return CURLE_OK;
121	}
122	SSL_CTX * ctx = (SSL_CTX *) sslctx;
123	// disable any default verification for server certs
124	SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
125	// set the verification callback.
126	SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req);
127	// the calls are void
128	return CURLE_OK;
129	
130}
131
132/**
133 * class LLURLRequest
134 */
135
136// static
137std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action)
138{
139	static const std::string VERBS[] =
140	{
141		"(invalid)",
142		"HEAD",
143		"GET",
144		"PUT",
145		"POST",
146		"DELETE",
147		"MOVE"
148	};
149	if(((S32)action <=0) || ((S32)action >= REQUEST_ACTION_COUNT))
150	{
151		return VERBS[0];
152	}
153	return VERBS[action];
154}
155
156LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
157	mAction(action)
158{
159	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
160	initialize();
161}
162
163LLURLRequest::LLURLRequest(
164	LLURLRequest::ERequestAction action,
165	const std::string& url) :
166	mAction(action)
167{
168	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
169	initialize();
170	setURL(url);
171}
172
173LLURLRequest::~LLURLRequest()
174{
175	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
176	delete mDetail;
177	mDetail = NULL ;
178}
179
180void LLURLRequest::setURL(const std::string& url)
181{
182	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
183	mDetail->mURL = url;
184}
185
186std::string LLURLRequest::getURL() const
187{
188	return mDetail->mURL;
189}
190
191void LLURLRequest::addHeader(const char* header)
192{
193	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
194	mDetail->mCurlRequest->slist_append(header);
195}
196
197void LLURLRequest::setBodyLimit(U32 size)
198{
199	mDetail->mBodyLimit = size;
200	mDetail->mIsBodyLimitSet = true;
201}
202
203void LLURLRequest::setCallback(LLURLRequestComplete* callback)
204{
205	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
206	mCompletionCallback = callback;
207	mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback);
208}
209
210// Added to mitigate the effect of libcurl looking
211// for the ALL_PROXY and http_proxy env variables
212// and deciding to insert a Pragma: no-cache
213// header! The only usage of this method at the
214// time of this writing is in llhttpclient.cpp
215// in the request() method, where this method
216// is called with use_proxy = FALSE
217void LLURLRequest::useProxy(bool use_proxy)
218{
219    static char *env_proxy;
220
221    if (use_proxy && (env_proxy == NULL))
222    {
223        apr_status_t status;
224        LLAPRPool pool;
225		status = apr_env_get(&env_proxy, "ALL_PROXY", pool.getAPRPool());
226        if (status != APR_SUCCESS)
227        {
228			status = apr_env_get(&env_proxy, "http_proxy", pool.getAPRPool());
229        }
230        if (status != APR_SUCCESS)
231        {
232           use_proxy = FALSE;
233        }
234    }
235
236
237    lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << llendl;
238
239    if (env_proxy && use_proxy)
240    {
241		mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy);
242    }
243    else
244    {
245        mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, "");
246    }
247}
248
249void LLURLRequest::useProxy(const std::string &proxy)
250{
251    mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy);
252}
253
254void LLURLRequest::allowCookies()
255{
256	mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, "");
257}
258
259//virtual 
260bool LLURLRequest::isValid() 
261{
262	return mDetail->mCurlRequest && mDetail->mCurlRequest->isValid(); 
263}
264
265// virtual
266LLIOPipe::EStatus LLURLRequest::handleError(
267	LLIOPipe::EStatus status,
268	LLPumpIO* pump)
269{
270	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
271	
272	if(!isValid())
273	{
274		return STATUS_EXPIRED ;
275	}
276
277	if(mCompletionCallback && pump)
278	{
279		LLURLRequestComplete* complete = NULL;
280		complete = (LLURLRequestComplete*)mCompletionCallback.get();
281		complete->httpStatus(
282			HTTP_STATUS_PIPE_ERROR,
283			LLIOPipe::lookupStatusString(status));
284		complete->responseStatus(status);
285		pump->respond(complete);
286		mCompletionCallback = NULL;
287	}
288	return status;
289}
290
291static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request");
292
293// virtual
294LLIOPipe::EStatus LLURLRequest::process_impl(
295	const LLChannelDescriptors& channels,
296	buffer_ptr_t& buffer,
297	bool& eos,
298	LLSD& context,
299	LLPumpIO* pump)
300{
301	LLFastTimer t(FTM_PROCESS_URL_REQUEST);
302	PUMP_DEBUG;
303	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
304	//llinfos << "LLURLRequest::process_impl()" << llendl;
305	if (!buffer) return STATUS_ERROR;
306	
307	// we're still waiting or prcessing, check how many
308	// bytes we have accumulated.
309	const S32 MIN_ACCUMULATION = 100000;
310	if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION))
311	{
312		static LLFastTimer::DeclareTimer FTM_URL_ADJUST_TIMEOUT("Adjust Timeout");
313		LLFastTimer t(FTM_URL_ADJUST_TIMEOUT);
314		 // This is a pretty sloppy calculation, but this
315		 // tries to make the gross assumption that if data
316		 // is coming in at 56kb/s, then this transfer will
317		 // probably succeed. So, if we're accumlated
318		 // 100,000 bytes (MIN_ACCUMULATION) then let's
319		 // give this client another 2s to complete.
320		 const F32 TIMEOUT_ADJUSTMENT = 2.0f;
321		 mDetail->mByteAccumulator = 0;
322		 pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT);
323		 lldebugs << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << llendl;
324		 if (mState == STATE_INITIALIZED)
325		 {
326			  llinfos << "LLURLRequest adjustTimeoutSeconds called during upload" << llendl;
327		 }
328	}
329
330	switch(mState)
331	{
332	case STATE_INITIALIZED:
333	{
334		PUMP_DEBUG;
335		// We only need to wait for input if we are uploading
336		// something.
337		if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
338		{
339			// we're waiting to get all of the information
340			return STATUS_BREAK;
341		}
342
343		// *FIX: bit of a hack, but it should work. The configure and
344		// callback method expect this information to be ready.
345		mDetail->mResponseBuffer = buffer;
346		mDetail->mChannels = channels;
347		if(!configure())
348		{
349			return STATUS_ERROR;
350		}
351		mState = STATE_WAITING_FOR_RESPONSE;
352
353		// *FIX: Maybe we should just go to the next state now...
354		return STATUS_BREAK;
355	}
356	case STATE_WAITING_FOR_RESPONSE:
357	case STATE_PROCESSING_RESPONSE:
358	{
359		PUMP_DEBUG;
360		LLIOPipe::EStatus status = STATUS_BREAK;
361		static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform");
362		{
363			LLFastTimer t(FTM_URL_PERFORM);
364			if(!mDetail->mCurlRequest->wait())
365			{
366				return status ;
367			}
368		}
369
370		while(1)
371		{
372			CURLcode result;
373
374			static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result");
375
376			bool newmsg = false;
377			{
378				LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT);
379				newmsg = mDetail->mCurlRequest->getResult(&result);
380			}
381		
382			if(!newmsg)
383			{
384				// keep processing
385				break;
386			}
387		
388
389			mState = STATE_HAVE_RESPONSE;
390			context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
391			context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
392			lldebugs << this << "Setting context to " << context << llendl;
393			switch(result)
394			{
395				case CURLE_OK:
396				case CURLE_WRITE_ERROR:
397					// NB: The error indication means that we stopped the
398					// writing due the body limit being reached
399					if(mCompletionCallback && pump)
400					{
401						LLURLRequestComplete* complete = NULL;
402						complete = (LLURLRequestComplete*)
403							mCompletionCallback.get();
404						complete->responseStatus(
405								result == CURLE_OK
406									? STATUS_OK : STATUS_STOP);
407						LLPumpIO::links_t chain;
408						LLPumpIO::LLLinkInfo link;
409						link.mPipe = mCompletionCallback;
410						link.mChannels = LLBufferArray::makeChannelConsumer(
411							channels);
412						chain.push_back(link);
413						static LLFastTimer::DeclareTimer FTM_PROCESS_URL_PUMP_RESPOND("Pump Respond");
414						{
415							LLFastTimer t(FTM_PROCESS_URL_PUMP_RESPOND);
416							pump->respond(chain, buffer, context);
417						}
418						mCompletionCallback = NULL;
419					}
420					break;
421				case CURLE_FAILED_INIT:
422				case CURLE_COULDNT_CONNECT:
423					status = STATUS_NO_CONNECTION;
424					break;
425				default:
426					llwarns << "URLRequest Error: " << result
427							<< ", "
428							<< LLCurl::strerror(result)
429							<< ", "
430							<< (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL)
431							<< llendl;
432					status = STATUS_ERROR;
433					break;
434			}
435		}
436		return status;
437	}
438	case STATE_HAVE_RESPONSE:
439		PUMP_DEBUG;
440		// we already stuffed everything into channel in in the curl
441		// callback, so we are done.
442		eos = true;
443		context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
444		context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
445		lldebugs << this << "Setting context to " << context << llendl;
446		return STATUS_DONE;
447
448	default:
449		PUMP_DEBUG;
450		context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
451		context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
452		lldebugs << this << "Setting context to " << context << llendl;
453		return STATUS_ERROR;
454	}
455}
456
457void LLURLRequest::initialize()
458{
459	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
460	mState = STATE_INITIALIZED;
461	mDetail = new LLURLRequestDetail;
462
463	if(!isValid())
464	{
465		return ;
466	}
467
468	mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);
469	mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this);
470	mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this);
471	mRequestTransferedBytes = 0;
472	mResponseTransferedBytes = 0;
473}
474
475static LLFastTimer::DeclareTimer FTM_URL_REQUEST_CONFIGURE("URL Configure");
476bool LLURLRequest::configure()
477{
478	LLFastTimer t(FTM_URL_REQUEST_CONFIGURE);
479	
480	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
481	bool rv = false;
482	S32 bytes = mDetail->mResponseBuffer->countAfter(
483   		mDetail->mChannels.in(),
484		NULL);
485	switch(mAction)
486	{
487	case HTTP_HEAD:
488		mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1);
489		mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1);
490		mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
491		rv = true;
492		break;
493	case HTTP_GET:
494		mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1);
495		mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
496
497		// Set Accept-Encoding to allow response compression
498		mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
499		rv = true;
500		break;
501
502	case HTTP_PUT:
503		// Disable the expect http 1.1 extension. POST and PUT default
504		// to turning this on, and I am not too sure what it means.
505		addHeader("Expect:");
506
507		mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1);
508		mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes);
509		rv = true;
510		break;
511
512	case HTTP_POST:
513		// Disable the expect http 1.1 extension. POST and PUT default
514		// to turning this on, and I am not too sure what it means.
515		addHeader("Expect:");
516
517		// Disable the content type http header.
518		// *FIX: what should it be?
519		addHeader("Content-Type:");
520
521		// Set the handle for an http post
522		mDetail->mCurlRequest->setPost(NULL, bytes);
523
524		// Set Accept-Encoding to allow response compression
525		mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
526		rv = true;
527		break;
528
529	case HTTP_DELETE:
530		// Set the handle for an http post
531		mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
532		rv = true;
533		break;
534
535	case HTTP_MOVE:
536		// Set the handle for an http post
537		mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
538		// *NOTE: should we check for the Destination header?
539		rv = true;
540		break;
541
542	default:
543		llwarns << "Unhandled URLRequest action: " << mAction << llendl;
544		break;
545	}
546	if(rv)
547	{
548		mDetail->mCurlRequest->sendRequest(mDetail->mURL);
549	}
550	return rv;
551}
552
553// static
554size_t LLURLRequest::downCallback(
555	char* data,
556	size_t size,
557	size_t nmemb,
558	void* user)
559{
560	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
561	LLURLRequest* req = (LLURLRequest*)user;
562	if(STATE_WAITING_FOR_RESPONSE == req->mState)
563	{
564		req->mState = STATE_PROCESSING_RESPONSE;
565	}
566	U32 bytes = size * nmemb;
567	if (req->mDetail->mIsBodyLimitSet)
568	{
569		if (bytes > req->mDetail->mBodyLimit)
570		{
571			bytes = req->mDetail->mBodyLimit;
572			req->mDetail->mBodyLimit = 0;
573		}
574		else
575		{
576			req->mDetail->mBodyLimit -= bytes;
577		}
578	}
579
580	req->mDetail->mResponseBuffer->append(
581		req->mDetail->mChannels.out(),
582		(U8*)data,
583		bytes);
584	req->mResponseTransferedBytes += bytes;
585	req->mDetail->mByteAccumulator += bytes;
586	return bytes;
587}
588
589// static
590size_t LLURLRequest::upCallback(
591	char* data,
592	size_t size,
593	size_t nmemb,
594	void* user)
595{
596	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
597	LLURLRequest* req = (LLURLRequest*)user;
598	S32 bytes = llmin(
599		(S32)(size * nmemb),
600		req->mDetail->mResponseBuffer->countAfter(
601			req->mDetail->mChannels.in(),
602			req->mDetail->mLastRead));
603	req->mDetail->mLastRead =  req->mDetail->mResponseBuffer->readAfter(
604		req->mDetail->mChannels.in(),
605		req->mDetail->mLastRead,
606		(U8*)data,
607		bytes);
608	req->mRequestTransferedBytes += bytes;
609	return bytes;
610}
611
612static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
613{
614	const char* header_line = (const char*)data;
615	size_t header_len = size * nmemb;
616	LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
617
618	if (!complete || !header_line)
619	{
620		return header_len;
621	}
622
623	// *TODO: This should be a utility in llstring.h: isascii()
624	for (size_t i = 0; i < header_len; ++i)
625	{
626		if (header_line[i] < 0)
627		{
628			return header_len;
629		}
630	}
631
632	std::string header(header_line, header_len);
633
634	// Per HTTP spec the first header line must be the status line.
635	if (header.substr(0,5) == "HTTP/")
636	{
637		std::string::iterator end = header.end();
638		std::string::iterator pos1 = std::find(header.begin(), end, ' ');
639		if (pos1 != end) ++pos1;
640		std::string::iterator pos2 = std::find(pos1, end, ' ');
641		if (pos2 != end) ++pos2;
642		std::string::iterator pos3 = std::find(pos2, end, '\r');
643
644		std::string version(header.begin(), pos1);
645		std::string status(pos1, pos2);
646		std::string reason(pos2, pos3);
647
648		S32 status_code = atoi(status.c_str());
649		if (status_code > 0)
650		{
651			complete->httpStatus((U32)status_code, reason);
652			return header_len;
653		}
654	}
655
656	std::string::iterator sep = std::find(header.begin(),header.end(),':');
657
658	if (sep != header.end())
659	{
660		std::string key(header.begin(), sep);
661		std::string value(sep + 1, header.end());
662
663		key = utf8str_tolower(utf8str_trim(key));
664		value = utf8str_trim(value);
665
666		complete->header(key, value);
667	}
668	else
669	{
670		LLStringUtil::trim(header);
671		if (!header.empty())
672		{
673			llwarns << "Unable to parse header: " << header << llendl;
674		}
675	}
676
677	return header_len;
678}
679
680static LLFastTimer::DeclareTimer FTM_PROCESS_URL_EXTRACTOR("URL Extractor");
681/**
682 * LLContextURLExtractor
683 */
684// virtual
685LLIOPipe::EStatus LLContextURLExtractor::process_impl(
686	const LLChannelDescriptors& channels,
687	buffer_ptr_t& buffer,
688	bool& eos,
689	LLSD& context,
690	LLPumpIO* pump)
691{
692	LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR);
693	PUMP_DEBUG;
694	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
695	// The destination host is in the context.
696	if(context.isUndefined() || !mRequest)
697	{
698		return STATUS_PRECONDITION_NOT_MET;
699	}
700
701	// copy in to out, since this just extract the URL and does not
702	// actually change the data.
703	LLChangeChannel change(channels.in(), channels.out());
704	std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
705
706	// find the context url
707	if(context.has(CONTEXT_DEST_URI_SD_LABEL))
708	{
709		mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString());
710		return STATUS_DONE;
711	}
712	return STATUS_ERROR;
713}
714
715
716/**
717 * LLURLRequestComplete
718 */
719LLURLRequestComplete::LLURLRequestComplete() :
720	mRequestStatus(LLIOPipe::STATUS_ERROR)
721{
722	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
723}
724
725// virtual
726LLURLRequestComplete::~LLURLRequestComplete()
727{
728	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
729}
730
731//virtual 
732void LLURLRequestComplete::header(const std::string& header, const std::string& value)
733{
734}
735
736//virtual 
737void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
738		const buffer_ptr_t& buffer)
739{
740	if(STATUS_OK == mRequestStatus)
741	{
742		response(channels, buffer);
743	}
744	else
745	{
746		noResponse();
747	}
748}
749
750//virtual 
751void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
752		const buffer_ptr_t& buffer)
753{
754	llwarns << "LLURLRequestComplete::response default implementation called"
755		<< llendl;
756}
757
758//virtual 
759void LLURLRequestComplete::noResponse()
760{
761	llwarns << "LLURLRequestComplete::noResponse default implementation called"
762		<< llendl;
763}
764
765void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
766{
767	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
768	mRequestStatus = status;
769}
770
771static LLFastTimer::DeclareTimer FTM_PROCESS_URL_COMPLETE("URL Complete");
772// virtual
773LLIOPipe::EStatus LLURLRequestComplete::process_impl(
774	const LLChannelDescriptors& channels,
775	buffer_ptr_t& buffer,
776	bool& eos,
777	LLSD& context,
778	LLPumpIO* pump)
779{
780	LLFastTimer t(FTM_PROCESS_URL_COMPLETE);
781	PUMP_DEBUG;
782	complete(channels, buffer);
783	return STATUS_OK;
784}