/**
 * @file test_httprequest.hpp
 * @brief unit tests for the LLCore::HttpRequest class
 *
 * $LicenseInfo:firstyear=2012&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012-2013, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */
#ifndef TEST_LLCORE_HTTP_REQUEST_H_
#define TEST_LLCORE_HTTP_REQUEST_H_

#include "httprequest.h"
#include "bufferarray.h"
#include "httphandler.h"
#include "httpheaders.h"
#include "httpresponse.h"
#include "httpoptions.h"
#include "_httpservice.h"
#include "_httprequestqueue.h"

#include <curl/curl.h>
#include <boost/regex.hpp>
#include <sstream>

#include "llcorehttp_test.h"


using namespace LLCoreInt;

// spin/sleep waiting times for client/server exchange tests
//
// These are now fairly generous to try to get around timeout
// ('reasonable time') failures during execution on a heavily-
// loaded system where the unit test is in competition with
// other programs.
static const int LOOP_SLEEP_INTERVAL(10000);
static const int LOOP_COUNT_SHORT(500);         // 5-second dwell time
static const int LOOP_COUNT_LONG(3000);         // 30-second dwell time

namespace
{

#if defined(WIN32)

void usleep(unsigned long usec);

#endif

}

namespace tut
{

typedef std::vector<std::pair<boost::regex, boost::regex> > regex_container_t;

struct HttpRequestTestData
{
    // the test objects inherit from this so the member functions and variables
    // can be referenced directly inside of the test functions.
    int             mHandlerCalls;
    HttpStatus      mStatus;
};

class TestHandler2 : public LLCore::HttpHandler
{
public:
    TestHandler2(HttpRequestTestData * state,
                 const std::string & name)
        : mState(state),
          mName(name),
          mExpectHandle(LLCORE_HTTP_HANDLE_INVALID)
        {}

    virtual void onCompleted(HttpHandle handle, HttpResponse * response)
        {
            if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle)
            {
                ensure("Expected handle received in handler", mExpectHandle == handle);
            }
            ensure("Handler got a response", NULL != response);
            if (response && mState)
            {
                const HttpStatus actual_status(response->getStatus());
                std::ostringstream test;
                test << "Expected HttpStatus received in response.  Wanted:  "
                     << mState->mStatus.toHex() << " Received:  " << actual_status.toHex();
                ensure(test.str().c_str(), actual_status == mState->mStatus);
            }
            if (mState)
            {
                mState->mHandlerCalls++;
            }
            if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty())
            {
                ensure("Response required with header check", response != NULL);
                HttpHeaders::ptr_t header(response->getHeaders());  // Will not hold onto this
                ensure("Some quantity of headers returned", header != NULL);

                if (! mHeadersRequired.empty())
                {
                    for (int i(0); i < mHeadersRequired.size(); ++i)
                    {
                        bool found = false;
                        for (HttpHeaders::const_iterator iter(header->begin());
                             header->end() != iter;
                             ++iter)
                        {
                            // std::cerr << "Header: " << (*iter).first
                            //        << ": " << (*iter).second << std::endl;

                            if (boost::regex_match((*iter).first,
                                                   mHeadersRequired[i].first) &&
                                boost::regex_match((*iter).second,
                                                   mHeadersRequired[i].second))
                            {
                                found = true;
                                break;
                            }
                        }
                        std::ostringstream str;
                        str << "Required header #" << i << " "
                            << mHeadersRequired[i].first << "=" << mHeadersRequired[i].second
                            << " not found in response";
                        ensure(str.str(), found);
                    }
                }

                if (! mHeadersDisallowed.empty())
                {
                    for (int i(0); i < mHeadersDisallowed.size(); ++i)
                    {
                        for (HttpHeaders::const_iterator iter(header->begin());
                             header->end() != iter;
                             ++iter)
                        {
                            if (boost::regex_match((*iter).first,
                                                   mHeadersDisallowed[i].first) &&
                                boost::regex_match((*iter).second,
                                                   mHeadersDisallowed[i].second))
                            {
                                std::ostringstream str;
                                str << "Disallowed header #" << i << " "
                                    << mHeadersDisallowed[i].first << "=" << mHeadersDisallowed[i].second
                                    << " found in response";
                                ensure(str.str(), false);
                            }
                        }
                    }
                }
            }

