HTTP Proxy, PUT & POST, unit tests and refactoring.

Implemented/modified PUT & POST to not used chunked encoding for the request.
Made the unit test much happier and probably a better thing for the pipeline.
Have a cheesy static & dynamic proxy capability using both local options and
a way to wire into LLProxy in llmessages.  Not a clean thing but it will get
the proxy path working with both socks5 & http proxies.  Refactoring to get
rid of unneeded library handler and unified an HttpStatus return for all
requests.  Big batch of code removed as a result of that and more is possible
as well as some syscall avoidance with a bit more work.  Boosted the unit
tests for simple PUT & POST test which revealed the test harness does *not*
like chunked encoding so we'll avoid it for now (and don't really need it
in any of our schemes).
master
Monty Brandenberg 2012-06-12 17:42:33 -04:00
parent 24e16e1632
commit 7adeb39237
24 changed files with 703 additions and 164 deletions

View File

@ -10,12 +10,14 @@ include(OpenSSL)
include(ZLIB)
include(LLCoreHttp)
include(LLAddBuildTest)
include(LLMessage)
include(LLCommon)
include(Tut)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
include_directories(
${LLMESSAGE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLCOREHTTP_INCLUDE_DIRS}
)
@ -31,6 +33,7 @@ set(llcorehttp_SOURCE_FILES
_httpopcancel.cpp
_httpoperation.cpp
_httpoprequest.cpp
_httpopsetget.cpp
_httpopsetpriority.cpp
_httppolicy.cpp
_httppolicyglobal.cpp
@ -54,6 +57,7 @@ set(llcorehttp_HEADER_FILES
_httpopcancel.h
_httpoperation.h
_httpoprequest.h
_httpopsetget.h
_httpopsetpriority.h
_httppolicy.h
_httppolicyglobal.h
@ -113,6 +117,7 @@ if (LL_TESTS)
set(test_libs
${LLCOREHTTP_LIBRARIES}
${WINDOWS_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${CURL_LIBRARIES}

View File

@ -66,17 +66,6 @@ void HttpOpCancel::stageFromRequest(HttpService * service)
}
void HttpOpCancel::visitNotifier(HttpRequest * request)
{
if (mLibraryHandler)
{
HttpResponse * response = new HttpResponse();
mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response);
response->release();
}
}
} // end namespace LLCore

View File

@ -59,13 +59,10 @@ private:
public:
virtual void stageFromRequest(HttpService *);
virtual void visitNotifier(HttpRequest * request);
public:
// Request data
HttpHandle mHandle;
}; // end class HttpOpCancel

View File

