phoenix-firestorm/indra/newview/llappcorehttp.cpp

358 lines
9.2 KiB
C++
Executable File

/**
* @file llappcorehttp.cpp
* @brief
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2013, 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 "llviewerprecompiledheaders.h"
#include "llappcorehttp.h"
#include "llappviewer.h"
#include "llviewercontrol.h"
// Here is where we begin to get our connection usage under control.
// This establishes llcorehttp policy classes that, among other
// things, limit the maximum number of connections to outside
// services. Each of the entries below maps to a policy class and
// has a limit, sometimes configurable, of how many connections can
// be open at a time.
const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0);
static const struct
{
LLAppCoreHttp::EAppPolicy mPolicy;
U32 mDefault;
U32 mMin;
U32 mMax;
U32 mRate;
std::string mKey;
const char * mUsage;
} init_data[] = // Default and dynamic values for classes
{
{
LLAppCoreHttp::AP_DEFAULT, 8, 8, 8, 0,
"",
"other"
},
{
LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 0,
"TextureFetchConcurrency",
"texture fetch"
},
{
LLAppCoreHttp::AP_MESH1, 32, 1, 128, 100,
"MeshMaxConcurrentRequests",
"mesh fetch"
},
{
LLAppCoreHttp::AP_MESH2, 8, 1, 32, 100,
"Mesh2MaxConcurrentRequests",
"mesh2 fetch"
},
{
LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 0,
"",
"large mesh fetch"
},
{
LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 0,
"",
"asset upload"
},
{
LLAppCoreHttp::AP_LONG_POLL, 32, 32, 32, 0,
"",
"long poll"
}
};
static void setting_changed();
LLAppCoreHttp::LLAppCoreHttp()
: mRequest(NULL),
mStopHandle(LLCORE_HTTP_HANDLE_INVALID),
mStopRequested(0.0),
mStopped(false)
{
for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i)
{
mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
mSettings[i] = 0U;
}
}
LLAppCoreHttp::~LLAppCoreHttp()
{
delete mRequest;
mRequest = NULL;
}
void LLAppCoreHttp::init()
{
LLCore::HttpStatus status = LLCore::HttpRequest::createService();
if (! status)
{
LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " << status.toString()
<< LL_ENDL;
}
// Point to our certs or SSH/https: will fail on connect
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
LLCore::HttpRequest::GLOBAL_POLICY_ID,
gDirUtilp->getCAFile(), NULL);
if (! status)
{
LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " << status.toString()
<< LL_ENDL;
}
// Establish HTTP Proxy, if desired.
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY,
LLCore::HttpRequest::GLOBAL_POLICY_ID,
1, NULL);
if (! status)
{
LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString()
<< LL_ENDL;
}
// Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy):
// 0 - None
// 1 - Basic start, stop simple transitions
// 2 - libcurl CURLOPT_VERBOSE mode with brief lines
// 3 - with partial data content
static const std::string http_trace("QAModeHttpTrace");
if (gSavedSettings.controlExists(http_trace))
{
long trace_level(0L);
trace_level = long(gSavedSettings.getU32(http_trace));
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE,
LLCore::HttpRequest::GLOBAL_POLICY_ID,
trace_level, NULL);
}
// Setup default policy and constrain if directed to
mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
// Setup additional policies based on table and some special rules
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
const EAppPolicy policy(init_data[i].mPolicy);
if (AP_DEFAULT == policy)
{
// Pre-created
continue;
}
mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
if (! mPolicies[policy])
{
// Use default policy (but don't accidentally modify default)
LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
<< ". Using default policy."
<< LL_ENDL;
mPolicies[policy] = mPolicies[AP_DEFAULT];
continue;
}
}
// Need a request object to handle dynamic options before setting them
mRequest = new LLCore::HttpRequest;
// Apply initial settings
refreshSettings(true);
// Kick the thread
status = LLCore::HttpRequest::startThread();
if (! status)
{
LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " << status.toString()
<< LL_ENDL;
}
// Register signals for settings and state changes
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
{
LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey);
if (cntrl_ptr.isNull())
{
LL_WARNS("Init") << "Unable to set signal on global setting '" << init_data[i].mKey
<< "'" << LL_ENDL;
}
else
{
mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed));
}
}
}
}
void setting_changed()
{
LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false);
}
void LLAppCoreHttp::requestStop()
{
llassert_always(mRequest);
mStopHandle = mRequest->requestStopThread(this);
if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle)
{
mStopRequested = LLTimer::getTotalSeconds();
}
}
void LLAppCoreHttp::cleanup()
{
if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle)
{
// Should have been started already...
requestStop();
}
if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle)
{
LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown"
<< LL_ENDL;
}
else
{
while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME))
{
mRequest->update(200000);
ms_sleep(50);
}
if (! mStopped)
{
LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete"
<< LL_ENDL;
}
}
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
mSettingsSignal[i].disconnect();
}
delete mRequest;
mRequest = NULL;
LLCore::HttpStatus status = LLCore::HttpRequest::destroyService();
if (! status)
{
LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: "
<< status.toString()
<< LL_ENDL;
}
}
void LLAppCoreHttp::refreshSettings(bool initial)
{
LLCore::HttpStatus status;
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
const EAppPolicy policy(init_data[i].mPolicy);
// Set any desired throttle
if (initial && init_data[i].mRate)
{
// Init-time only, can use the static setters here
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE,
mPolicies[policy],
init_data[i].mRate,
NULL);
if (! status)
{
LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
<< " throttle rate. Reason: " << status.toString()
<< LL_ENDL;
}
}
// Get target connection concurrency value
U32 setting(init_data[i].mDefault);
if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
{
U32 new_setting(gSavedSettings.getU32(init_data[i].mKey));
if (new_setting)
{
// Treat zero settings as an ask for default
setting = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax);
}
}
if (! initial && setting == mSettings[policy])
{
// Unchanged, try next setting
continue;
}
// Set it and report
// *TODO: These are intended to be per-host limits when we can
// support that in llcorehttp/libcurl.
LLCore::HttpHandle handle;
handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
mPolicies[policy],
setting, NULL);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
status = mRequest->getStatus();
LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
<< " concurrency. Reason: " << status.toString()
<< LL_ENDL;
}
else
{
LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage
<< " concurrency. New value: " << setting
<< LL_ENDL;
mSettings[policy] = setting;
if (initial && setting != init_data[i].mDefault)
{
LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage
<< " concurrency. New value: " << setting
<< LL_ENDL;
}
}
}
}
void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *)
{
mStopped = true;
}