            if (! mCheckContentType.empty())
            {
                ensure("Response required with content type check", response != NULL);
                std::string con_type(response->getContentType());
                ensure("Content-Type as expected (" + mCheckContentType + ")",
                       mCheckContentType == con_type);
            }

            // std::cout << "TestHandler2::onCompleted() invoked" << std::endl;
        }

    HttpRequestTestData * mState;
    std::string mName;
    HttpHandle mExpectHandle;
    std::string mCheckContentType;
    regex_container_t mHeadersRequired;
    regex_container_t mHeadersDisallowed;
};

typedef test_group<HttpRequestTestData> HttpRequestTestGroupType;
typedef HttpRequestTestGroupType::object HttpRequestTestObjectType;
HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests");

template <> template <>
void HttpRequestTestObjectType::test<1>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest construction");

    HttpRequest * req = NULL;

    try
    {
        // Get singletons created
        HttpRequest::createService();

        // create a new ref counted object with an implicit reference
        req = new HttpRequest();

        // release the request object
        delete req;
        req = NULL;

        HttpRequest::destroyService();
    }
    catch (...)
    {
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

template <> template <>
void HttpRequestTestObjectType::test<2>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest and Null Op queued");

    HttpRequest * req = NULL;

    try
    {
        // Get singletons created
        HttpRequest::createService();

        // create a new ref counted object with an implicit reference
        req = new HttpRequest();

        // Issue a NoOp
        HttpHandle handle = req->requestNoOp(LLCore::HttpHandler::ptr_t());
        ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID);

        // release the request object
        delete req;
        req = NULL;

        // Request queue should have two references:  global singleton & service object
        ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount());

        // Okay, tear it down
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

namespace
{
    void NoOpDeletor(LLCore::HttpHandler *) { }
}

template <> template <>
void HttpRequestTestObjectType::test<3>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest NoOp + Stop execution");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = 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();

        // Issue a NoOp
        HttpHandle handle = req->requestNoOp(handlerp);
        ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_SHORT);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time", count < limit);
        ensure("One handler invocation for request", mHandlerCalls == 1);

        // Okay, request a shutdown of the servicing thread
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

template <> template <>
void HttpRequestTestObjectType::test<4>()
{
    ScopedCurlInit ready;

    set_test_name("2 HttpRequest instances, one thread");

    // Handler can be stack-allocated *if* there are no dangling
    // references to it after completion of this method.
    TestHandler2 handler1(this, "handler1");
    TestHandler2 handler2(this, "handler2");

    LLCore::HttpHandler::ptr_t handler1p(&handler1, NoOpDeletor);
    LLCore::HttpHandler::ptr_t handler2p(&handler2, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req1 = NULL;
    HttpRequest * req2 = 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
        req1 = new HttpRequest();
        req2 = new HttpRequest();

        // Issue some NoOps
        HttpHandle handle = req1->requestNoOp(handler1p);
        ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID);
        handler1.mExpectHandle = handle;

        handle = req2->requestNoOp(handler2p);
        ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID);
        handler2.mExpectHandle = handle;

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 2)
        {
            req1->update(1000000);
            req2->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time", count < limit);
        ensure("One handler invocation for request", mHandlerCalls == 2);

        // Okay, request a shutdown of the servicing thread
        handle = req2->requestStopThread(handler2p);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
        handler2.mExpectHandle = handle;

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 3)
        {
            req1->update(1000000);
            req2->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Second request executed in reasonable time", count < limit);
        ensure("Second handler invocation", mHandlerCalls == 3);

        // See that we actually shutdown the thread
        count = 0;
        limit = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req1;
        req1 = NULL;
        delete req2;
        req2 = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 3 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req1);
        delete req1;
        delete req2;
        HttpRequest::destroyService();
        throw;
    }
}