@ -47,7 +47,6 @@ namespace LLCore
HttpOperation::HttpOperation()
: LLCoreInt::RefCounted(true),
mReplyQueue(NULL),
mLibraryHandler(NULL),
mUserHandler(NULL),
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
mReqPriority(0U)
@ -57,13 +56,12 @@ HttpOperation::HttpOperation()
HttpOperation::~HttpOperation()
{
setHandlers(NULL, NULL, NULL);
setReplyPath(NULL, NULL);
}
void HttpOperation::setHandlers(HttpReplyQueue * reply_queue,
HttpHandler * lib_handler,
HttpHandler * user_handler)
void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue,
HttpHandler * user_handler)
{
if (reply_queue != mReplyQueue)
{
@ -80,9 +78,6 @@ void HttpOperation::setHandlers(HttpReplyQueue * reply_queue,
mReplyQueue = reply_queue;
}
// Not refcounted
mLibraryHandler = lib_handler;
// Not refcounted
mUserHandler = user_handler;
}
@ -121,11 +116,12 @@ void HttpOperation::stageFromActive(HttpService *)
void HttpOperation::visitNotifier(HttpRequest *)
{
if (mLibraryHandler)
if (mUserHandler)
{
HttpResponse * response = new HttpResponse();
mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response);
response->setStatus(mStatus);
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
response->release();
}
@ -142,7 +138,7 @@ HttpStatus HttpOperation::cancel()
void HttpOperation::addAsReply()
{
if (mReplyQueue && mLibraryHandler)
if (mReplyQueue)
{
addRef();
mReplyQueue->addOp(this);

View File

@ -80,9 +80,8 @@ private:
void operator=(const HttpOperation &); // Not defined
public:
void setHandlers(HttpReplyQueue * reply_queue,
HttpHandler * lib_handler,
HttpHandler * user_handler);
void setReplyPath(HttpReplyQueue * reply_queue,
HttpHandler * handler);
HttpHandler * getUserHandler() const
{
@ -102,13 +101,15 @@ protected:
protected:
HttpReplyQueue * mReplyQueue; // Have refcount
HttpHandler * mLibraryHandler; // Have refcount
HttpHandler * mUserHandler; // Have refcount
HttpHandler * mUserHandler;
public:
// Request Data
HttpRequest::policy_t mReqPolicy;
HttpRequest::priority_t mReqPriority;
// Reply Data
HttpStatus mStatus;
}; // end class HttpOperation

View File

@ -44,6 +44,7 @@
#include "_httplibcurl.h"
#include "llhttpstatuscodes.h"
#include "llproxy.h"
namespace
{
@ -207,7 +208,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
{
static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS);
if (mLibraryHandler)
if (mUserHandler)
{
HttpResponse * response = new HttpResponse();
response->setStatus(mStatus);
@ -219,7 +220,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
response->setRange(mReplyOffset, mReplyLength);
}
mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response);
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
response->release();
}
@ -304,6 +305,39 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id,
}
HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
{
HttpStatus status;
mProcFlags = 0;
mReqPolicy = policy_id;
mReqPriority = priority;
mReqMethod = HOR_PUT;
mReqURL = url;
if (body)
{
body->addRef();
mReqBody = body;
}
if (headers && ! mReqHeaders)
{
headers->addRef();
mReqHeaders = headers;
}
if (options && ! mReqOptions)
{
mReqOptions = new HttpOptions(*options);
}
return status;
}
HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
{
// Scrub transport and result data for retried op case
@ -346,8 +380,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
// *FIXME: Need to deal with proxy setup...
// curl_easy_setopt(handle, CURLOPT_PROXY, "");
// *FIXME: Revisit this old DNS timeout setting - may no longer be valid
curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
@ -361,18 +393,31 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
std::string opt_value;
const std::string * opt_value(NULL);
if (policy.get(HttpRequest::GP_CA_PATH, opt_value))
{
curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value.c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
}
if (policy.get(HttpRequest::GP_CA_FILE, opt_value))
{
curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value.c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
}
if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value))
{
curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value.c_str());
if (*opt_value == "LLProxy")
{
// 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
{
// *TODO: This is fine for now but get fuller socks/
// authentication thing going later....
curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
}
}
switch (mReqMethod)
@ -394,7 +439,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
}
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
}
break;
@ -409,7 +453,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
}
curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");

View File

@ -91,6 +91,13 @@ public:
HttpOptions * options,
HttpHeaders * headers);
HttpStatus setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers);
HttpStatus prepareRequest(HttpService * service);
virtual HttpStatus cancel();

View File

@ -0,0 +1,105 @@
/**
* @file _httpopsetget.cpp
* @brief Definitions for internal class HttpOpSetGet
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 "_httpopsetget.h"
#include <cstdio>
#include <algorithm>
#include "httpcommon.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "_httprequestqueue.h"
#include "_httpreplyqueue.h"
#include "_httpservice.h"
#include "_httppolicy.h"
#include "_httplibcurl.h"
namespace LLCore
{
// ==================================
// HttpOpSetget
// ==================================
HttpOpSetGet::HttpOpSetGet()
: HttpOperation(),
mIsGlobal(false),
mDoSet(false),
mSetting(-1), // Nothing requested
mLongValue(0L)
{}
HttpOpSetGet::~HttpOpSetGet()
{}
void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting)
{
mIsGlobal = true;
mSetting = setting;
}
void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value)
{
mIsGlobal = true;
mDoSet = true;
mSetting = setting;
mStrValue = value;
}
void HttpOpSetGet::stageFromRequest(HttpService * service)
{
HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions());
HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting));
if (mDoSet)
{
mStatus = pol_opt.set(setting, mStrValue);
}
if (mStatus)
{
const std::string * value;
if ((mStatus = pol_opt.get(setting, value)))
{
mStrValue = *value;
}
}
addAsReply();
}
} // end namespace LLCore

View File

@ -0,0 +1,78 @@
/**
* @file _httpopsetget.h
* @brief Internal declarations for the HttpOpSetGet subclass
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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$
*/
#ifndef _LLCORE_HTTP_OPSETGET_H_
#define _LLCORE_HTTP_OPSETGET_H_
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "httpcommon.h"
#include <curl/curl.h>
#include "_httpoperation.h"
#include "_refcounted.h"
namespace LLCore
{
/// HttpOpSetGet requests dynamic changes to policy and
/// configuration settings.
class HttpOpSetGet : public HttpOperation
{
public:
HttpOpSetGet();
virtual ~HttpOpSetGet();
private:
HttpOpSetGet(const HttpOpSetGet &); // Not defined
void operator=(const HttpOpSetGet &); // Not defined
public:
void setupGet(HttpRequest::EGlobalPolicy setting);
void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value);
virtual void stageFromRequest(HttpService *);
public:
// Request data
bool mIsGlobal;
bool mDoSet;
int mSetting;
long mLongValue;
std::string mStrValue;
}; // end class HttpOpSetGet
} // end namespace LLCore
#endif // _LLCORE_HTTP_OPSETGET_H_

