summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty Brandenberg <monty@lindenlab.com>2013-07-12 15:00:24 -0400
committerMonty Brandenberg <monty@lindenlab.com>2013-07-12 15:00:24 -0400
commiteff651cffca60f2b69f6c596a8e9aa9e1ab44d3c (patch)
tree43b7a995f0e3df6942643735e6e1ea615e7a1b0c
parentfb734d621e6fa2004d191849783e81da75992d06 (diff)
SH-4312 Configuration data between viewer and llcorehttp is clumsy.
Much improved. Unified the global and class options into a single option list. Implemented static and dynamic setting paths as much as possible. Dynamic path does require packet/RPC but otherwise there's near unification. Dynamic modes can't get values back yet due to the response/notifier scheme but this doesn't bother me. Flatten global and class options into simpler struct-like entities. Setter/getter available on these when needed (external APIs) but code can otherwise fiddle directly when it knows what to do. Much duplicated options/state removed from HttpPolicy. Comments cleaned up. Threads better described and consistently mentioned in API docs. Integration test extended for 503 responses with Reply-After headers.
-rwxr-xr-xindra/llcorehttp/_httpinternal.h6
-rwxr-xr-xindra/llcorehttp/_httplibcurl.h15
-rwxr-xr-xindra/llcorehttp/_httpoperation.h8
-rwxr-xr-xindra/llcorehttp/_httpoprequest.cpp19
-rwxr-xr-xindra/llcorehttp/_httpopsetget.cpp93
-rwxr-xr-xindra/llcorehttp/_httpopsetget.h27
-rwxr-xr-xindra/llcorehttp/_httppolicy.cpp110
-rwxr-xr-xindra/llcorehttp/_httppolicy.h39
-rwxr-xr-xindra/llcorehttp/_httppolicyclass.cpp37
-rwxr-xr-xindra/llcorehttp/_httppolicyclass.h19
-rwxr-xr-xindra/llcorehttp/_httppolicyglobal.cpp66
-rwxr-xr-xindra/llcorehttp/_httppolicyglobal.h23
-rwxr-xr-xindra/llcorehttp/_httpservice.cpp170
-rwxr-xr-xindra/llcorehttp/_httpservice.h52
-rwxr-xr-xindra/llcorehttp/examples/http_texture_load.cpp7
-rwxr-xr-xindra/llcorehttp/httpcommon.h109
-rwxr-xr-xindra/llcorehttp/httprequest.cpp107
-rwxr-xr-xindra/llcorehttp/httprequest.h128
-rwxr-xr-xindra/llcorehttp/tests/test_httprequest.hpp140
-rwxr-xr-xindra/llcorehttp/tests/test_llcorehttp_peer.py49
-rwxr-xr-xindra/newview/llappcorehttp.cpp29
21 files changed, 881 insertions, 372 deletions
diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index f085ca3b91..80f4f34942 100755
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -36,7 +36,8 @@
// General library to-do list
//
// - Implement policy classes. Structure is mostly there just didn't
-// need it for the first consumer.
+// need it for the first consumer. [Classes are there. More
+// advanced features, like borrowing, aren't there yet.]
// - Consider Removing 'priority' from the request interface. Its use
// in an always active class can lead to starvation of low-priority
// requests. Requires coodination of priority values across all
@@ -46,6 +47,7 @@
// may not really need it.
// - Set/get for global policy and policy classes is clumsy. Rework
// it heading in a direction that allows for more dynamic behavior.
+// [Mostly fixed]
// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the
// pedantic.
// - Update downloader and other long-duration services are going to
@@ -73,7 +75,7 @@
// the main source file.
// - Expand areas of usage eventually leading to the removal of LLCurl.
// Rough order of expansion:
-// . Mesh fetch
+// . Mesh fetch [Underway]
// . Avatar names
// . Group membership lists
// . Caps access in general
diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h
index 611f029ef5..0ec90437bb 100755
--- a/indra/llcorehttp/_httplibcurl.h
+++ b/indra/llcorehttp/_httplibcurl.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -71,16 +71,22 @@ public:
///
/// @return Indication of how long this method is
/// willing to wait for next service call.
+ ///
+ /// Threading: called by worker thread.
HttpService::ELoopSpeed processTransport();
/// Add request to the active list. Caller is expected to have
/// provided us with a reference count on the op to hold the
/// request. (No additional references will be added.)
+ ///
+ /// Threading: called by worker thread.
void addOp(HttpOpRequest * op);
/// One-time call to set the number of policy classes to be
/// serviced and to create the resources for each. Value
/// must agree with HttpPolicy::setPolicies() call.
+ ///
+ /// Threading: called by init thread.
void start(int policy_count);
/// Synchronously stop libcurl operations. All active requests
@@ -91,9 +97,13 @@ public:
/// respective reply queues.
///
/// Can be restarted with a start() call.
+ ///
+ /// Threading: called by worker thread.
void shutdown();
/// Return global and per-class counts of active requests.
+ ///
+ /// Threading: called by worker thread.
int getActiveCount() const;
int getActiveCountInClass(int policy_class) const;
@@ -103,6 +113,7 @@ public:
///
/// @return True if handle was found and operation canceled.
///
+ /// Threading: called by worker thread.
bool cancel(HttpHandle handle);
protected:
@@ -121,7 +132,7 @@ protected:
HttpService * mService; // Simple reference, not owner
active_set_t mActiveOps;
int mPolicyCount;
- CURLM ** mMultiHandles;
+ CURLM ** mMultiHandles; // One handle per policy class
}; // end class HttpLibcurl
} // end namespace LLCore
diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h
index 914627fad0..937a61187d 100755
--- a/indra/llcorehttp/_httpoperation.h
+++ b/indra/llcorehttp/_httpoperation.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -72,7 +72,7 @@ class HttpService;
class HttpOperation : public LLCoreInt::RefCounted
{
public:
- /// Threading: called by a consumer/application thread.
+ /// Threading: called by consumer thread.
HttpOperation();
protected:
@@ -108,7 +108,7 @@ public:
/// by the worker thread. This is passible data
/// until notification is performed.
///
- /// Threading: called by application thread.
+ /// Threading: called by consumer thread.
///
void setReplyPath(HttpReplyQueue * reply_queue,
HttpHandler * handler);
@@ -141,7 +141,7 @@ public:
/// call to HttpRequest::update(). This method does the necessary
/// dispatching.
///
- /// Threading: called by application thread.
+ /// Threading: called by consumer thread.
///
virtual void visitNotifier(HttpRequest *);
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index ce0fc605ab..d72f8f6119 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -402,7 +402,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
// *FIXME: better error handling later
HttpStatus status;
- // Get policy options
+ // Get global policy options
HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
mCurlHandle = curl_easy_init();
@@ -441,30 +441,27 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
- const std::string * opt_value(NULL);
- long opt_long(0L);
- policy.get(HttpRequest::GP_LLPROXY, &opt_long);
- if (opt_long)
+ if (policy.mUseLLProxy)
{
// Use the viewer-based thread-safe API which has a
// fast/safe check for proxy enable. Would like to
// encapsulate this someway...
LLProxy::getInstance()->applyProxySettings(mCurlHandle);
}
- else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value))
+ else if (policy.mHttpProxy.size())
{
// *TODO: This is fine for now but get fuller socks5/
// authentication thing going later....
- curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str());
+ curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
}
- if (policy.get(HttpRequest::GP_CA_PATH, &opt_value))
+ if (policy.mCAPath.size())
{
- curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
+ curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());
}
- if (policy.get(HttpRequest::GP_CA_FILE, &opt_value))
+ if (policy.mCAFile.size())
{
- curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
+ curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());
}
switch (mReqMethod)
diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp
index 8198528a9b..a5363f9170 100755
--- a/indra/llcorehttp/_httpopsetget.cpp
+++ b/indra/llcorehttp/_httpopsetget.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -27,6 +27,7 @@
#include "_httpopsetget.h"
#include "httpcommon.h"
+#include "httprequest.h"
#include "_httpservice.h"
#include "_httppolicy.h"
@@ -43,10 +44,11 @@ namespace LLCore
HttpOpSetGet::HttpOpSetGet()
: HttpOperation(),
- mIsGlobal(false),
- mDoSet(false),
- mSetting(-1), // Nothing requested
- mLongValue(0L)
+ mReqOption(HttpRequest::PO_CONNECTION_LIMIT),
+ mReqClass(HttpRequest::INVALID_POLICY_ID),
+ mReqDoSet(false),
+ mReqLongValue(0L),
+ mReplyLongValue(0L)
{}
@@ -54,37 +56,84 @@ HttpOpSetGet::~HttpOpSetGet()
{}
-void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting)
+HttpStatus HttpOpSetGet::setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass)
{
- mIsGlobal = true;
- mSetting = setting;
+ HttpStatus status;
+
+ mReqOption = opt;
+ mReqClass = pclass;
+ return status;
}
-void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value)
+HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value)
{
- mIsGlobal = true;
- mDoSet = true;
- mSetting = setting;
- mStrValue = value;
+ HttpStatus status;
+
+ if (! HttpService::sOptionDesc[opt].mIsLong)
+ {
+ return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
+ }
+ if (! HttpService::sOptionDesc[opt].mIsDynamic)
+ {
+ return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+ }
+
+ mReqOption = opt;
+ mReqClass = pclass;
+ mReqDoSet = true;
+ mReqLongValue = value;
+
+ return status;
}
-void HttpOpSetGet::stageFromRequest(HttpService * service)
+HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value)
{
- HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions());
- HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting));
+ HttpStatus status;
+
+ if (HttpService::sOptionDesc[opt].mIsLong)
+ {
+ return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
+ }
+ if (! HttpService::sOptionDesc[opt].mIsDynamic)
+ {
+ return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+ }
+
+ mReqOption = opt;
+ mReqClass = pclass;
+ mReqDoSet = true;
+ mReqStrValue = value;
- if (mDoSet)
+ return status;
+}
+
+
+void HttpOpSetGet::stageFromRequest(HttpService * service)
+{
+ if (mReqDoSet)
{
- mStatus = pol_opt.set(setting, mStrValue);
+ if (HttpService::sOptionDesc[mReqOption].mIsLong)
+ {
+ mStatus = service->setPolicyOption(mReqOption, mReqClass,
+ mReqLongValue, &mReplyLongValue);
+ }
+ else
+ {
+ mStatus = service->setPolicyOption(mReqOption, mReqClass,
+ mReqStrValue, &mReplyStrValue);
+ }
}
- if (mStatus)
+ else
{
- const std::string * value(NULL);
- if ((mStatus = pol_opt.get(setting, &value)))
+ if (HttpService::sOptionDesc[mReqOption].mIsLong)
+ {
+ mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyLongValue);
+ }
+ else
{
- mStrValue = *value;
+ mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyStrValue);
}
}
diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h
index 6966b9d94e..a1e76dd429 100755
--- a/indra/llcorehttp/_httpopsetget.h
+++ b/indra/llcorehttp/_httpopsetget.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -46,7 +46,10 @@ namespace LLCore
/// configuration settings.
///
/// *NOTE: Expect this to change. Don't really like it yet.
-
+///
+/// *TODO: Can't return values to caller yet. Need to do
+/// something better with HttpResponse and visitNotifier().
+///
class HttpOpSetGet : public HttpOperation
{
public:
@@ -61,19 +64,23 @@ private:
public:
/// Threading: called by application thread
- void setupGet(HttpRequest::EGlobalPolicy setting);
- void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value);
+ HttpStatus setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass);
+ HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value);
+ HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value);
virtual void stageFromRequest(HttpService *);
public:
// Request data
- bool mIsGlobal;
- bool mDoSet;
- int mSetting;
- long mLongValue;
- std::string mStrValue;
-
+ HttpRequest::EPolicyOption mReqOption;
+ HttpRequest::policy_t mReqClass;
+ bool mReqDoSet;
+ long mReqLongValue;
+ std::string mReqStrValue;
+
+ // Reply Data
+ long mReplyLongValue;
+ std::string mReplyStrValue;
}; // end class HttpOpSetGet
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 5f303dd0fe..2754e8ef07 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -41,57 +41,64 @@ namespace LLCore
// Per-policy-class data for a running system.
-// Collection of queues, parameters, history, metrics, etc.
+// Collection of queues, options and other data
// for a single policy class.
//
// Threading: accessed only by worker thread
-struct HttpPolicy::State
+struct HttpPolicy::ClassState
{
public:
- State()
- : mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT),
- mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT),
- mConnMin(1),
- mNextSample(0),
- mErrorCount(0),
- mErrorFactor(0)
+ ClassState()
{}
HttpReadyQueue mReadyQueue;
HttpRetryQueue mRetryQueue;
HttpPolicyClass mOptions;
-
- long mConnMax;
- long mConnAt;
- long mConnMin;
-
- HttpTime mNextSample;
- unsigned long mErrorCount;
- unsigned long mErrorFactor;
};
HttpPolicy::HttpPolicy(HttpService * service)
- : mActiveClasses(0),
- mState(NULL),
- mService(service)
-{}
+ : mService(service)
+{
+ // Create default class
+ mClasses.push_back(new ClassState());
+}
HttpPolicy::~HttpPolicy()
{
shutdown();
+
+ for (class_list_t::iterator it(mClasses.begin()); it != mClasses.end(); ++it)
+ {
+ delete (*it);
+ }
+ mClasses.clear();
mService = NULL;
}
+HttpRequest::policy_t HttpPolicy::createPolicyClass()
+{
+ const HttpRequest::policy_t policy_class(mClasses.size());
+ if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
+ {
+ return HttpRequest::INVALID_POLICY_ID;
+ }
+ mClasses.push_back(new ClassState());
+ return policy_class;
+}
+
+
void HttpPolicy::shutdown()
{
- for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+ for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
- HttpRetryQueue & retryq(mState[policy_class].mRetryQueue);
+ ClassState & state(*mClasses[policy_class]);
+
+ HttpRetryQueue & retryq(state.mRetryQueue);
while (! retryq.empty())
{
HttpOpRequest * op(retryq.top());
@@ -101,7 +108,7 @@ void HttpPolicy::shutdown()
op->release();
}
- HttpReadyQueue & readyq(mState[policy_class].mReadyQueue);
+ HttpReadyQueue & readyq(state.mReadyQueue);
while (! readyq.empty())
{
HttpOpRequest * op(readyq.top());
@@ -111,28 +118,11 @@ void HttpPolicy::shutdown()
op->release();
}
}
- delete [] mState;
- mState = NULL;
- mActiveClasses = 0;
}
-void HttpPolicy::start(const HttpPolicyGlobal & global,
- const std::vector<HttpPolicyClass> & classes)
-{
- llassert_always(! mState);
-
- mGlobalOptions = global;
- mActiveClasses = classes.size();
- mState = new State [mActiveClasses];
- for (int i(0); i < mActiveClasses; ++i)
- {
- mState[i].mOptions = classes[i];
- mState[i].mConnMax = classes[i].mConnectionLimit;
- mState[i].mConnAt = mState[i].mConnMax;
- mState[i].mConnMin = 2;
- }
-}
+void HttpPolicy::start()
+{}
void HttpPolicy::addOp(HttpOpRequest * op)
@@ -141,7 +131,7 @@ void HttpPolicy::addOp(HttpOpRequest * op)
op->mPolicyRetries = 0;
op->mPolicy503Retries = 0;
- mState[policy_class].mReadyQueue.push(op);
+ mClasses[policy_class]->mReadyQueue.push(op);
}
@@ -183,7 +173,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
<< static_cast<HttpHandle>(op)
<< LL_ENDL;
}
- mState[policy_class].mRetryQueue.push(op);
+ mClasses[policy_class]->mRetryQueue.push(op);
}
@@ -204,11 +194,11 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP);
HttpLibcurl & transport(mService->getTransport());
- for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+ for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
- State & state(mState[policy_class]);
+ ClassState & state(*mClasses[policy_class]);
int active(transport.getActiveCountInClass(policy_class));
- int needed(state.mConnAt - active); // Expect negatives here
+ int needed(state.mOptions.mConnectionLimit - active); // Expect negatives here
HttpRetryQueue & retryq(state.mRetryQueue);
HttpReadyQueue & readyq(state.mReadyQueue);
@@ -256,9 +246,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
{
- for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+ for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
- State & state(mState[policy_class]);
+ ClassState & state(*mClasses[policy_class]);
// We don't scan retry queue because a priority change there
// is meaningless. The request will be issued based on retry
// intervals not priority value, which is now moot.
@@ -286,9 +276,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
bool HttpPolicy::cancel(HttpHandle handle)
{
- for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+ for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
- State & state(mState[policy_class]);
+ ClassState & state(*mClasses[policy_class]);
// Scan retry queue
HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
@@ -382,13 +372,21 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
return false; // not active
}
+
+HttpPolicyClass & HttpPolicy::getClassOptions(HttpRequest::policy_t pclass)
+{
+ llassert_always(pclass >= 0 && pclass < mClasses.size());
+
+ return mClasses[pclass]->mOptions;
+}
+
int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
{
- if (policy_class < mActiveClasses)
+ if (policy_class < mClasses.size())
{
- return (mState[policy_class].mReadyQueue.size()
- + mState[policy_class].mRetryQueue.size());
+ return (mClasses[policy_class]->mReadyQueue.size()
+ + mClasses[policy_class]->mRetryQueue.size());
}
return 0;
}
diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h
index 03d92c0b8e..bf1aa74267 100755
--- a/indra/llcorehttp/_httppolicy.h
+++ b/indra/llcorehttp/_httppolicy.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -60,6 +60,9 @@ private:
void operator=(const HttpPolicy &); // Not defined
public:
+ /// Threading: called by init thread.
+ HttpRequest::policy_t createPolicyClass();
+
/// Cancel all ready and retry requests sending them to
/// their notification queues. Release state resources
/// making further request handling impossible.
@@ -71,9 +74,8 @@ public:
/// requests. One-time call invoked before starting
/// the worker thread.
///
- /// Threading: called by application thread
- void start(const HttpPolicyGlobal & global,
- const std::vector<HttpPolicyClass> & classes);
+ /// Threading: called by init thread
+ void start();
/// Give the policy layer some cycles to scan the ready
/// queue promoting higher-priority requests to active
@@ -93,7 +95,7 @@ public:
/// and should not be modified by anyone until retrieved
/// from queue.
///
- /// Threading: called by any thread
+ /// Threading: called by worker thread
void addOp(HttpOpRequest *);
/// Similar to addOp, used when a caller wants to retry a
@@ -130,30 +132,39 @@ public:
/// Threading: called by worker thread
bool stageAfterCompletion(HttpOpRequest * op);
- // Get pointer to global policy options. Caller is expected
- // to do context checks like no setting once running.
+ /// Get a reference to global policy options. Caller is expected
+ /// to do context checks like no setting once running. These
+ /// are done, for example, in @see HttpService interfaces.
///
/// Threading: called by any thread *but* the object may
/// only be modified by the worker thread once running.
- ///
HttpPolicyGlobal & getGlobalOptions()
{
return mGlobalOptions;
}
+ /// Get a reference to class policy options. Caller is expected
+ /// to do context checks like no setting once running. These
+ /// are done, for example, in @see HttpService interfaces.
+ ///
+ /// Threading: called by any thread *but* the object may
+ /// only be modified by the worker thread once running and
+ /// read accesses by other threads are exposed to races at
+ /// that point.
+ HttpPolicyClass & getClassOptions(HttpRequest::policy_t pclass);
+
/// Get ready counts for a particular policy class
///
/// Threading: called by worker thread
int getReadyCount(HttpRequest::policy_t policy_class) const;
protected:
- struct State;
-
- int mActiveClasses;
- State * mState;
- HttpService * mService; // Naked pointer, not refcounted, not owner
- HttpPolicyGlobal mGlobalOptions;
+ struct ClassState;
+ typedef std::vector<ClassState *> class_list_t;
+ HttpPolicyGlobal mGlobalOptions;
+ class_list_t mClasses;
+ HttpService * mService; // Naked pointer, not refcounted, not owner
}; // end class HttpPolicy
} // end namespace LLCore
diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp
index 1a55ab1ac6..fe4359081a 100755
--- a/indra/llcorehttp/_httppolicyclass.cpp
+++ b/indra/llcorehttp/_httppolicyclass.cpp
@@ -34,8 +34,7 @@ namespace LLCore
HttpPolicyClass::HttpPolicyClass()
- : mSetMask(0UL),
- mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
+ : mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mPipelining(HTTP_PIPELINING_DEFAULT)
{}
@@ -49,7 +48,6 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
{
if (this != &other)
{
- mSetMask = other.mSetMask;
mConnectionLimit = other.mConnectionLimit;
mPerHostConnectionLimit = other.mPerHostConnectionLimit;
mPipelining = other.mPipelining;
@@ -59,26 +57,25 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
- : mSetMask(other.mSetMask),
- mConnectionLimit(other.mConnectionLimit),
+ : mConnectionLimit(other.mConnectionLimit),
mPerHostConnectionLimit(other.mPerHostConnectionLimit),
mPipelining(other.mPipelining)
{}
-HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
+HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)
{
switch (opt)
{
- case HttpRequest::CP_CONNECTION_LIMIT:
+ case HttpRequest::PO_CONNECTION_LIMIT:
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
break;
- case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
+ case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);
break;
- case HttpRequest::CP_ENABLE_PIPELINING:
+ case HttpRequest::PO_ENABLE_PIPELINING:
mPipelining = llclamp(value, 0L, 1L);
break;
@@ -86,38 +83,30 @@ HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
- mSetMask |= 1UL << int(opt);
return HttpStatus();
}
-HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value)
+HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) const
{
- static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
- long * src(NULL);
-
switch (opt)
{
- case HttpRequest::CP_CONNECTION_LIMIT:
- src = &mConnectionLimit;
+ case HttpRequest::PO_CONNECTION_LIMIT:
+ *value = mConnectionLimit;
break;
- case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
- src = &mPerHostConnectionLimit;
+ case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
+ *value = mPerHostConnectionLimit;
break;
- case HttpRequest::CP_ENABLE_PIPELINING:
- src = &mPipelining;
+ case HttpRequest::PO_ENABLE_PIPELINING:
+ *value = mPipelining;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
- if (! (mSetMask & (1UL << int(opt))))
- return not_set;
-
- *value = *src;
return HttpStatus();
}
diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h
index d175413cbd..69fb459d22 100755
--- a/indra/llcorehttp/_httppolicyclass.h
+++ b/indra/llcorehttp/_httppolicyclass.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -34,6 +34,18 @@
namespace LLCore
{
+/// Options struct for per-class policy options.
+///
+/// Combines both raw blob data access with semantics-enforcing
+/// set/get interfaces. For internal operations by the worker
+/// thread, just grab the setting directly from instance and test/use
+/// as needed. When attached to external APIs (the public API
+/// options interfaces) the set/get methods are available to
+/// enforce correct ranges, data types, contexts, etc. and suitable
+/// status values are returned.
+///
+/// Threading: Single-threaded. In practice, init thread before
+/// worker starts, worker thread after.
class HttpPolicyClass
{
public:
@@ -44,11 +56,10 @@ public:
HttpPolicyClass(const HttpPolicyClass &); // Not defined
public:
- HttpStatus set(HttpRequest::EClassPolicy opt, long value);
- HttpStatus get(HttpRequest::EClassPolicy opt, long * value);
+ HttpStatus set(HttpRequest::EPolicyOption opt, long value);
+ HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
public:
- unsigned long mSetMask;
long mConnectionLimit;
long mPerHostConnectionLimit;
long mPipelining;
diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp
index 72f409d3b1..1dc95f3dce 100755
--- a/indra/llcorehttp/_httppolicyglobal.cpp
+++ b/indra/llcorehttp/_httppolicyglobal.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -34,8 +34,7 @@ namespace LLCore
HttpPolicyGlobal::HttpPolicyGlobal()
- : mSetMask(0UL),
- mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
+ : mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mTrace(HTTP_TRACE_OFF),
mUseLLProxy(0)
{}
@@ -49,7 +48,6 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
{
if (this != &other)
{
- mSetMask = other.mSetMask;
mConnectionLimit = other.mConnectionLimit;
mCAPath = other.mCAPath;
mCAFile = other.mCAFile;
@@ -61,19 +59,19 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
}
-HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
+HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, long value)
{
switch (opt)
{
- case HttpRequest::GP_CONNECTION_LIMIT:
+ case HttpRequest::PO_CONNECTION_LIMIT:
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
break;
- case HttpRequest::GP_TRACE:
+ case HttpRequest::PO_TRACE:
mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));
break;
- case HttpRequest::GP_LLPROXY:
+ case HttpRequest::PO_LLPROXY:
mUseLLProxy = llclamp(value, 0L, 1L);
break;
@@ -81,24 +79,23 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
- mSetMask |= 1UL << int(opt);
return HttpStatus();
}
-HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value)
+HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::string & value)
{
switch (opt)
{
- case HttpRequest::GP_CA_PATH:
+ case HttpRequest::PO_CA_PATH:
mCAPath = value;
break;
- case HttpRequest::GP_CA_FILE:
+ case HttpRequest::PO_CA_FILE:
mCAFile = value;
break;
- case HttpRequest::GP_HTTP_PROXY:
+ case HttpRequest::PO_HTTP_PROXY:
mCAFile = value;
break;
@@ -106,69 +103,54 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
- mSetMask |= 1UL << int(opt);
return HttpStatus();
}
-HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value)
+HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
{
- static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
- long * src(NULL);
-
switch (opt)
{
- case HttpRequest::GP_CONNECTION_LIMIT:
- src = &mConnectionLimit;
+ case HttpRequest::PO_CONNECTION_LIMIT:
+ *value = mConnectionLimit;
break;
- case HttpRequest::GP_TRACE:
- src = &mTrace;
+ case HttpRequest::PO_TRACE:
+ *value = mTrace;
break;
- case HttpRequest::GP_LLPROXY:
- src = &mUseLLProxy;
+ case HttpRequest::PO_LLPROXY:
+ *value = mUseLLProxy;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
- if (! (mSetMask & (1UL << int(opt))))
- return not_set;
-
- *value = *src;
return HttpStatus();
}
-HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value)
+HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * value) const
{
- static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
- const std::string * src(NULL);
-
switch (opt)
{
- case HttpRequest::GP_CA_PATH:
- src = &mCAPath;
+ case HttpRequest::PO_CA_PATH:
+ *value = mCAPath;
break;
- case HttpRequest::GP_CA_FILE:
- src = &mCAFile;
+ case HttpRequest::PO_CA_FILE:
+ *value = mCAFile;
break;
- case HttpRequest::GP_HTTP_PROXY:
- src = &mHttpProxy;
+ case HttpRequest::PO_HTTP_PROXY:
+ *value = mHttpProxy;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
- if (! (mSetMask & (1UL << int(opt))))
- return not_set;
-
- *value = src;
return HttpStatus();
}
diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h
index a50d0e4188..67c4ba9481 100755
--- a/indra/llcorehttp/_httppolicyglobal.h
+++ b/indra/llcorehttp/_httppolicyglobal.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -34,6 +34,18 @@
namespace LLCore
{
+/// Options struct for global policy options.
+///
+/// Combines both raw blob data access with semantics-enforcing
+/// set/get interfaces. For internal operations by the worker
+/// thread, just grab the setting directly from instance and test/use
+/// as needed. When attached to external APIs (the public API
+/// options interfaces) the set/get methods are available to
+/// enforce correct ranges, data types, contexts, etc. and suitable
+/// status values are returned.
+///
+/// Threading: Single-threaded. In practice, init thread before
+/// worker starts, worker thread after.
class HttpPolicyGlobal
{
public:
@@ -46,13 +58,12 @@ private:
HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined
public:
- HttpStatus set(HttpRequest::EGlobalPolicy opt, long value);
- HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value);
- HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value);
- HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value);
+ HttpStatus set(HttpRequest::EPolicyOption opt, long value);
+ HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
+ HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
+ HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
public:
- unsigned long mSetMask;
long mConnectionLimit;
std::string mCAPath;
std::string mCAFile;
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index 0821401289..e21d196a3e 100755
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -43,6 +43,17 @@
namespace LLCore
{
+const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
+{ // isLong isDynamic isGlobal isClass
+ { true, true, true, true }, // PO_CONNECTION_LIMIT
+ { true, true, false, true }, // PO_PER_HOST_CONNECTION_LIMIT
+ { false, false, true, false }, // PO_CA_PATH
+ { false, false, true, false }, // PO_CA_FILE
+ { false, true, true, false }, // PO_HTTP_PROXY
+ { true, true, true, false }, // PO_LLPROXY
+ { true, true, true, false }, // PO_TRACE
+ { true, true, false, true } // PO_ENABLE_PIPELINING
+};
HttpService * HttpService::sInstance(NULL);
volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
@@ -51,12 +62,9 @@ HttpService::HttpService()
mExitRequested(0U),
mThread(NULL),
mPolicy(NULL),
- mTransport(NULL)
-{
- // Create the default policy class
- HttpPolicyClass pol_class;
- mPolicyClasses.push_back(pol_class);
-}
+ mTransport(NULL),
+ mLastPolicy(0)
+{}
HttpService::~HttpService()
@@ -146,13 +154,8 @@ void HttpService::term()
HttpRequest::policy_t HttpService::createPolicyClass()
{
- const HttpRequest::policy_t policy_class(mPolicyClasses.size());
- if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
- {
- return 0;
- }
- mPolicyClasses.push_back(HttpPolicyClass());
- return policy_class;
+ mLastPolicy = mPolicy->createPolicyClass();
+ return mLastPolicy;
}
@@ -185,8 +188,8 @@ void HttpService::startThread()
}
// Push current policy definitions, enable policy & transport components
- mPolicy->start(mPolicyGlobal, mPolicyClasses);
- mTransport->start(mPolicyClasses.size());
+ mPolicy->start();
+ mTransport->start(mLastPolicy + 1);
mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
sState = RUNNING;
@@ -319,7 +322,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
{
// Setup for subsequent tracing
long tracing(HTTP_TRACE_OFF);
- mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing);
+ mPolicy->getGlobalOptions().get(HttpRequest::PO_TRACE, &tracing);
op->mTracing = (std::max)(op->mTracing, int(tracing));
if (op->mTracing > HTTP_TRACE_OFF)
@@ -342,4 +345,137 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
}
+HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+ long * ret_value)
+{
+ if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
+ || opt >= HttpRequest::PO_LAST // ditto
+ || (! sOptionDesc[opt].mIsLong) // datatype is long
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
+ || (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)) // class setting permitted
+ // can always get, no dynamic check
+ {
+ return HttpStatus(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+ }
+
+ HttpStatus status;
+ if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+ {
+ HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+ status = opts.get(opt, ret_value);
+ }
+ else
+ {
+ HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
+
+ status = opts.get(opt, ret_value);
+ }
+
+ return status;
+}
+
+
+HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+ std::string * ret_value)
+{
+ HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+
+ if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
+ || opt >= HttpRequest::PO_LAST // ditto
+ || (sOptionDesc[opt].mIsLong) // datatype is string
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
+ || (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)) // class setting permitted
+ // can always get, no dynamic check
+ {
+ return status;
+ }
+
+ // Only global has string values
+ if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+ {
+ HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+ status = opts.get(opt, ret_value);
+ }
+
+ return status;
+}
+
+
+HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+ long value, long * ret_value)
+{
+ HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+
+ if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
+ || opt >= HttpRequest::PO_LAST // ditto
+ || (! sOptionDesc[opt].mIsLong) // datatype is long
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
+ || (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass) // class setting permitted
+ || (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
+ {
+ return status;
+ }
+
+ if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+ {
+ HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+ status = opts.set(opt, value);
+ if (status && ret_value)
+ {
+ status = opts.get(opt, ret_value);
+ }
+ }
+ else
+ {
+ HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
+
+ status = opts.set(opt, value);
+ if (status && ret_value)
+ {
+ status = opts.get(opt, ret_value);
+ }
+ }
+
+ return status;
+}
+
+
+HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+ const std::string & value, std::string * ret_value)
+{
+ HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+
+ if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
+ || opt >= HttpRequest::PO_LAST // ditto
+ || (sOptionDesc[opt].mIsLong) // datatype is string
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
+ || (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass) // class setting permitted
+ || (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
+ {
+ return status;
+ }
+
+ // Only string values are global at this time
+ if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+ {
+ HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+ status = opts.set(opt, value);
+ if (status && ret_value)
+ {
+ status = opts.get(opt, ret_value);
+ }
+ }
+
+ return status;
+}
+
+
} // end namespace LLCore
diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h
index ffe0349d4d..cf23f3ab61 100755
--- a/indra/llcorehttp/_httpservice.h
+++ b/indra/llcorehttp/_httpservice.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -53,6 +53,7 @@ namespace LLCore
class HttpRequestQueue;
class HttpPolicy;
class HttpLibcurl;
+class HttpOpSetGet;
/// The HttpService class does the work behind the request queue. It
@@ -106,7 +107,7 @@ public:
NORMAL, ///< continuous polling of request, ready, active queues
REQUEST_SLEEP ///< can sleep indefinitely waiting for request queue write
};
-
+
static void init(HttpRequestQueue *);
static void term();
@@ -136,7 +137,7 @@ public:
/// acquires its weaknesses.
static bool isStopped();
- /// Threading: callable by consumer thread *once*.
+ /// Threading: callable by init thread *once*.
void startThread();
/// Threading: callable by worker thread.
@@ -181,27 +182,38 @@ public:
}
/// Threading: callable by consumer thread.
- HttpPolicyGlobal & getGlobalOptions()
- {
- return mPolicyGlobal;
- }
-
- /// Threading: callable by consumer thread.
HttpRequest::policy_t createPolicyClass();
- /// Threading: callable by consumer thread.
- HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class)
- {
- llassert(policy_class >= 0 && policy_class < mPolicyClasses.size());
- return mPolicyClasses[policy_class];
- }
-
protected:
void threadRun(LLCoreInt::HttpThread * thread);
ELoopSpeed processRequestQueue(ELoopSpeed loop);
+
+protected:
+ friend class HttpOpSetGet;
+ friend class HttpRequest;
+
+ // Used internally to describe what operations are allowed
+ // on each policy option.
+ struct OptionDescriptor
+ {
+ bool mIsLong;
+ bool mIsDynamic;
+ bool mIsGlobal;
+ bool mIsClass;
+ };
+
+ HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+ long * ret_value);
+ HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+ std::string * ret_value);
+ HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+ long value, long * ret_value);
+ HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+ const std::string & value, std::string * ret_value);
protected:
+ static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST];
static HttpService * sInstance;
// === shared data ===
@@ -210,13 +222,13 @@ protected:
LLAtomicU32 mExitRequested;
LLCoreInt::HttpThread * mThread;
- // === consumer-thread-only data ===
- HttpPolicyGlobal mPolicyGlobal;
- std::vector<HttpPolicyClass> mPolicyClasses;
-
// === working-thread-only data ===
HttpPolicy * mPolicy; // Simple pointer, has ownership
HttpLibcurl * mTransport; // Simple pointer, has ownership
+
+ // === main-thread-only data ===
+ HttpRequest::policy_t mLastPolicy;
+
}; // end class HttpService
} // end namespace LLCore
diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 88c8102b27..73c49687d7 100755
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -236,9 +236,10 @@ int main(int argc, char** argv)
// Initialization
init_curl();
LLCore::HttpRequest::createService();
- LLCore::HttpRequest::setPolicyClassOption(LLCore::HttpRequest::DEFAULT_POLICY_ID,
- LLCore::HttpRequest::CP_CONNECTION_LIMIT,
- concurrency_limit);
+ LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ concurrency_limit,
+ NULL);
LLCore::HttpRequest::startThread();
// Get service point
diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h
index c0d4ec5aad..9db884057f 100755
--- a/indra/llcorehttp/httpcommon.h
+++ b/indra/llcorehttp/httpcommon.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -29,9 +29,9 @@
/// @package LLCore::HTTP
///
-/// This library implements a high-level, Indra-code-free client interface to
-/// HTTP services based on actual patterns found in the viewer and simulator.
-/// Interfaces are similar to those supplied by the legacy classes
+/// This library implements a high-level, Indra-code-free (somewhat) client
+/// interface to HTTP services based on actual patterns found in the viewer
+/// and simulator. Interfaces are similar to those supplied by the legacy classes
/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that
/// allows an application to specify connection behaviors: limits on
/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc.
@@ -52,7 +52,7 @@
/// - "llcorehttp/httprequest.h"
/// - "llcorehttp/httpresponse.h"
///
-/// The library is still under early development and particular users
+/// The library is still under development and particular users
/// may need access to internal implementation details that are found
/// in the _*.h header files. But this is a crutch to be avoided if at
/// all possible and probably indicates some interface work is neeeded.
@@ -66,6 +66,8 @@
/// . CRYPTO_set_id_callback(...)
/// - HttpRequest::createService() called to instantiate singletons
/// and support objects.
+/// - HttpRequest::startThread() to kick off the worker thread and
+/// begin servicing requests.
///
/// An HTTP consumer in an application, and an application may have many
/// consumers, does a few things:
@@ -91,10 +93,12 @@
/// objects.
/// - Do completion processing in your onCompletion() method.
///
-/// Code fragments:
-/// Rather than a poorly-maintained example in comments, look in the
-/// example subdirectory which is a minimal yet functional tool to do
-/// GET request performance testing. With four calls:
+/// Code fragments.
+///
+/// Initialization. Rather than a poorly-maintained example in
+/// comments, look in the example subdirectory which is a minimal
+/// yet functional tool to do GET request performance testing.
+/// With four calls:
///
/// init_curl();
/// LLCore::HttpRequest::createService();
@@ -103,7 +107,85 @@
///
/// the program is basically ready to issue requests.
///
-
+/// HttpHandler. Having started life as a non-indra library,
+/// this code broke away from the classic Responder model and
+/// introduced a handler class to represent an interface for
+/// request responses. This is a non-reference-counted entity
+/// which can be used as a base class or a mixin. An instance
+/// of a handler can be used for each request or can be shared
+/// among any number of requests. Your choice but expect to
+/// code something like the following:
+///
+/// class AppHandler : public LLCore::HttpHandler
+/// {
+/// public:
+/// virtual void onCompleted(HttpHandle handle,
+/// HttpResponse * response)
+/// {
+/// ...
+/// }
+/// ...
+/// };
+/// ...
+/// handler = new handler(...);
+///
+///
+/// Issuing requests. Using 'hr' above,
+///
+/// hr->requestGet(HttpRequest::DEFAULT_POLICY_ID,
+/// 0, // Priority, not used yet
+/// url,
+/// NULL, // options
+/// NULL, // additional headers
+/// handler);
+///
+/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID,
+/// the request was successfully issued and there will eventally
+/// be a status delivered to the handler. If invalid is returnedd,
+/// the actual status can be retrieved by calling hr->getStatus().
+///
+/// Completing requests and delivering notifications. Operations
+/// are all performed by the worker thread and will be driven to
+/// completion regardless of caller actions. Notification of
+/// completion (success or failure) is done by calls to
+/// HttpRequest::update() which will invoke handlers for completed
+/// requests:
+///
+/// hr->update(0);
+/// // Callbacks into handler->onCompleted()
+///
+///
+/// Threads.
+///
+/// Threads are supported and used by this library. The various
+/// classes, methods and members are documented with thread
+/// constraints which programmers must follow and which are
+/// defined as follows:
+///
+/// consumer Any thread that has instanced HttpRequest and is
+/// issuing requests. A particular instance can only
+/// be used by one consumer thread but a consumer may
+/// have many instances available to it.
+/// init Special consumer thread, usually the main thread,
+/// involved in setting up the library at startup.
+/// worker Thread used internally by the library to perform
+/// HTTP operations. Consumers will not have to deal
+/// with this thread directly but some APIs are reserved
+/// to it.
+/// any Consumer or worker thread.
+///
+/// For the most part, API users will not have to do much in the
+/// way of ensuring thread safely. However, there is a tremendous
+/// amount of sharing between threads of read-only data. So when
+/// documentation declares that an option or header instance
+/// becomes shared between consumer and worker, the consumer must
+/// not modify the shared object.
+///
+/// Internally, there is almost no thread synchronization. During
+/// normal operations (non-init, non-term), only the request queue
+/// and the multiple reply queues are shared between threads and
+/// only here are mutexes used.
+///
#include "linden_common.h" // Modifies curl/curl.h interfaces
@@ -239,9 +321,10 @@ struct HttpStatus
return *this;
}
- static const type_enum_t EXT_CURL_EASY = 0;
- static const type_enum_t EXT_CURL_MULTI = 1;
- static const type_enum_t LLCORE = 2;
+ static const type_enum_t EXT_CURL_EASY = 0; ///< mStatus is an error from a curl_easy_*() call
+ static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call
+ static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code
+ ///< 100-999 directly represent HTTP status codes
type_enum_t mType;
short mStatus;
diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp
index 9b739a8825..7b1888e3eb 100755
--- a/indra/llcorehttp/httprequest.cpp
+++ b/indra/llcorehttp/httprequest.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * 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
@@ -54,12 +54,8 @@ namespace LLCore
// ====================================
-HttpRequest::policy_t HttpRequest::sNextPolicyID(1);
-
-
HttpRequest::HttpRequest()
- : //HttpHandler(),
- mReplyQueue(NULL),
+ : mReplyQueue(NULL),
mRequestQueue(NULL)
{
mRequestQueue = HttpRequestQueue::instanceOf();
@@ -90,45 +86,91 @@ HttpRequest::~HttpRequest()
// ====================================
-HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value)
+HttpRequest::policy_t HttpRequest::createPolicyClass()
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
- return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+ return 0;
}
- return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
+ return HttpService::instanceOf()->createPolicyClass();
}
-HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value)
+HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+ long value, long * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
- return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
+ return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
-HttpRequest::policy_t HttpRequest::createPolicyClass()
+HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+ const std::string & value, std::string * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
- return 0;
+ return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
- return HttpService::instanceOf()->createPolicyClass();
+ return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
-HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id,
- EClassPolicy opt,
- long value)
+HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
+ long value, HttpHandler * handler)
{
- if (HttpService::RUNNING == HttpService::instanceOf()->getState())
+ HttpStatus status;
+ HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+ HttpOpSetGet * op = new HttpOpSetGet();
+ if (! (status = op->setupSet(opt, pclass, value)))
{
- return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+ op->release();
+ mLastReqStatus = status;
+ return handle;
+ }
+ op->setReplyPath(mReplyQueue, handler);
+ if (! (status = mRequestQueue->addOp(op))) // transfers refcount
+ {
+ op->release();
+ mLastReqStatus = status;
+ return handle;
+ }
+
+ mLastReqStatus = status;
+ handle = static_cast<HttpHandle>(op);
+
+ return handle;
+}
+
+
+HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
+ const std::string & value, HttpHandler * handler)
+{
+ HttpStatus status;
+ HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+ HttpOpSetGet * op = new HttpOpSetGet();
+ if (! (status = op->setupSet(opt, pclass, value)))
+ {
+ op->release();
+ mLastReqStatus = status;
+ return handle;
+ }
+ op->setReplyPath(mReplyQueue, handler);
+ if (! (status = mRequestQueue->addOp(op))) // transfers refcount
+ {
+ op->release();
+ mLastReqStatus = status;
+ return handle;
}
- return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value);
+
+ mLastReqStatus = status;
+ handle = static_cast<HttpHandle>(op);
+
+ return handle;
}
@@ -474,31 +516,6 @@ HttpHandle HttpRequest::requestSpin(int mode)
return handle;
}
-// ====================================
-// Dynamic Policy Methods
-// ====================================
-
-HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler)
-{
- HttpStatus status;
- HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
-
- HttpOpSetGet * op = new HttpOpSetGet();
- op->setupSet(GP_HTTP_PROXY, proxy);
- op->setReplyPath(mReplyQueue, handler);
- if (! (status = mRequestQueue->addOp(op))) // transfers refcount
- {
- op->release();
- mLastReqStatus = status;
- return handle;
- }
-
- mLastReqStatus = status;
- handle = static_cast<HttpHandle>(op);
-
- return handle;
-}
-
} // end namespace LLCore
diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h
index 5000f47d0d..5c54d35a21 100755
--- a/indra/llcorehttp/httprequest.h
+++ b/indra/llcorehttp/httprequest.h
@@ -56,6 +56,9 @@ class BufferArray;
/// The class supports the current HTTP request operations:
///
/// - requestGetByteRange: GET with Range header for a single range of bytes
+/// - requestGet:
+/// - requestPost:
+/// - requestPut:
///
/// Policy Classes
///
@@ -100,9 +103,26 @@ public:
/// Represents a default, catch-all policy class that guarantees
/// eventual service for any HTTP request.
- static const int DEFAULT_POLICY_ID = 0;
+ static const policy_t DEFAULT_POLICY_ID = 0;
+ static const policy_t INVALID_POLICY_ID = 0xFFFFFFFFU;
+ static const policy_t GLOBAL_POLICY_ID = 0xFFFFFFFEU;
- enum EGlobalPolicy
+ /// Create a new policy class into which requests can be made.
+ ///
+ /// All class creation must occur before threads are started and
+ /// transport begins. Policy classes are limited to a small value.
+ /// Currently that limit is the default class + 1.
+ ///
+ /// @return If positive, the policy_id used to reference
+ /// the class in other methods. If 0, requests
+ /// for classes have exceeded internal limits
+ /// or caller has tried to create a class after
+ /// threads have been started. Caller must fallback
+ /// and recover.
+ ///
+ static policy_t createPolicyClass();
+
+ enum EPolicyOption
{
/// Maximum number of connections the library will use to
/// perform operations. This is somewhat soft as the underlying
@@ -113,24 +133,30 @@ public:
/// a somewhat soft value. There may be an additional five
/// connections per policy class depending upon runtime
/// behavior.
- GP_CONNECTION_LIMIT,
+ ///
+ /// Both global and per-class
+ PO_CONNECTION_LIMIT,
+
+ /// Limits the number of connections used for a single
+ /// literal address/port pair within the class.
+ PO_PER_HOST_CONNECTION_LIMIT,
/// String containing a system-appropriate directory name
/// where SSL certs are stored.
- GP_CA_PATH,
+ PO_CA_PATH,
/// String giving a full path to a file containing SSL certs.
- GP_CA_FILE,
+ PO_CA_FILE,
/// String of host/port to use as simple HTTP proxy. This is
/// going to change in the future into something more elaborate
/// that may support richer schemes.
- GP_HTTP_PROXY,
+ PO_HTTP_PROXY,
/// Long value that if non-zero enables the use of the
/// traditional LLProxy code for http/socks5 support. If
- /// enabled, has priority over GP_HTTP_PROXY.
- GP_LLPROXY,
+ // enabled, has priority over GP_HTTP_PROXY.
+ PO_LLPROXY,
/// Long value setting the logging trace level for the
/// library. Possible values are:
@@ -143,57 +169,46 @@ public:
/// These values are also used in the trace modes for
/// individual requests in HttpOptions. Also be aware that
/// tracing tends to impact performance of the viewer.
- GP_TRACE
- };
-
- /// Set a parameter on a global policy option. Calls
- /// made after the start of the servicing thread are
- /// not honored and return an error status.
- ///
- /// @param opt Enum of option to be set.
- /// @param value Desired value of option.
- /// @return Standard status code.
- static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value);
- static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value);
-
- /// Create a new policy class into which requests can be made.
- ///
- /// All class creation must occur before threads are started and
- /// transport begins. Policy classes are limited to a small value.
- /// Currently that limit is the default class + 1.
- ///
- /// @return If positive, the policy_id used to reference
- /// the class in other methods. If 0, requests
- /// for classes have exceeded internal limits
- /// or caller has tried to create a class after
- /// threads have been started. Caller must fallback
- /// and recover.
- ///
- static policy_t createPolicyClass();
-
- enum EClassPolicy
- {
- /// Limits the number of connections used for the class.
- CP_CONNECTION_LIMIT,
-
- /// Limits the number of connections used for a single
- /// literal address/port pair within the class.
- CP_PER_HOST_CONNECTION_LIMIT,
+ PO_TRACE,
/// Suitable requests are allowed to pipeline on their
/// connections when they ask for it.
- CP_ENABLE_PIPELINING
+ PO_ENABLE_PIPELINING,
+
+ PO_LAST // Always at end
};
-
+
+ /// Set a policy option for a global or class parameter at
+ /// startup time (prior to thread start).
+ ///
+ /// @param opt Enum of option to be set.
+ /// @param pclass For class-based options, the policy class ID to
+ /// be changed. For globals, specify GLOBAL_POLICY_ID.
+ /// @param value Desired value of option.
+ /// @param ret_value Pointer to receive effective set value
+ /// if successful. May be NULL if effective
+ /// value not wanted.
+ /// @return Standard status code.
+ static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+ long value, long * ret_value);
+ static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+ const std::string & value, std::string * ret_value);
+
/// Set a parameter on a class-based policy option. Calls
/// made after the start of the servicing thread are
/// not honored and return an error status.
///
- /// @param policy_id ID of class as returned by @see createPolicyClass().
- /// @param opt Enum of option to be set.
- /// @param value Desired value of option.
- /// @return Standard status code.
- static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value);
+ /// @param opt Enum of option to be set.
+ /// @param pclass For class-based options, the policy class ID to
+ /// be changed. Ignored for globals but recommend
+ /// using INVALID_POLICY_ID in this case.
+ /// @param value Desired value of option.
+ /// @return Handle of dynamic request. Use @see getStatus() if
+ /// the returned handle is invalid.
+ HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value,
+ HttpHandler * handler);
+ HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value,
+ HttpHandler * handler);
/// @}
@@ -495,16 +510,6 @@ public:
/// @}
- /// @name DynamicPolicyMethods
- ///
- /// @{
-
- /// Request that a running transport pick up a new proxy setting.
- /// An empty string will indicate no proxy is to be used.
- HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler);
-
- /// @}
-
protected:
void generateNotification(HttpOperation * op);
@@ -526,7 +531,6 @@ private:
/// Must be established before any threading is allowed to
/// start.
///
- static policy_t sNextPolicyID;
/// @}
// End Global State
diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index 27d65f171e..f1b9c02393 100755
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -1213,7 +1213,7 @@ void HttpRequestTestObjectType::test<12>()
HttpRequest::createService();
// Enable tracing
- HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
+ HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
// Start threading early so that thread memory is invariant
// over the test.
@@ -1331,7 +1331,7 @@ void HttpRequestTestObjectType::test<13>()
HttpRequest::createService();
// Enable tracing
- HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
+ HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
// Start threading early so that thread memory is invariant
// over the test.
@@ -2972,6 +2972,142 @@ void HttpRequestTestObjectType::test<21>()
}
+template <> template <>
+void HttpRequestTestObjectType::test<22>()
+{
+ ScopedCurlInit ready;
+
+ set_test_name("HttpRequest GET 503s with 'Retry-After'");
+
+ // This tests mainly that the code doesn't fall over if
+ // various well- and mis-formed Retry-After headers are
+ // sent along with the response. Direct inspection of
+ // the parsing result isn't supported.
+
+ // Handler can be stack-allocated *if* there are no dangling
+ // references to it after completion of this method.
+ // Create before memory record as the string copy will bump numbers.
+ TestHandler2 handler(this, "handler");
+ std::string url_base(get_base_url() + "/503/"); // path to 503 generators
+
+ // record the total amount of dynamically allocated memory
+ mMemTotal = GetMemTotal();
+ mHandlerCalls = 0;
+
+ HttpRequest * req = NULL;
+ HttpOptions * opts = NULL;
+
+ try
+ {
+ // Get singletons created
+ HttpRequest::createService();
+
+ // Start threading early so that thread memory is invariant
+ // over the test.
+ HttpRequest::startThread();
+
+ // create a new ref counted object with an implicit reference
+ req = new HttpRequest();
+ ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
+
+ opts = new HttpOptions();
+ opts->setRetries(1); // Retry once only
+ opts->setUseRetryAfter(true); // Try to parse the retry-after header
+
+ // Issue a GET that 503s with valid retry-after
+ mStatus = HttpStatus(503);
+ int url_limit(6);
+ for (int i(0); i < url_limit; ++i)
+ {
+ std::ostringstream url;
+ url << url_base << i << "/";
+ HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
+ 0U,
+ url.str(),
+ 0,
+ 0,
+ opts,
+ NULL,
+ &handler);
+
+ std::ostringstream testtag;
+ testtag << "Valid handle returned for 503 request #" << i;
+ ensure(testtag.str(), handle != LLCORE_HTTP_HANDLE_INVALID);
+ }
+
+
+ // Run the notification pump.
+ int count(0);
+ int limit(300); // One retry but several seconds needed
+ while (count++ < limit && mHandlerCalls < url_limit)
+ {
+ req->update(0);
+ usleep(100000);
+ }
+ ensure("Request executed in reasonable time", count < limit);
+ ensure("One handler invocation for request", mHandlerCalls == url_limit);
+
+ // Okay, request a shutdown of the servicing thread
+ mStatus = HttpStatus();
+ mHandlerCalls = 0;
+ HttpHandle handle = req->requestStopThread(&handler);
+ ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
+
+ // Run the notification pump again
+ count = 0;
+ limit = 100;
+ while (count++ < limit && mHandlerCalls < 1)
+ {
+ req->update(1000000);
+ usleep(100000);
+ }
+ ensure("Second request executed in reasonable time", count < limit);
+ ensure("Second handler invocation", mHandlerCalls == 1);
+
+ // See that we actually shutdown the thread
+ count = 0;
+ limit = 10;
+ while (count++ < limit && ! HttpService::isStopped())
+ {
+ usleep(100000);
+ }
+ ensure("Thread actually stopped running", HttpService::isStopped());
+
+ // release options
+ opts->release();
+ opts = NULL;
+
+ // release the request object
+ delete req;
+ req = NULL;
+
+ // Shut down service
+ HttpRequest::destroyService();
+
+#if defined(WIN32)
+ // Can only do this memory test on Windows. On other platforms,
+ // the LL logging system holds on to memory and produces what looks
+ // like memory leaks...
+
+ // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
+ ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
+#endif
+ }
+ catch (...)
+ {
+ stop_thread(req);
+ if (opts)
+ {
+ opts->release();
+ opts = NULL;
+ }
+ delete req;
+ HttpRequest::destroyService();
+ throw;
+ }
+}
+
+
} // end namespace tut
namespace
diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py
index 75a3c39ef2..f6c4d1a820 100755
--- a/indra/llcorehttp/tests/test_llcorehttp_peer.py
+++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py
@@ -9,7 +9,7 @@
$LicenseInfo:firstyear=2008&license=viewerlgpl$
Second Life Viewer Source Code
-Copyright (C) 2012, Linden Research, Inc.
+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
@@ -47,6 +47,17 @@ from testrunner import freeport, run, debug, VERBOSE
class TestHTTPRequestHandler(BaseHTTPRequestHandler):
"""This subclass of BaseHTTPRequestHandler is to receive and echo
LLSD-flavored messages sent by the C++ LLHTTPClient.
+
+ [Merge with viewer-cat later]
+ - '/503/' Generate 503 responses with various kinds
+ of 'retry-after' headers
+ -- '/503/0/' "Retry-After: 2"
+ -- '/503/1/' "Retry-After: Thu, 31 Dec 2043 23:59:59 GMT"
+ -- '/503/2/' "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT"
+ -- '/503/3/' "Retry-After: "
+ -- '/503/4/' "Retry-After: (*#*(@*(@(")"
+ -- '/503/5/' "Retry-After: aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo"
+ -- '/503/6/' "Retry-After: 1 2 3 4 5 6 7 8 9 10"
"""
def read(self):
# The following logic is adapted from the library module
@@ -107,7 +118,41 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
if "/sleep/" in self.path:
time.sleep(30)
- if "fail" not in self.path:
+ if "/503/" in self.path:
+ # Tests for various kinds of 'Retry-After' header parsing
+ body = None
+ if "/503/0/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "2")
+ elif "/503/1/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "Thu, 31 Dec 2043 23:59:59 GMT")
+ elif "/503/2/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "Fri, 31 Dec 1999 23:59:59 GMT")
+ elif "/503/3/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "")
+ elif "/503/4/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "(*#*(@*(@(")
+ elif "/503/5/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo")
+ elif "/503/6/" in self.path:
+ self.send_response(503)
+ self.send_header("retry-after", "1 2 3 4 5 6 7 8 9 10")
+ else:
+ # Unknown request
+ self.send_response(400)
+ body = "Unknown /503/ path in server"
+ if "/reflect/" in self.path:
+ self.reflect_headers()
+ self.send_header("Content-type", "text/plain")
+ self.end_headers()
+ if body:
+ self.wfile.write(body)
+ elif "fail" not in self.path:
data = data.copy() # we're going to modify
# Ensure there's a "reply" key in data, even if there wasn't before
data["reply"] = data.get("reply", llsd.LLSD("success"))
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 0c242e57db..104debe023 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -105,8 +105,9 @@ void LLAppCoreHttp::init()
}
// Point to our certs or SSH/https: will fail on connect
- status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE,
- gDirUtilp->getCAFile());
+ 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()
@@ -114,7 +115,9 @@ void LLAppCoreHttp::init()
}
// Establish HTTP Proxy, if desired.
- status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1);
+ 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()
@@ -131,7 +134,9 @@ void LLAppCoreHttp::init()
{
long trace_level(0L);
trace_level = long(gSavedSettings.getU32(http_trace));
- status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level);
+ status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE,
+ LLCore::HttpRequest::GLOBAL_POLICY_ID,
+ trace_level, NULL);
}
// Setup default policy and constrain if directed to
@@ -164,6 +169,9 @@ void LLAppCoreHttp::init()
}
}
+ // Need a request object to handle dynamic options before setting them
+ mRequest = new LLCore::HttpRequest;
+
// Apply initial settings
refreshSettings(true);
@@ -175,8 +183,6 @@ void LLAppCoreHttp::init()
<< LL_ENDL;
}
- mRequest = new LLCore::HttpRequest;
-
// Register signals for settings and state changes
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
@@ -287,12 +293,13 @@ void LLAppCoreHttp::refreshSettings(bool initial)
// Set it and report
// *TODO: These are intended to be per-host limits when we can
// support that in llcorehttp/libcurl.
- LLCore::HttpStatus status;
- status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy],
- LLCore::HttpRequest::CP_CONNECTION_LIMIT,
- setting);
- if (! status)
+ LLCore::HttpHandle handle;
+ handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
+ mPolicies[policy],
+ setting, NULL);
+ if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
+ LLCore::HttpStatus status(mRequest->getStatus());
LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
<< " concurrency. Reason: " << status.toString()
<< LL_ENDL;