template <> template <>
void HttpRequestTestObjectType::test<5>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest Spin (soft) + NoOp + hard termination");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = 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();

        // Issue a Spin
        HttpHandle handle = req->requestSpin(1);
        ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Issue a NoOp
        handle = req->requestNoOp(handlerp);
        ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_SHORT);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("NoOp notification received", mHandlerCalls == 1);

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


template <> template <>
void HttpRequestTestObjectType::test<6>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest Spin + NoOp + hard termination");

    // 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");
    mHandlerCalls = 0;

    HttpRequest * req = NULL;

    try
    {
        LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);

        // 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();

        // Issue a Spin
        HttpHandle handle = req->requestSpin(0);        // Hard spin
        ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Issue a NoOp
        handle = req->requestNoOp(handlerp);
        ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_SHORT);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("No notifications received", mHandlerCalls == 0);

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


template <> template <>
void HttpRequestTestObjectType::test<7>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest GET to dead port + Stop execution");

    // 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");

    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t opts;

    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();

        opts = HttpOptions::ptr_t(new HttpOptions());
        opts->setRetries(1);            // Don't try for too long - default retries take about 18S

        // Issue a GET that can't connect
        mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
        HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                     "http://127.0.0.1:2/nothing/here",
                                                     0,
                                                     0,
                                                     opts,
                                                     HttpHeaders::ptr_t(),
                                                     handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options
        opts.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        opts.reset();
        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 GET 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = 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();

        // Issue a GET that *can* connect
        mStatus = HttpStatus(200);
        HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
                                            url_base,
                                            HttpOptions::ptr_t(),
                                            HttpHeaders::ptr_t(),
                                            handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