View File

@ -60,18 +60,4 @@ void HttpOpSetPriority::stageFromRequest(HttpService * service)
}
void HttpOpSetPriority::visitNotifier(HttpRequest * request)
{
if (mLibraryHandler)
{
HttpResponse * response = new HttpResponse();
response->setStatus(mStatus);
mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response);
response->release();
}
}
} // end namespace LLCore

View File

@ -56,10 +56,8 @@ private:
public:
virtual void stageFromRequest(HttpService *);
virtual void visitNotifier(HttpRequest * request);
protected:
HttpStatus mStatus;
// Request Data
HttpHandle mHandle;
HttpRequest::priority_t mPriority;
}; // end class HttpOpSetPriority

View File

@ -71,6 +71,12 @@ HttpPolicy::~HttpPolicy()
}
void HttpPolicy::setPolicies(const HttpPolicyGlobal & global)
{
mGlobalOptions = global;
}
void HttpPolicy::addOp(HttpOpRequest * op)
{
const int policy_class(op->mReqPolicy);

View File

@ -95,7 +95,9 @@ public:
{
return mGlobalOptions;
}
void setPolicies(const HttpPolicyGlobal & global);
protected:
struct State
{

View File

@ -32,7 +32,7 @@ namespace LLCore
HttpPolicyGlobal::HttpPolicyGlobal()
: mValidMask(0UL),
: mSetMask(0UL),
mConnectionLimit(32L)
{}
@ -41,6 +41,20 @@ HttpPolicyGlobal::~HttpPolicyGlobal()
{}
HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
{
if (this != &other)
{
mSetMask = other.mSetMask;
mConnectionLimit = other.mConnectionLimit;
mCAPath = other.mCAPath;
mCAFile = other.mCAFile;
mHttpProxy = other.mHttpProxy;
}
return *this;
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
{
switch (opt)
@ -53,7 +67,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
mValidMask |= 1UL << int(opt);
mSetMask |= 1UL << int(opt);
return HttpStatus();
}
@ -78,7 +92,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
mValidMask |= 1UL << int(opt);
mSetMask |= 1UL << int(opt);
return HttpStatus();
}
@ -90,7 +104,7 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value)
switch (opt)
{
case HttpRequest::GP_CONNECTION_LIMIT:
if (! (mValidMask & (1UL << int(opt))))
if (! (mSetMask & (1UL << int(opt))))
return not_set;
value = mConnectionLimit;
break;
@ -103,28 +117,28 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value)
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, std::string & value)
HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value)
{
static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
switch (opt)
{
case HttpRequest::GP_CA_PATH:
if (! (mValidMask & (1UL << int(opt))))
if (! (mSetMask & (1UL << int(opt))))
return not_set;
value = mCAPath;
value = &mCAPath;
break;
case HttpRequest::GP_CA_FILE:
if (! (mValidMask & (1UL << int(opt))))
if (! (mSetMask & (1UL << int(opt))))
return not_set;
value = mCAFile;
value = &mCAFile;
break;
case HttpRequest::GP_HTTP_PROXY:
if (! (mValidMask & (1UL << int(opt))))
if (! (mSetMask & (1UL << int(opt))))
return not_set;
value = mHttpProxy;
value = &mHttpProxy;
break;
default:

View File

@ -40,18 +40,19 @@ public:
HttpPolicyGlobal();
~HttpPolicyGlobal();
HttpPolicyGlobal & operator=(const HttpPolicyGlobal &);
private:
HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined
void operator=(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, std::string & value);
HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value);
public:
unsigned long mValidMask;
unsigned long mSetMask;
long mConnectionLimit;
std::string mCAPath;
std::string mCAFile;

View File

@ -79,11 +79,8 @@ HttpService::~HttpService()
mTransport = NULL;
}
if (mPolicy)
{
delete mPolicy;
mPolicy = NULL;
}
delete mPolicy;
mPolicy = NULL;
if (mThread)
{
@ -145,6 +142,10 @@ void HttpService::startThread()
{
mThread->release();
}
// Push current policy definitions
mPolicy->setPolicies(mPolicyGlobal);
mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
mThread->addRef(); // Need an explicit reference, implicit one is used internally
sState = RUNNING;

View File

@ -30,6 +30,7 @@
#include "httpcommon.h"
#include "httprequest.h"
#include "_httppolicyglobal.h"
namespace LLCoreInt
@ -157,6 +158,11 @@ public:
{
return *mTransport;
}
HttpPolicyGlobal & getGlobalOptions()
{
return mPolicyGlobal;
}
protected:
void threadRun(LLCoreInt::HttpThread * thread);
@ -173,11 +179,11 @@ protected:
// === calling-thread-only data ===
LLCoreInt::HttpThread * mThread;
HttpPolicyGlobal mPolicyGlobal;
// === working-thread-only data ===
HttpPolicy * mPolicy; // Simple pointer, has ownership
HttpLibcurl * mTransport; // Simple pointer, has ownership
}; // end class HttpService
} // end namespace LLCore

