// Added to mitigate the effect of libcurl looking
// for the ALL_PROXY and http_proxy env variables
// and deciding to insert a Pragma: no-cache
// header! The only usage of this method at the
// time of this writing is in llhttpclient.cpp
// in the request() method, where this method
// is called with use_proxy = FALSE
void LLURLRequest::useProxy(bool use_proxy)
{
    static std::string env_proxy;

    if (use_proxy && env_proxy.empty())
    {
		char* env_proxy_str;
        LLScopedVolatileAPRPool scoped_pool;
        apr_status_t status = apr_env_get(&env_proxy_str, "ALL_PROXY", scoped_pool);
        if (status != APR_SUCCESS)
        {
			status = apr_env_get(&env_proxy_str, "http_proxy", scoped_pool);
        }
        if (status != APR_SUCCESS)
        {
            use_proxy = false;
        }
		else
		{
			// env_proxy_str is stored in the scoped_pool, so we have to make a copy.
			env_proxy = env_proxy_str;
		}
    }

	LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL;

	AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
	curlEasyRequest_w->setoptString(CURLOPT_PROXY, (use_proxy && !env_proxy.empty()) ? env_proxy : std::string(""));
}
void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)
{
	{
		try
		{
			mCurlEasyRequestStateMachinePtr = new AICurlEasyRequestStateMachine(false);
		}
		catch(AICurlNoEasyHandle const& error)
		{
			llwarns << "Failed to initialize LLXMLRPCTransaction: " << error.what() << llendl;
			setStatus(StatusOtherError, "No curl easy handle");
			return;
		}
		AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequestStateMachinePtr->mCurlEasyRequest);

		curlEasyRequest_w->setWriteCallback(&curlDownloadCallback, (void*)this);
		BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert");
		curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert);
		curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0);
		// Be a little impatient about establishing connections.
		curlEasyRequest_w->setopt(CURLOPT_CONNECTTIMEOUT, 40L);

		/* Setting the DNS cache timeout to -1 disables it completely.
		   This might help with bug #503 */
		curlEasyRequest_w->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1L);

		curlEasyRequest_w->addHeader("Content-Type: text/xml");

		if (useGzip)
		{
			curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
		}
		
		int requestTextSize;
		char* requestText = XMLRPC_REQUEST_ToXML(request, &requestTextSize);
		if (requestText)
		{
			curlEasyRequest_w->setPost(new AIXMLRPCData(requestText), requestTextSize);
		}
		else
		{
			setStatus(StatusOtherError);
		}

		curlEasyRequest_w->finalizeRequest(mURI);
	}
	if (mStatus == LLXMLRPCTransaction::StatusNotStarted)	// It could be LLXMLRPCTransaction::StatusOtherError.
	{
	  mCurlEasyRequestStateMachinePtr->run(boost::bind(&LLXMLRPCTransaction::Impl::curlEasyRequestCallback, this, _1));
	  setStatus(LLXMLRPCTransaction::StatusStarted);
	}
	else
	{
	  // This deletes the statemachine immediately.
	  mCurlEasyRequestStateMachinePtr->kill();
	  mCurlEasyRequestStateMachinePtr = NULL;
	}
}
void AICurlEasyRequestStateMachine::initialize_impl(void)
{
  {
	AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
	llassert(curlEasyRequest_w->is_finalized());	// Call finalizeRequest() before calling run().
	curlEasyRequest_w->send_handle_events_to(this);
  }
  mAdded = false;
  mTimedOut = false;
  mFinished = false;
  mHandled = false;
  set_state(AICurlEasyRequestStateMachine_addRequest);
}
void LLXMLRPCTransaction::Impl::curlEasyRequestCallback(bool success)
{
	llassert(mStatus == LLXMLRPCTransaction::StatusStarted || mStatus == LLXMLRPCTransaction::StatusDownloading);

	AICurlEasyRequestStateMachine* state_machine = mCurlEasyRequestStateMachinePtr;
	// We're done with the statemachine, one way or another.
	// Set mCurlEasyRequestStateMachinePtr to NULL so we won't call mCurlEasyRequestStateMachinePtr->running() in the destructor.
	// Note that the state machine auto-cleaning: it will be deleted by the main-thread after this function returns.
	mCurlEasyRequestStateMachinePtr = NULL;

	if (!success)
	{
		// AICurlEasyRequestStateMachine did abort.
		// This currently only happens when libcurl didn't finish before the timer expired.
		std::ostringstream msg;
		F32 timeout_value = gSavedSettings.getF32("CurlRequestTimeOut");
		msg << "Connection to " << mURI << " timed out (" << timeout_value << " s)!";
		if (timeout_value < 40)
		{
			msg << "\nTry increasing CurlRequestTimeOut in Debug Settings.";
		}
		setStatus(LLXMLRPCTransaction::StatusOtherError, msg.str());
		return;
	}

	AICurlEasyRequest_wat curlEasyRequest_w(*state_machine->mCurlEasyRequest);
	CURLcode result;
	curlEasyRequest_w->getResult(&result, &mTransferInfo);

	if (result != CURLE_OK)
	{
		setCurlStatus(result);
		llwarns << "LLXMLRPCTransaction CURL error "
				<< mCurlCode << ": " << curlEasyRequest_w->getErrorString() << llendl;
		llwarns << "LLXMLRPCTransaction request URI: "
				<< mURI << llendl;
			
		return;
	}
	
	setStatus(LLXMLRPCTransaction::StatusComplete);

	mResponse = XMLRPC_REQUEST_FromXML(
			mResponseText.data(), mResponseText.size(), NULL);

	bool		hasError = false;
	bool		hasFault = false;
	int			faultCode = 0;
	std::string	faultString;

	LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse));
	if (error.isValid())
	{
		hasError = true;
		faultCode = error["faultCode"].asInt();
		faultString = error["faultString"].asString();
	}
	else if (XMLRPC_ResponseIsFault(mResponse))
	{
		hasFault = true;
		faultCode = XMLRPC_GetResponseFaultCode(mResponse);
		faultString = XMLRPC_GetResponseFaultString(mResponse);
	}

	if (hasError || hasFault)
	{
		setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
		
		llwarns << "LLXMLRPCTransaction XMLRPC "
				<< (hasError ? "error " : "fault ")
				<< faultCode << ": "
				<< faultString << llendl;
		llwarns << "LLXMLRPCTransaction request URI: "
				<< mURI << llendl;
	}
}
void LLURLRequest::useProxy(const std::string &proxy)
{
	AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
    curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy);
}
void LLURLRequest::addHeader(const char* header)
{
	AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
	curlEasyRequest_w->addHeader(header);
}
Esempio n. 7
0
/**
	@brief does a blocking request on the url, returning the data or bad status.

	@param url URI to verb on.
	@param method the verb to hit the URI with.
	@param body the body of the call (if needed - for instance not used for GET and DELETE, but is for POST and PUT)
	@param headers HTTP headers to use for the request.
	@param timeout Curl timeout to use. Defaults to 5. Rationale:
	Without this timeout, blockingGet() calls have been observed to take
	up to 90 seconds to complete.  Users of blockingGet() already must 
	check the HTTP return code for validity, so this will not introduce
	new errors.  A 5 second timeout will succeed > 95% of the time (and 
	probably > 99% of the time) based on my statistics. JC

	@returns an LLSD map: {status: integer, body: map}
  */
