Most notably getMesh (the only one possibly using any significant bandwidth), but in general every type of requests that just have to happen anyway and in the order they are requested: they are just passed to the curl thread, but now the curl thread will queue them and hold back if the (general) service they use is loaded too heavily.
296 lines
8.8 KiB
C++
296 lines
8.8 KiB
C++
/**
|
|
* @file llurlrequest.cpp
|
|
* @author Phoenix
|
|
* @date 2005-04-28
|
|
* @brief Implementation of the URLRequest class and related classes.
|
|
*
|
|
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
#include "llurlrequest.h"
|
|
|
|
#ifdef CWDEBUG
|
|
#include <libcwd/buf2str.h>
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <openssl/x509_vfy.h>
|
|
#include <openssl/ssl.h>
|
|
#include "aicurleasyrequeststatemachine.h"
|
|
#include "llioutil.h"
|
|
#include "llpumpio.h"
|
|
#include "llsd.h"
|
|
#include "llstring.h"
|
|
#include "apr_env.h"
|
|
#include "llapr.h"
|
|
#include "llscopedvolatileaprpool.h"
|
|
#include "llfasttimer.h"
|
|
#include "message.h"
|
|
|
|
/**
|
|
* String constants
|
|
*/
|
|
const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");
|
|
|
|
/**
|
|
* class LLURLRequest
|
|
*/
|
|
|
|
// static
|
|
std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action)
|
|
{
|
|
static int const array_size = LLHTTPClient::REQUEST_ACTION_COUNT; // INVALID == 0
|
|
static char const* const VERBS[array_size] =
|
|
{
|
|
"(invalid)",
|
|
"HEAD",
|
|
"GET",
|
|
"PUT",
|
|
"POST",
|
|
"DELETE",
|
|
"MOVE"
|
|
};
|
|
return VERBS[action >= array_size ? LLHTTPClient::INVALID : action];
|
|
}
|
|
|
|
// This might throw AICurlNoEasyHandle.
|
|
LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string const& url, Injector* body,
|
|
LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool keepalive, bool is_auth, bool compression,
|
|
bool queue_if_too_much_bandwidth_usage) :
|
|
mAction(action), mURL(url), mKeepAlive(keepalive), mIsAuth(is_auth), mNoCompression(!compression),
|
|
mBody(body), mResponder(responder), mHeaders(headers), mResponderNameCache(responder ? responder->getName() : "<uninitialized>")
|
|
{
|
|
if (queue_if_too_much_bandwidth_usage)
|
|
{
|
|
AICurlEasyRequest_wat(*mCurlEasyRequest)->queue_if_too_much_bandwidth_usage();
|
|
}
|
|
}
|
|
|
|
void LLURLRequest::initialize_impl(void)
|
|
{
|
|
// If the header is "Pragma" with no value, the caller intends to
|
|
// force libcurl to drop the Pragma header it so gratuitously inserts.
|
|
// Before inserting the header, force libcurl to not use the proxy.
|
|
std::string pragma_value;
|
|
if (mHeaders.getValue("Pragma", pragma_value) && pragma_value.empty())
|
|
{
|
|
useProxy(false);
|
|
}
|
|
|
|
if (mAction == LLHTTPClient::HTTP_PUT || mAction == LLHTTPClient::HTTP_POST)
|
|
{
|
|
// If the Content-Type header was passed in we defer to the caller's wisdom,
|
|
// but if they did not specify a Content-Type, then ask the injector.
|
|
mHeaders.addHeader("Content-Type", mBody->contentType(), AIHTTPHeaders::keep_existing_header);
|
|
}
|
|
else if (mAction != LLHTTPClient::HTTP_HEAD)
|
|
{
|
|
// Check to see if we have already set Accept or not. If no one
|
|
// set it, set it to application/llsd+xml since that's what we
|
|
// almost always want.
|
|
mHeaders.addHeader("Accept", "application/llsd+xml", AIHTTPHeaders::keep_existing_header);
|
|
}
|
|
|
|
if (mAction == LLHTTPClient::HTTP_POST && gMessageSystem)
|
|
{
|
|
mHeaders.addHeader("X-SecondLife-UDP-Listen-Port", llformat("%d", gMessageSystem->mPort));
|
|
}
|
|
|
|
bool success = false;
|
|
try
|
|
{
|
|
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
|
|
easy_request_w->prepRequest(easy_request_w, mHeaders, mResponder);
|
|
|
|
if (mBody)
|
|
{
|
|
// This might throw AICurlNoBody.
|
|
mBodySize = mBody->get_body(easy_request_w->sChannels, easy_request_w->getInput());
|
|
}
|
|
|
|
success = configure(easy_request_w);
|
|
}
|
|
catch (AICurlNoBody const& error)
|
|
{
|
|
llwarns << "Injector::get_body() failed: " << error.what() << llendl;
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
// Continue to initialize base class.
|
|
AICurlEasyRequestStateMachine::initialize_impl();
|
|
}
|
|
else
|
|
{
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void LLURLRequest::addHeader(const char* header)
|
|
{
|
|
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
|
|
curlEasyRequest_w->addHeader(header);
|
|
}
|
|
|
|
// 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(""));
|
|
}
|
|
|
|
#ifdef AI_UNUSED
|
|
void LLURLRequest::useProxy(const std::string &proxy)
|
|
{
|
|
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
|
|
curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy);
|
|
}
|
|
#endif
|
|
|
|
bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w)
|
|
{
|
|
bool rv = false;
|
|
{
|
|
switch(mAction)
|
|
{
|
|
case LLHTTPClient::HTTP_HEAD:
|
|
curlEasyRequest_w->setopt(CURLOPT_NOBODY, 1);
|
|
rv = true;
|
|
break;
|
|
|
|
case LLHTTPClient::HTTP_GET:
|
|
curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1);
|
|
|
|
// Set Accept-Encoding to allow response compression
|
|
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
|
|
rv = true;
|
|
break;
|
|
|
|
case LLHTTPClient::HTTP_PUT:
|
|
|
|
// Set the handle for an http put
|
|
curlEasyRequest_w->setPut(mBodySize, mKeepAlive);
|
|
|
|
// Set Accept-Encoding to allow response compression
|
|
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
|
|
rv = true;
|
|
break;
|
|
|
|
case LLHTTPClient::HTTP_POST:
|
|
|
|
// Set the handle for an http post
|
|
curlEasyRequest_w->setPost(mBodySize, mKeepAlive);
|
|
|
|
// Set Accept-Encoding to allow response compression
|
|
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
|
|
rv = true;
|
|
break;
|
|
|
|
case LLHTTPClient::HTTP_DELETE:
|
|
// Set the handle for an http post
|
|
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
|
|
rv = true;
|
|
break;
|
|
|
|
case LLHTTPClient::HTTP_MOVE:
|
|
// Set the handle for an http post
|
|
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
|
|
rv = true;
|
|
break;
|
|
|
|
default:
|
|
llwarns << "Unhandled URLRequest action: " << mAction << llendl;
|
|
break;
|
|
}
|
|
if(rv)
|
|
{
|
|
curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, gNoVerifySSLCert ? 0L : 1L);
|
|
// Don't verify host name if this is not an authentication request,
|
|
// so urls with scrubbed host names will work (improves DNS performance).
|
|
curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, (gNoVerifySSLCert || !mIsAuth) ? 0L : 2L);
|
|
curlEasyRequest_w->finalizeRequest(mURL, mResponder->getHTTPTimeoutPolicy(), this);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// Called from AIStateMachine::mainloop, but put here because we don't want to include llurlrequest.h there of course.
|
|
void print_statemachine_diagnostics(U64 total_clocks, U64 max_delta, AIEngine::queued_type::const_reference slowest_element)
|
|
{
|
|
AIStateMachine const& slowest_state_machine = slowest_element.statemachine();
|
|
LLURLRequest const* request = dynamic_cast<LLURLRequest const*>(&slowest_state_machine);
|
|
F64 const tfactor = 1000 / calc_clock_frequency();
|
|
std::ostringstream msg;
|
|
if (total_clocks > max_delta)
|
|
{
|
|
msg << "AIStateMachine::mainloop did run for " << (total_clocks * tfactor) << " ms. The slowest ";
|
|
}
|
|
else
|
|
{
|
|
msg << "AIStateMachine::mainloop: A ";
|
|
}
|
|
msg << "state machine ";
|
|
if (request)
|
|
{
|
|
msg << "(" << request->getResponderName() << ") ";
|
|
}
|
|
msg << "ran for " << (max_delta * tfactor) << " ms";
|
|
if (slowest_state_machine.getRuntime() > max_delta)
|
|
{
|
|
msg << " (" << (slowest_state_machine.getRuntime() * tfactor) << " ms in total now)";
|
|
}
|
|
msg << ".";
|
|
llwarns << msg.str() << llendl;
|
|
}
|
|
|