View File

@ -34,6 +34,7 @@
#include "_httpoprequest.h"
#include "_httpopsetpriority.h"
#include "_httpopcancel.h"
#include "_httpopsetget.h"
#include "lltimer.h"
@ -48,39 +49,6 @@ bool has_inited(false);
namespace LLCore
{
// ====================================
// InternalHandler Implementation
// ====================================
class HttpRequest::InternalHandler : public HttpHandler
{
public:
InternalHandler(HttpRequest & request)
: mRequest(request)
{}
protected:
InternalHandler(const InternalHandler &); // Not defined
void operator=(const InternalHandler &); // Not defined
public:
void onCompleted(HttpHandle handle, HttpResponse * response)
{
HttpOperation * op(static_cast<HttpOperation *>(handle));
HttpHandler * user_handler(op->getUserHandler());
if (user_handler)
{
user_handler->onCompleted(handle, response);
}
}
protected:
HttpRequest & mRequest;
}; // end class HttpRequest::InternalHandler
// ====================================
// HttpRequest Implementation
// ====================================
@ -92,15 +60,12 @@ HttpRequest::policy_t HttpRequest::sNextPolicyID(1);
HttpRequest::HttpRequest()
: //HttpHandler(),
mReplyQueue(NULL),
mRequestQueue(NULL),
mSelfHandler(NULL)
mRequestQueue(NULL)
{
mRequestQueue = HttpRequestQueue::instanceOf();
mRequestQueue->addRef();
mReplyQueue = new HttpReplyQueue();
mSelfHandler = new InternalHandler(*this);
}
@ -117,9 +82,6 @@ HttpRequest::~HttpRequest()
mReplyQueue->release();
mReplyQueue = NULL;
}
delete mSelfHandler;
mSelfHandler = NULL;
}
@ -132,7 +94,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value)
{
// *FIXME: Fail if thread is running.
return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value);
return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
}
@ -140,7 +102,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri
{
// *FIXME: Fail if thread is running.
return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value);
return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
}
@ -192,7 +154,7 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
mLastReqStatus = status;
return handle;
}
op->setHandlers(mReplyQueue, mSelfHandler, user_handler);
op->setReplyPath(mReplyQueue, user_handler);
mRequestQueue->addOp(op); // transfers refcount
mLastReqStatus = status;
@ -220,7 +182,7 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id,
mLastReqStatus = status;
return handle;
}
op->setHandlers(mReplyQueue, mSelfHandler, user_handler);
op->setReplyPath(mReplyQueue, user_handler);
mRequestQueue->addOp(op); // transfers refcount
mLastReqStatus = status;
@ -230,19 +192,31 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id,
}
HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler)
HttpHandle HttpRequest::requestPut(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * user_handler)
{
HttpStatus status;
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpCancel * op = new HttpOpCancel(handle);
op->setHandlers(mReplyQueue, mSelfHandler, user_handler);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;
ret_handle = static_cast<HttpHandle>(op);
HttpOpRequest * op = new HttpOpRequest();
if (! (status = op->setupPut(policy_id, priority, url, body, options, headers)))
{
op->release();
mLastReqStatus = status;
return handle;
}
op->setReplyPath(mReplyQueue, user_handler);
mRequestQueue->addOp(op); // transfers refcount
return ret_handle;
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
}
@ -252,7 +226,7 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler)
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpNull * op = new HttpOpNull();
op->setHandlers(mReplyQueue, mSelfHandler, user_handler);
op->setReplyPath(mReplyQueue, user_handler);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;
@ -262,23 +236,6 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler)
}
HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority,
HttpHandler * handler)
{
HttpStatus status;
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetPriority * op = new HttpOpSetPriority(request, priority);
op->setHandlers(mReplyQueue, mSelfHandler, handler);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;
ret_handle = static_cast<HttpHandle>(op);
return ret_handle;
}
HttpStatus HttpRequest::update(long millis)
{
const HttpTime limit(totalTime() + (1000 * HttpTime(millis)));
@ -302,6 +259,38 @@ HttpStatus HttpRequest::update(long millis)
// Request Management Methods
// ====================================
HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler)
{
HttpStatus status;
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpCancel * op = new HttpOpCancel(handle);
op->setReplyPath(mReplyQueue, user_handler);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;
ret_handle = static_cast<HttpHandle>(op);
return ret_handle;
}
HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority,
HttpHandler * handler)
{
HttpStatus status;
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetPriority * op = new HttpOpSetPriority(request, priority);
op->setReplyPath(mReplyQueue, handler);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;
ret_handle = static_cast<HttpHandle>(op);
return ret_handle;
}
// ====================================
// Utility Methods
@ -350,7 +339,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler)
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpStop * op = new HttpOpStop();
op->setHandlers(mReplyQueue, mSelfHandler, user_handler);
op->setReplyPath(mReplyQueue, user_handler);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
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);
mRequestQueue->addOp(op); // transfer refcount as well
mLastReqStatus = status;