template <> template <>
void HttpRequestTestObjectType::test<9>()
{
    ScopedCurlInit ready;

    std::string url_base(get_base_url());
    // std::cerr << "Base:  "  << url_base << std::endl;

    set_test_name("HttpRequest GET with Range: header 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = 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();

        // Issue a GET that *can* connect
        mStatus = HttpStatus(200);
        HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                     url_base,
                                                     0,
                                                     0,
                                                     HttpOptions::ptr_t(),
                                                     HttpHeaders::ptr_t(),
                                                     handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


template <> template <>
void HttpRequestTestObjectType::test<10>()
{
    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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    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();

        // 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,
                                            url_base,
                                            body,
                                            HttpOptions::ptr_t(),
                                            HttpHeaders::ptr_t(),
                                            handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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);
    }
    catch (...)
    {
        if (body)
        {
            body->release();
        }
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

template <> template <>
void HttpRequestTestObjectType::test<11>()
{
    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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    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();

        // 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,
                                             url_base,
                                             body,
                                             HttpOptions::ptr_t(),
                                             HttpHeaders::ptr_t(),
                                             handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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);
    }
    catch (...)
    {
        if (body)
        {
            body->release();
        }
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

template <> template <>
void HttpRequestTestObjectType::test<12>()
{
    ScopedCurlInit ready;

    std::string url_base(get_base_url());
    // std::cerr << "Base:  "  << url_base << std::endl;

    set_test_name("HttpRequest GET with some tracing");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;

    try
    {
        // Get singletons created
        HttpRequest::createService();

        // Enable tracing
        HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);

        // 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();

        // Issue a GET that *can* connect
        mStatus = HttpStatus(200);
        HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                     url_base,
                                                     0,
                                                     0,
                                                     HttpOptions::ptr_t(),
                                                     HttpHeaders::ptr_t(),
                                                     handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


template <> template <>
void HttpRequestTestObjectType::test<13>()
{
    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());
    // std::cerr << "Base:  "  << url_base << std::endl;

    set_test_name("HttpRequest GET with returned headers");

    // 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");
    handler.mHeadersRequired.reserve(20);               // Avoid memory leak test failure
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t opts;

    try
    {
        // Get singletons created
        HttpRequest::createService();

        // Enable tracing
        HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);

        // 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();

        opts = HttpOptions::ptr_t(new HttpOptions());
        opts->setWantHeaders(true);

        // Issue a GET that succeeds
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(boost::regex("X-LL-Special", boost::regex::icase),
                                          boost::regex(".*", boost::regex::icase)));
        HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                     url_base,
                                                     0,
                                                 0,
                                                     opts,
                                                     HttpHeaders::ptr_t(),
                                                     handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // release options
        opts.reset();

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mHeadersRequired.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        opts.reset();
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


template <> template <>
void HttpRequestTestObjectType::test<14>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest GET timeout");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    std::string url_base(get_base_url() + "/sleep/");   // path to a 30-second sleep
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t opts;

    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();

        opts = HttpOptions::ptr_t(new HttpOptions);
        opts->setRetries(0);            // Don't retry
        opts->setTimeout(2);

        // Issue a GET that sleeps
        mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
        HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                     url_base,
                                                     0,
                                                     0,
                                                     opts,
                                                     HttpHeaders::ptr_t(),
                                                     handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options
        opts.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        opts.reset();
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

// Test retrieval of Content-Type/Content-Encoding headers
template <> template <>
void HttpRequestTestObjectType::test<15>()
{
    ScopedCurlInit ready;

    std::string url_base(get_base_url());
    // std::cerr << "Base:  "  << url_base << std::endl;

    set_test_name("HttpRequest GET with Content-Type");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);

    // Load and clear the string setting to preload std::string object
    // for memory return tests.
    handler.mCheckContentType = "application/llsd+xml";
    handler.mCheckContentType.clear();
    mHandlerCalls = 0;

    HttpRequest * req = 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();

        // Issue a GET that *can* connect
        mStatus = HttpStatus(200);
        handler.mCheckContentType = "application/llsd+xml";
        HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
                                            url_base,
                                            HttpOptions::ptr_t(),
                                            HttpHeaders::ptr_t(),
                                            handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mCheckContentType.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();

        ensure("Two handler calls on the way out", 2 == mHandlerCalls);
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


// Test header generation on GET requests
template <> template <>
void HttpRequestTestObjectType::test<16>()
{
    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());

    set_test_name("Header generation for HttpRequest GET");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t options;
    HttpHeaders::ptr_t headers;

    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();

        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setWantHeaders(true);

        // Issue a GET that *can* connect
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("\\*/\\*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
                                            url_base + "reflect/",
                                            options,
                                            HttpHeaders::ptr_t(),
                                            handlerp);
        ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time", count < limit);
        ensure("One handler invocation for request", mHandlerCalls == 1);

        // Do a texture-style fetch
        headers = HttpHeaders::ptr_t(new HttpHeaders);
        headers->append("Accept", "image/x-j2c");

        mStatus = HttpStatus(200);
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("image/x-j2c", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("\\W*X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));

        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                          url_base + "reflect/",
                                          0,
                                          47,
                                          options,
                                          headers,
                                          handlerp);
        ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time", count < limit);
        ensure("One handler invocation for request", mHandlerCalls == 2);


        // Okay, request a shutdown of the servicing thread
        mStatus = HttpStatus();
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 3)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Second request executed in reasonable time", count < limit);
        ensure("Second handler invocation", mHandlerCalls == 3);

        // See that we actually shutdown the thread
        count = 0;
        limit = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options & headers
        options.reset();
        headers.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        options.reset();
        headers.reset();

        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


// Test header generation on POST requests
template <> template <>
void HttpRequestTestObjectType::test<17>()
{
    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());

    set_test_name("Header generation for HttpRequest POST");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t options;
    HttpHeaders::ptr_t headers;
    BufferArray * ba = 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();

        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setWantHeaders(true);

        // And a buffer array
        const char * msg("It was the best of times, it was the worst of times.");
        ba = new BufferArray;
        ba->append(msg, strlen(msg));

        // Issue a default POST
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("\\*/\\*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-length", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));

        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-expect", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer_encoding", boost::regex::icase),
                boost::regex(".*chunked.*", boost::regex::icase)));
        HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
                                             url_base + "reflect/",
                                             ba,
                                             options,
                                             HttpHeaders::ptr_t(),
                                             handlerp);
        ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
        ba->release();
        ba = NULL;

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options & headers
        options.reset();
        headers.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        if (ba)
        {
            ba->release();
            ba = NULL;
        }
        options.reset();
        headers.reset();

        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