static LLSD blocking_request(
	const std::string& url,
	LLURLRequest::ERequestAction method,
	const LLSD& body,
	const LLSD& headers = LLSD(),
	const F32 timeout = 5
)
{
	lldebugs << "blockingRequest of " << url << llendl;

	S32 http_status = 499;
	LLSD response = LLSD::emptyMap();

	try
	{
		AICurlEasyRequest easy_request(false);
		AICurlEasyRequest_wat curlEasyRequest_w(*easy_request);

		LLHTTPBuffer http_buffer;
		
		// * Set curl handle options
		curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout);	// seconds, see warning at top of function.
		curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer);

		// * Setup headers.
		if (headers.isMap())
		{
			LLSD::map_const_iterator iter = headers.beginMap();
			LLSD::map_const_iterator end  = headers.endMap();
			for (; iter != end; ++iter)
			{
				std::ostringstream header;
				header << iter->first << ": " << iter->second.asString() ;
				lldebugs << "header = " << header.str() << llendl;
				curlEasyRequest_w->addHeader(header.str().c_str());
			}
		}
		
		// Needs to stay alive until after the call to perform().
		std::ostringstream ostr;

		// * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
		if (method == LLURLRequest::HTTP_GET)
		{
			curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1);
		}
		else if (method == LLURLRequest::HTTP_POST)
		{
			//copied from PHP libs, correct?
			curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml");
			LLSDSerialize::toXML(body, ostr);
			curlEasyRequest_w->setPost(ostr.str().c_str(), ostr.str().length());
		}
		
		// * Do the action using curl, handle results
		curlEasyRequest_w->addHeader("Accept: application/llsd+xml");
		curlEasyRequest_w->finalizeRequest(url);

		S32 curl_success = curlEasyRequest_w->perform();
		curlEasyRequest_w->getinfo(CURLINFO_RESPONSE_CODE, &http_status);
		// if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
		if ( http_status != 404 && (http_status != 200 || curl_success != 0) )
		{
			// We expect 404s, don't spam for them.
			llwarns << "CURL REQ URL: " << url << llendl;
			llwarns << "CURL REQ METHOD TYPE: " << method << llendl;
			llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl;
			llwarns << "CURL REQ BODY: " << ostr.str() << llendl;
			llwarns << "CURL HTTP_STATUS: " << http_status << llendl;
			llwarns << "CURL ERROR: " << curlEasyRequest_w->getErrorString() << llendl;
			llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;
			response["body"] = http_buffer.asString();
		}
		else
		{
			response["body"] = http_buffer.asLLSD();
			lldebugs << "CURL response: " << http_buffer.asString() << llendl;
		}
	}
	catch(AICurlNoEasyHandle const& error)
	{
		response["body"] = error.what();
	}

	response["status"] = http_status;
	return response;
}