View File

@ -124,8 +124,8 @@ public:
/// @param opt Enum of option to be set.
/// @param value Desired value of option.
/// @return Standard status code.
HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value);
HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value);
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.
///
@ -236,6 +236,32 @@ public:
HttpHandler * handler);
///
/// @param policy_id Default or user-defined policy class under
/// which this request is to be serviced.
/// @param priority Standard priority scheme inherited from
/// Indra code base.
/// @param url
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
/// to the content.
/// @param options (optional)
/// @param headers (optional)
/// @param handler (optional)
/// @return The handle of the request if successfully
/// queued or LLCORE_HTTP_HANDLE_INVALID if the
/// request could not be queued. In the latter
/// case, @see getStatus() will return more info.
///
HttpHandle requestPut(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * handler);
/// Queue a NoOp request.
/// The request is queued and serviced by the working thread which
/// immediately processes it and returns the request to the reply
@ -325,13 +351,20 @@ public:
HttpHandle requestStopThread(HttpHandler * handler);
/// @}
/// @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);
class InternalHandler;
friend class InternalHandler;
private:
/// @name InstanceData
///
@ -339,7 +372,6 @@ private:
HttpStatus mLastReqStatus;
HttpReplyQueue * mReplyQueue;
HttpRequestQueue * mRequestQueue;
InternalHandler * mSelfHandler;
/// @}

View File