// Test header generation on PUT requests
template <> template <>
void HttpRequestTestObjectType::test<18>()
{
    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());

    set_test_name("Header generation for HttpRequest PUT");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t options;
    HttpHeaders::ptr_t headers;
    BufferArray * ba = 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();

        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setWantHeaders(true);

        // And a buffer array
        const char * msg("It was the best of times, it was the worst of times.");
        ba = new BufferArray;
        ba->append(msg, strlen(msg));

        // Issue a default PUT
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("\\*/\\*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-length", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));

        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-expect", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
                boost::regex(".*chunked.*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));

        HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
                                            url_base + "reflect/",
                                            ba,
                                            options,
                                            HttpHeaders::ptr_t(),
                                            handlerp);
        ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
        ba->release();
        ba = NULL;

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options & headers
        options.reset();
        headers.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        if (ba)
        {
            ba->release();
            ba = NULL;
        }
        options.reset();
        headers.reset();

        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


// Test header generation on GET requests with overrides
template <> template <>
void HttpRequestTestObjectType::test<19>()
{
    // It appears that HttpRequest is fully capable of sending duplicate header values in violation of
    // this test's expectations. Something needs to budge: is sending duplicate header values desired?
    //
    // Test server /reflect/ response headers (mirrored from request)
    //
    // X-Reflect-content-type: text/plain
    // X-Reflect-content-type: text/html
    // X-Reflect-content-type: application/llsd+xml
    //
    skip("FIXME: Bad assertions or broken functionality.");

    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());

    set_test_name("Header generation for HttpRequest GET with header overrides");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t options;
    HttpHeaders::ptr_t headers;

    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();

        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setWantHeaders(true);

        // headers
        headers = HttpHeaders::ptr_t(new HttpHeaders);
        headers->append("Keep-Alive", "120");
        headers->append("Accept-encoding", "deflate");
        headers->append("Accept", "text/plain");

        // Issue a GET with modified headers
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("text/plain", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("deflate", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("120", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));

        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("300", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("\\*/\\*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
                                            url_base + "reflect/",
                                            options,
                                            headers,
                                            handlerp);
        ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options & headers
        options.reset();
        headers.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        options.reset();
        headers.reset();

        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


// Test header generation on POST requests with overrides
template <> template <>
void HttpRequestTestObjectType::test<20>()
{
    // It appears that HttpRequest is fully capable of sending duplicate header values in violation of
    // this test's expectations. Something needs to budge: is sending duplicate header values desired?
    //
    // Test server /reflect/ response headers (mirrored from request)
    //
    // X-Reflect-content-type: text/plain
    // X-Reflect-content-type: text/html
    // X-Reflect-content-type: application/llsd+xml
    //
    skip("FIXME: Bad assertions or broken functionality.");

    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());

    set_test_name("Header generation for HttpRequest POST with header overrides");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t options;
    HttpHeaders::ptr_t headers;
    BufferArray * ba = 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();

        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setWantHeaders(true);

        // headers
        headers = HttpHeaders::ptr_t(new HttpHeaders());
        headers->append("keep-Alive", "120");
        headers->append("Accept", "text/html");
        headers->append("content-type", "application/llsd+xml");
        headers->append("cache-control", "no-store");

        // And a buffer array
        const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
        ba = new BufferArray;
        ba->append(msg, strlen(msg));

        // Issue a default POST
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("text/html", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("120", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-length", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex("application/llsd\\+xml", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex("no-store", boost::regex::icase)));

        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("\\*/\\*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("300", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-expect", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));

        HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
                                             url_base + "reflect/",
                                             ba,
                                             options,
                                             headers,
                                             handlerp);
        ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
        ba->release();
        ba = NULL;

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options & headers
        options.reset();
        headers.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        if (ba)
        {
            ba->release();
            ba = NULL;
        }
        options.reset();
        headers.reset();
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


// Test header generation on PUT requests with overrides
template <> template <>
void HttpRequestTestObjectType::test<21>()
{
    // It appears that HttpRequest is fully capable of sending duplicate header values in violation of
    // this test's expectations. Something needs to budge: is sending duplicate header values desired?
    //
    // Test server /reflect/ response headers (mirrored from request)
    //
    // X-Reflect-content-type: text/plain
    // X-Reflect-content-type: text/html
    // X-Reflect-content-type: application/llsd+xml
    //
    skip("FIXME: Bad assertions or broken functionality.");

    ScopedCurlInit ready;

    // Warmup boost::regex to pre-alloc memory for memory size tests
    boost::regex warmup("askldjflasdj;f", boost::regex::icase);
    boost::regex_match("akl;sjflajfk;ajsk", warmup);

    std::string url_base(get_base_url());

    set_test_name("Header generation for HttpRequest PUT with header overrides");

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t options;
    HttpHeaders::ptr_t headers;
    BufferArray * ba = 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();

        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setWantHeaders(true);

        // headers
        headers = HttpHeaders::ptr_t(new HttpHeaders);
        headers->append("content-type", "text/plain");
        headers->append("content-type", "text/html");
        headers->append("content-type", "application/llsd+xml");

        // And a buffer array
        const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
        ba = new BufferArray;
        ba->append(msg, strlen(msg));

        // Issue a default PUT
        mStatus = HttpStatus(200);
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-connection", boost::regex::icase),
                boost::regex("keep-alive", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept", boost::regex::icase),
                boost::regex("\\*/\\*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
                boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-keep-alive", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-host", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-length", boost::regex::icase),
                boost::regex("\\d+", boost::regex::icase)));
        handler.mHeadersRequired.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex("application/llsd\\+xml", boost::regex::icase)));

        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-cache-control", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-pragma", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-range", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-referer", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-expect", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
                boost::regex(".*", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex("text/plain", boost::regex::icase)));
        handler.mHeadersDisallowed.push_back(
            regex_container_t::value_type(
                boost::regex("X-Reflect-content-type", boost::regex::icase),
                boost::regex("text/html", boost::regex::icase)));
        HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
                                            url_base + "reflect/",
                                            ba,
                                            options,
                                            headers,
                                            handlerp);
        ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
        ba->release();
        ba = NULL;

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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();
        handler.mHeadersRequired.clear();
        handler.mHeadersDisallowed.clear();
        handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 2)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options & headers
        options.reset();
        headers.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        if (ba)
        {
            ba->release();
            ba = NULL;
        }
        options.reset();
        headers.reset();
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

// BUG-2295 Tests - Content-Range header received but no body
template <> template <>
void HttpRequestTestObjectType::test<22>()
{
    ScopedCurlInit ready;

    std::string url_base(get_base_url());
    // std::cerr << "Base:  "  << url_base << std::endl;

    set_test_name("BUG-2295");

#if LL_WINDOWS && ADDRESS_SIZE == 64
    // teamcity win64 builds freeze on this test, if you figure out the cause, please fix it
    if (getenv("TEAMCITY_PROJECT_NAME"))
    {
        skip("BUG-2295 - partial load on W64 causes freeze");
    }
#endif
    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    mHandlerCalls = 0;

    HttpOptions::ptr_t options;
    HttpRequest * req = NULL;

    try
    {
        // options set
        options = HttpOptions::ptr_t(new HttpOptions());
        options->setRetries(1);         // Partial_File is retryable and can timeout in here
        options->setDNSCacheTimeout(30);

        // 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();

        // ======================================
        // Issue bug2295 GETs that will get a 206
        // ======================================
        mStatus = HttpStatus(206);
        static const int test_count(3);
        for (int i(0); i < test_count; ++i)
        {
            char buffer[128];
            sprintf(buffer, "/bug2295/%d/", i);
            HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                         url_base + buffer,
                                                         0,
                                                         25,
                                                         options,
                                                         HttpHeaders::ptr_t(),
                                                         handlerp);
            ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
        }

        // Run the notification pump.
        int count(0);
        int limit(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < test_count)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time - ms1", count < limit);
        ensure("One handler invocation for each request - ms1", mHandlerCalls == test_count);

        // ======================================
        // Issue bug2295 GETs that will get a libcurl 18 (PARTIAL_FILE)
        // ======================================
        mHandlerCalls = 0;
        mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE);
        static const int test2_count(1);
        for (int i(0); i < test2_count; ++i)
        {
            char buffer[128];
            sprintf(buffer, "/bug2295/00000012/%d/", i);
            HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                         url_base + buffer,
                                                         0,
                                                         25,
                                                         options,
                                                         HttpHeaders::ptr_t(),
                                                         handlerp);
            ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
        }

        // Run the notification pump.
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < test2_count)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time - ms2", count < limit);
        ensure("One handler invocation for each request - ms2", mHandlerCalls == test2_count);

        // ======================================
        // Issue bug2295 GETs that will get an llcorehttp HE_INV_CONTENT_RANGE_HDR status
        // ======================================
        mHandlerCalls = 0;
        mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
        static const int test3_count(1);
        for (int i(0); i < test3_count; ++i)
        {
            char buffer[128];
            sprintf(buffer, "/bug2295/inv_cont_range/%d/", i);
            HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
                                                         url_base + buffer,
                                                         0,
                                                         25,
                                                         options,
                                                         HttpHeaders::ptr_t(),
                                                         handlerp);
            ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
        }

        // Run the notification pump.
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < test3_count)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Request executed in reasonable time - ms3", count < limit);
        ensure("One handler invocation for each request - ms3", mHandlerCalls == test3_count);

        // ======================================
        // Okay, request a shutdown of the servicing thread
        // ======================================
        mStatus = HttpStatus();
        mHandlerCalls = 0;
        HttpHandle handle = req->requestStopThread(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Shutdown request executed in reasonable time", count < limit);
        ensure("Shutdown handler invocation", mHandlerCalls == 1);

        // See that we actually shutdown the thread
        count = 0;
        limit = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options
        options.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}