@ -44,6 +44,8 @@
#include "test_bufferarray.hpp"
#include "test_httprequestqueue.hpp"
#include "llproxy.h"
unsigned long ssl_thread_id_callback(void);
void ssl_locking_callback(int mode, int type, const char * file, int line);
@ -91,11 +93,15 @@ void init_curl()
CRYPTO_set_locking_callback(ssl_locking_callback);
CRYPTO_set_id_callback(ssl_thread_id_callback);
}
LLProxy::getInstance();
}
void term_curl()
{
LLProxy::cleanupClass();
CRYPTO_set_locking_callback(NULL);
for (int i(0); i < ssl_mutex_count; ++i)
{

View File

@ -97,13 +97,12 @@ namespace tut
// Get some handlers
TestHandler * h1 = new TestHandler();
TestHandler * h2 = new TestHandler();
// create a new ref counted object with an implicit reference
HttpOpNull * op = new HttpOpNull();
// Add the handlers
op->setHandlers(NULL, h1, h2);
op->setReplyPath(NULL, h1);
// Check ref count
ensure(op->getRefCount() == 1);
@ -117,8 +116,6 @@ namespace tut
// release the handlers
delete h1;
h1 = NULL;
delete h2;
h2 = NULL;
ensure(mMemTotal == GetMemTotal());
}

View File

@ -27,6 +27,7 @@
#define TEST_LLCORE_HTTP_REQUEST_H_
#include "httprequest.h"
#include "bufferarray.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "_httpservice.h"
@ -604,6 +605,244 @@ void HttpRequestTestObjectType::test<6>()
}
}
template <> template <>
void HttpRequestTestObjectType::test<7>()
{
ScopedCurlInit ready;
std::string url_base(get_base_url());
std::cerr << "Base: " << url_base << std::endl;
set_test_name("HttpRequest PUT to real service");
// 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");
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
BufferArray * body = new BufferArray;
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());
// Issue a GET that *can* connect
static const char * body_text("Now is the time for all good men...");
body->append(body_text, strlen(body_text));
mStatus = HttpStatus(200);
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
body,
NULL,
NULL,
&handler);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
int count(0);
int limit(10);
while (count++ < limit && mHandlerCalls < 1)
{
req->update(1000);
usleep(100000);
}
ensure("Request executed in reasonable time", count < limit);
ensure("One handler invocation for request", mHandlerCalls == 1);
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
handle = req->requestStopThread(&handler);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
count = 0;
limit = 10;
while (count++ < limit && mHandlerCalls < 2)
{
req->update(1000);
usleep(100000);
}
ensure("Second request executed in reasonable time", count < limit);
ensure("Second handler invocation", mHandlerCalls == 2);
// 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());
// Lose the request body
body->release();
body = NULL;
// release the request object
delete req;
req = NULL;
// Shut down service
HttpRequest::destroyService();
ensure("Two handler calls on the way out", 2 == mHandlerCalls);
#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 (...)
{
if (body)
{
body->release();
}
stop_thread(req);
delete req;
HttpRequest::destroyService();
throw;
}
}
template <> template <>
void HttpRequestTestObjectType::test<8>()
{
ScopedCurlInit ready;
std::string url_base(get_base_url());
std::cerr << "Base: " << url_base << std::endl;
set_test_name("HttpRequest POST to real service");
// 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");
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
BufferArray * body = new BufferArray;
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());
// Issue a GET that *can* connect
static const char * body_text("Now is the time for all good men...");
body->append(body_text, strlen(body_text));
mStatus = HttpStatus(200);
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
body,
NULL,
NULL,
&handler);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
int count(0);
int limit(10);
while (count++ < limit && mHandlerCalls < 1)
{
req->update(1000);
usleep(100000);
}
ensure("Request executed in reasonable time", count < limit);
ensure("One handler invocation for request", mHandlerCalls == 1);
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
handle = req->requestStopThread(&handler);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
count = 0;
limit = 10;
while (count++ < limit && mHandlerCalls < 2)
{
req->update(1000);
usleep(100000);
}
ensure("Second request executed in reasonable time", count < limit);
ensure("Second handler invocation", mHandlerCalls == 2);
// 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());
// Lose the request body
body->release();
body = NULL;
// release the request object
delete req;
req = NULL;
// Shut down service
HttpRequest::destroyService();
ensure("Two handler calls on the way out", 2 == mHandlerCalls);
#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 (...)
{
if (body)
{
body->release();
}
stop_thread(req);
delete req;
HttpRequest::destroyService();
throw;
}
}
} // end namespace tut
namespace

View File

@ -85,7 +85,15 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
# Read the provided POST data.
self.answer(self.read())
# self.answer(self.read())
self.answer(dict(reply="success", status=200,
reason=self.read()))
def do_PUT(self):
# Read the provided PUT data.
# self.answer(self.read())
self.answer(dict(reply="success", status=200,
reason=self.read()))
def answer(self, data):
debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path)

View File

@ -5358,6 +5358,7 @@ void CoreHttp::init()
mRequest = new LLCore::HttpRequest;
// Point to our certs or SSH/https: will fail on connect
status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE,
gDirUtilp->getCAFile());
if (! status)
@ -5366,6 +5367,18 @@ void CoreHttp::init()
<< status.toString()
<< LL_ENDL;
}
// Establish HTTP Proxy. "LLProxy" is a special string which directs
// the code to use LLProxy::applyProxySettings() to establish any
// HTTP or SOCKS proxy for http operations.
status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY,
std::string("LLProxy"));
if (! status)
{
LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: "
<< status.toString()
<< LL_ENDL;
}
status = LLCore::HttpRequest::startThread();
if (! status)