template <> template <>
void HttpRequestTestObjectType::test<23>()
{
    ScopedCurlInit ready;

    set_test_name("HttpRequest GET 503s with 'Retry-After'");

#if LL_WINDOWS && ADDRESS_SIZE == 64
    // teamcity win64 builds freeze on this test, if you figure out the cause, please fix it
    if (getenv("TEAMCITY_PROJECT_NAME"))
    {
        skip("llcorehttp 503-with-retry test hangs on Windows 64");
    }
#endif

    // 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");
    LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
    std::string url_base(get_base_url() + "/503/"); // path to 503 generators
    mHandlerCalls = 0;

    HttpRequest * req = NULL;
    HttpOptions::ptr_t opts;

    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();

        opts = HttpOptions::ptr_t(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,
                                                         url.str(),
                                                         0,
                                                         0,
                                                         opts,
                                                         HttpHeaders::ptr_t(),
                                                         handlerp);

            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(LOOP_COUNT_LONG);
        while (count++ < limit && mHandlerCalls < url_limit)
        {
            req->update(0);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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(handlerp);
        ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);

        // Run the notification pump again
        count = 0;
        limit = LOOP_COUNT_LONG;
        while (count++ < limit && mHandlerCalls < 1)
        {
            req->update(1000000);
            usleep(LOOP_SLEEP_INTERVAL);
        }
        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 = LOOP_COUNT_SHORT;
        while (count++ < limit && ! HttpService::isStopped())
        {
            usleep(LOOP_SLEEP_INTERVAL);
        }
        ensure("Thread actually stopped running", HttpService::isStopped());

        // release options
        opts.reset();

        // release the request object
        delete req;
        req = NULL;

        // Shut down service
        HttpRequest::destroyService();
    }
    catch (...)
    {
        stop_thread(req);
        opts.reset();
        delete req;
        HttpRequest::destroyService();
        throw;
    }
}


}  // end namespace tut

namespace
{

#if defined(WIN32)

void usleep(unsigned long usec)
{
    Sleep((DWORD) (usec / 1000UL));
}

#endif

}

#endif  // TEST_LLCORE_HTTP_REQUEST_H_