Apertura del repositorio

This commit is contained in:
2025-05-24 18:09:39 -03:00
parent 76e6359dad
commit d883ddd0d0
35253 changed files with 2891973 additions and 2 deletions
+168
View File
@@ -0,0 +1,168 @@
# -*- cmake -*-
project(llcorehttp)
include(00-Common)
include(CURL)
include(OpenSSL)
include(NGHTTP2)
include(ZLIBNG)
include(LLCoreHttp)
include(LLAddBuildTest)
include(LLCommon)
include(Tut)
include(bugsplat)
set(llcorehttp_SOURCE_FILES
bufferarray.cpp
bufferstream.cpp
httpcommon.cpp
llhttpconstants.cpp
httpheaders.cpp
httpoptions.cpp
httprequest.cpp
httpresponse.cpp
httpstats.cpp
_httplibcurl.cpp
_httpopcancel.cpp
_httpoperation.cpp
_httpoprequest.cpp
_httpopsetget.cpp
_httpopsetpriority.cpp
_httppolicy.cpp
_httppolicyclass.cpp
_httppolicyglobal.cpp
_httpreplyqueue.cpp
_httprequestqueue.cpp
_httpservice.cpp
_refcounted.cpp
)
set(llcorehttp_HEADER_FILES
CMakeLists.txt
bufferarray.h
bufferstream.h
httpcommon.h
llhttpconstants.h
httphandler.h
httpheaders.h
httpoptions.h
httprequest.h
httpresponse.h
httpstats.h
_httpinternal.h
_httplibcurl.h
_httpopcancel.h
_httpoperation.h
_httpoprequest.h
_httpopsetget.h
_httpopsetpriority.h
_httppolicy.h
_httppolicyclass.h
_httppolicyglobal.h
_httpreadyqueue.h
_httpreplyqueue.h
_httprequestqueue.h
_httpservice.h
_mutex.h
_refcounted.h
_thread.h
)
if (DARWIN OR LINUX)
# Boost headers define unused members in condition_variable so...
set_source_files_properties(${llcorehttp_SOURCE_FILES}
PROPERTIES COMPILE_FLAGS -Wno-unused-variable)
endif (DARWIN OR LINUX)
list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES})
add_library (llcorehttp ${llcorehttp_SOURCE_FILES})
target_link_libraries(
llcorehttp
llcommon
ll::libcurl
ll::openssl
ll::nghttp2
)
target_include_directories( llcorehttp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
# llmessage depends on llcorehttp, yet llcorehttp also depends on llmessage (at least for includes).
# Cannot/Should not use target_link_libraries here to add llmessage to the dependencies, as that would
# lead to circular dependencies (or in case of cmake, the first project declaring it's dependencies wins)
target_include_directories( llcorehttp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../llmessage)
# tests
set(LLCOREHTTP_TESTS ON CACHE BOOL
"Build and run llcorehttp integration tests specifically")
if (LL_TESTS AND LLCOREHTTP_TESTS)
SET(llcorehttp_TEST_SOURCE_FILES
)
set(llcorehttp_TEST_HEADER_FILES
tests/test_httpstatus.hpp
tests/test_refcounted.hpp
tests/test_httpoperation.hpp
tests/test_httprequest.hpp
tests/test_httprequestqueue.hpp
tests/test_httpheaders.hpp
tests/test_bufferarray.hpp
tests/test_bufferstream.hpp
)
list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES})
# LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}")
# set(TEST_DEBUG on)
set(test_libs
llcorehttp
llmessage
llcommon
)
# If http_proxy is in the current environment (e.g. to fetch s3-proxy
# autobuild packages), suppress it for this integration test: it screws up
# the tests.
#LL_ADD_INTEGRATION_TEST(llcorehttp
# "${llcorehttp_TEST_SOURCE_FILES}"
# "${test_libs}"
# "-Dhttp_proxy"
# ${PYTHON_EXECUTABLE}
# "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py"
# )
#
# Example Programs
#
SET(llcorehttp_EXAMPLE_SOURCE_FILES
examples/http_texture_load.cpp
)
set(example_libs
llcorehttp
llmessage
llcommon
)
add_executable(http_texture_load
${llcorehttp_EXAMPLE_SOURCE_FILES}
)
set_target_properties(http_texture_load
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}"
)
if (WINDOWS)
# The following come from LLAddBuildTest.cmake's INTEGRATION_TEST_xxxx target.
set_target_properties(http_texture_load
PROPERTIES
LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:CONSOLE"
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO"
LINK_FLAGS_RELEASE ""
)
endif (WINDOWS)
target_link_libraries(http_texture_load ${example_libs})
endif (LL_TESTS AND LLCOREHTTP_TESTS)
+679
View File
@@ -0,0 +1,679 @@
1. HTTP Fetching in 15 Minutes
Let's start with a trivial working example. You'll need a throwaway
build of the viewer. And we'll use indra/newview/llappviewer.cpp as
the host module for these hacks.
First, add some headers:
#include "httpcommon.h"
#include "httprequest.h"
#include "httphandler.h"
You'll need to derive a class from HttpHandler (not HttpHandle).
This is used to deliver notifications of HTTP completion to your
code. Place it near the top, before LLDeferredTaskList, say:
class MyHandler : public LLCore::HttpHandler
{
public:
MyHandler()
: LLCore::HttpHandler()
{}
virtual void onCompleted(LLCore::HttpHandle /* handle */,
LLCore::HttpResponse * /* response */)
{
LL_INFOS("Hack") << "It is happening again." << LL_ENDL;
delete this; // Last statement
}
};
Add some statics up there as well:
// Our request object. Allocate during initialiation.
static LLCore::HttpRequest * my_request(NULL);
// The policy class for HTTP traffic.
// Use HttpRequest::DEFAULT_POLICY_ID, but DO NOT SHIP WITH THIS VALUE!!
static LLCore::HttpRequest::policy_t my_policy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
// Priority for HTTP requests. Use 0U.
static LLCore::HttpRequest::priority_t my_priority(0U);
In LLAppViewer::init() after mAppCoreHttp.init(), create a request object:
my_request = new LLCore::HttpRequest();
In LLAppViewer::mainLoop(), just before entering the while loop,
we'll kick off one HTTP request:
// Construct a handler object (we'll use the heap this time):
MyHandler * my_handler = new MyHandler;
// Issue a GET request to 'http://www.example.com/' kicking off
// all the I/O, retry logic, etc.
LLCore::HttpHandle handle;
handle = my_request->requestGet(my_policy,
my_priority,
"http://www.example.com/",
NULL,
NULL,
my_handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
LL_WARNS("Hack") << "Failed to launch HTTP request. Try again."
<< LL_ENDL;
}
Finally, arrange to periodically call update() on the request object
to find out when the request completes. This will be done by
calling the onCompleted() method with status information and
response data from the HTTP operation. Add this to the
LLAppViewer::idle() method after the ping:
my_request->update(0);
That's it. Build it, run it and watch the log file. You should get
the "It is happening again." message indicating that the HTTP
operation completed in some manner.
2. What Does All That Mean
MyHandler/HttpHandler. This class replaces the Responder-style in
legacy code. One method is currently defined. It is used for all
request completions, successful or failed:
void onCompleted(LLCore::HttpHandle /* handle */,
LLCore::HttpResponse * /* response */);
The onCompleted() method is invoked as a callback during calls to
HttpRequest::update(). All I/O is completed asynchronously in
another thread. But notifications are polled by calling update()
and invoking a handler for completed requests.
In this example, the invocation also deletes the handler (which is
never referenced by the llcorehttp code again). But other
allocation models are possible including handlers shared by many
requests, stack-based handlers and handlers mixed in with other,
unrelated classes.
LLCore::HttpRequest(). Instances of this class are used to request
all major functions of the library. Initialization, starting
requests, delivering final notification of completion and various
utility operations are all done via instances. There is one very
important rule for instances:
Request objects may NOT be shared between threads.
my_priority. The APIs support the idea of priority ordering of
requests but it hasn't been implemented and the hope is that this
will become useless and removed from the interface. Use 0U except
as noted.
my_policy. This is an important one. This library attempts to
manage TCP connection usage more rigorously than in the past. This
is done by issuing requests to a queue that has various settable
properties. These establish connection usage for the queue as well
as how queues compete with one another. (This is patterned after
class-based queueing used in various networking stacks.) Several
classes are pre-defined. Deciding when to use an existing class and
when to create a new one will determine what kind of experience
users have. We'll pick up this question in detail below.
requestGet(). Issues an ordinary HTTP GET request to a given URL
and associating the request with a policy class, a priority and an
response handler. Two additional arguments, not used here, allow
for additional headers on the request and for per-request options.
If successful, the call returns a handle whose value is other than
LLCORE_HTTP_HANDLE_INVALID. The HTTP operation is then performed
asynchronously by another thread without any additional work by the
caller. If the handle returned is invalid, you can get the status
code by calling my_request->getStatus().
update(). To get notification that the request has completed, a
call to update() will invoke onCompleted() methods.
3. Refinements, Necessary and Otherwise
MyHandler::onCompleted(). You'll want to do something useful with
your response. Distinguish errors from successes and getting the
response body back in some form.
Add a new header:
#include "bufferarray.h"
Replace the existing MyHandler::onCompleted() definition with:
virtual void onCompleted(LLCore::HttpHandle /* handle */,
LLCore::HttpResponse * response)
{
LLCore::HttpStatus status = response->getStatus();
if (status)
{
// Successful request. Try to fetch the data
LLCore::BufferArray * data = response->getBody();
if (data && data->size())
{
// There's some data. A BufferArray is a linked list
// of buckets. We'll create a linear buffer and copy
// the data into it.
size_t data_len = data->size();
char * data_blob = new char [data_len + 1];
data->read(0, data_blob, data_len);
data_blob[data_len] = '\0';
// Process the data now in NUL-terminated string.
// Needs more scrubbing but this will do.
LL_INFOS("Hack") << "Received: " << data_blob << LL_ENDL;
// Free the temporary data
delete [] data_blob;
}
}
else
{
// Something went wrong. Translate the status to
// a meaningful message.
LL_WARNS("Hack") << "HTTP GET failed. Status: "
<< status.toTerseString()
<< ", Reason: " << status.toString()
<< LL_ENDL;
}
delete this; // Last statement
}
HttpHeaders. The header file "httprequest.h" documents the expected
important headers that will go out with the request. You can add to
these by including an HttpHeaders object with the requestGet() call.
These are typically setup once as part of init rather than
dynamically created.
Add another header:
#include "httpheaders.h"
In LLAppViewer::mainLoop(), add this alongside the allocation of
my_handler:
// Additional headers for all requests
LLCore::HttpHeaders * my_headers = new LLCore::HttpHeaders();
my_headers->append("Accept", "text/html, application/llsd+xml");
HttpOptions. Options are similar and include a mix of value types.
One interesting per-request option is the trace setting. This
enables various debug-type messages in the log file that show the
progress of the request through the library. It takes values from
zero to three with higher values giving more verbose logging. We'll
use '2' and this will also give us a chance to verify that
HttpHeaders works as expected.
Same as above, a new header:
#include "httpoptions.h"
And in LLAppView::mainLoop():
// Special options for requests
LLCore::HttpOptions * my_options = new LLCore::HttpOptions();
my_options->setTrace(2);
Now let's put that all together into a more complete requesting
sequence. Replace the existing invocation of requestGet() with this
slightly more elaborate block:
LLCore::HttpHandle handle;
handle = my_request->requestGet(my_policy,
my_priority,
"http://www.example.com/",
my_options,
my_headers,
my_handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
LLCore::HttpStatus status = my_request->getStatus();
LL_WARNS("Hack") << "Failed to request HTTP GET. Status: "
<< status.toTerseString()
<< ", Reason: " << status.toString()
<< LL_ENDL;
delete my_handler; // No longer needed.
my_handler = NULL;
}
Build, run and examine the log file. You'll get some new data with
this run. First, you should get the www.example.com home page
content:
----------------------------------------------------------------------------
2013-09-17T20:26:51Z INFO: MyHandler::onCompleted: Received: <!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 50px;
background-color: #fff;
border-radius: 1em;
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
body {
background-color: #fff;
}
div {
width: auto;
margin: 0 auto;
border-radius: 0;
padding: 1em;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is established to be used for illustrative examples in documents. You may use this
domain in examples without prior coordination or asking for permission.</p>
<p><a href="http://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
----------------------------------------------------------------------------
You'll also get a detailed trace of the HTTP operation itself. Note
the HEADEROUT line which shows the additional header added to the
request.
----------------------------------------------------------------------------
HttpService::processRequestQueue: TRACE, FromRequestQueue, Handle: 086D3148
HttpLibcurl::addOp: TRACE, ToActiveQueue, Handle: 086D3148, Actives: 0, Readies: 0
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: About to connect() to www.example.com port 80 (#0)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Trying 93.184.216.119...
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Connected to www.example.com (93.184.216.119) port 80 (#0)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Connected to www.example.com (93.184.216.119) port 80 (#0)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADEROUT, Data: GET / HTTP/1.1 Host: www.example.com Accept-Encoding: deflate, gzip Connection: keep-alive Keep-alive: 300 Accept: text/html, application/llsd+xml
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: HTTP/1.1 200 OK
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Accept-Ranges: bytes
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Cache-Control: max-age=604800
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Content-Type: text/html
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Date: Tue, 17 Sep 2013 20:26:56 GMT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Etag: "3012602696"
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Expires: Tue, 24 Sep 2013 20:26:56 GMT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Server: ECS (ewr/1590)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: X-Cache: HIT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: x-ec-custom-error: 1
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Content-Length: 1270
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data:
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: DATAIN, Data: 256 Bytes
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Connection #0 to host www.example.com left intact
HttpLibcurl::completeRequest: TRACE, RequestComplete, Handle: 086D3148, Status: Http_200
HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle: 086D3148
----------------------------------------------------------------------------
4. What Does All That Mean, Part 2
HttpStatus. The HttpStatus object encodes errors from libcurl, the
library itself and HTTP status values. It does this to avoid
collapsing all non-HTTP error into a single '499' HTTP status and to
make errors distinct.
To aid programming, the usual bool conversions are available so that
you can write 'if (status)' and the expected thing will happen
whether it's an HTTP, libcurl or library error. There's also
provision to override the treatment of HTTP errors (making 404 a
success, say).
Share data, don't copy it. The library was started with the goal of
avoiding data copies as much as possible. Instead, read-only data
sharing across threads with atomic reference counts is used for a
number of data types. These currently are:
* BufferArray. Linked list of data blocks/HTTP bodies.
* HttpHeaders. Shared headers for both requests and responses.
* HttpOptions. Request-only data modifying HTTP behavior.
* HttpResponse. HTTP response description given to onCompleted.
Using objects of these types requires a few rules:
* Constructor always gives a reference to caller.
* References are dropped with release() not delete.
* Additional references may be taken out with addRef().
* Unless otherwise stated, once an object is shared with another
thread it should be treated as read-only. There's no
synchronization on the objects themselves.
HttpResponse. You'll encounter this mainly in onCompleted() methods.
Commonly-used interfaces on this object:
* getStatus() to return the final status of the request.
* getBody() to retrieve the response body which may be NULL or
zero-length.
* getContentType() to return the value of the 'Content-Type'
header or an empty string if none was sent.
This is a reference-counted object so you can call addRef() on it
and hold onto the response for an arbitrary time. But you'll
usually just call a few methods and return from onCompleted() whose
caller will release the object.
BufferArray. The core data representation for request and response
bodies. In HTTP responses, it's fetched with the getBody() method
and may be NULL or non-NULL with zero length. All successful data
handling should check both conditions before attempting to fetch
data from the object. Data access model uses simple read/write
semantics:
* append()
* size()
* read()
* write()
(There is a more sophisticated stream adapter that extends these
methods and will be covered below.) So, one way to retrieve data
from a request is as follows:
LLCore::BufferArray * data = response->getBody();
if (data && data->size())
{
size_t data_len = data->size();
char * data_blob = new char [data_len + 1];
data->read(0, data_blob, data_len);
HttpOptions and HttpResponse. Really just simple containers of POD
and std::string pairs. But reference counted and the rule about not
modifying after sharing must be followed. You'll have the urge to
change options dynamically at some point. And you'll try to do that
by just writing new values to the shared object. And in tests
everything will appear to work. Then you ship and people in the
real world start hitting read/write races in strings and then crash.
Don't be lazy.
HttpHandle. Uniquely identifies a request and can be used to
identify it in an onCompleted() method or cancel it if it's still
queued. But as soon as a request's onCompleted() invocation
returns, the handle becomes invalid and may be reused immediately
for new requests. Don't hold on to handles after notification.
5. And Still More Refinements
(Note: The following refinements are just code fragments. They
don't directly fit into the working example above. But they
demonstrate several idioms you'll want to copy.)
LLSD, std::streambuf, std::iostream. The read(), write() and
append() methods may be adequate for your purposes. But we use a
lot of LLSD. Its interfaces aren't particularly compatible with
BufferArray. And so two adapters are available to give
stream-like behaviors: BufferArrayStreamBuf and BufferArrayStream,
which implement the std::streambuf and std::iostream interfaces,
respectively.
A std::streambuf interface isn't something you'll want to use
directly. Instead, you'll use the much friendlier std::iostream
interface found in BufferArrayStream. This adapter gives you all
the '>>' and '<<' operators you'll want as well as working
directly with the LLSD conversion operators.
Some new headers:
#include "bufferstream.h"
#include "llsdserialize.h"
And an updated fragment based on onCompleted() above:
// Successful request. Try to fetch the data
LLCore::BufferArray * data = response->getBody();
LLSD resp_llsd;
if (data && data->size())
{
// There's some data and we expect this to be
// LLSD. Checking of content type and validation
// during parsing would be admirable additions.
// But we'll forgo that now.
LLCore::BufferArrayStream data_stream(data);
LLSDSerialize::fromXML(resp_llsd, data_stream);
}
LL_INFOS("Hack") << "LLSD Received: " << resp_llsd << LL_ENDL;
}
else
{
Converting an LLSD object into an XML stream stored in a
BufferArray is just the reverse of the above:
BufferArray * data = new BufferArray();
LLCore::BufferArrayStream data_stream(data);
LLSD src_llsd;
src_llsd["foo"] = "bar";
LLSDSerialize::toXML(src_llsd, data_stream);
// 'data' now contains an XML payload and can be sent
// to a web service using the requestPut() or requestPost()
// methods.
... requestPost(...);
// And don't forget to release the BufferArray.
data->release();
data = NULL;
There are now helper functions in llmessage/llcorehttputil.h to
assist with LLSD usage. requestPostWithLLSD(...) provides a
requestPost()-like interface that takes an LLSD object rather than
a BufferArray. And responseToLLSD(...) attempts to convert a
BufferArray received from a server into an LLSD object. You can
find examples in llmeshrepository.cpp, llinventorymodel.cpp,
llinventorymodelbackgroundfetch.cpp and lltexturefetch.cpp.
LLSD will often go hand-in-hand with BufferArray and data
transport. But you can also do all the streaming I/O you'd expect
of a std::iostream object:
BufferArray * data = new BufferArray();
LLCore::BufferArrayStream data_stream(data);
data_stream << "Hello, World!" << 29.4 << '\n';
std::string str;
data_stream >> str;
std::cout << str << std::endl;
data->release();
// Actual delete will occur when 'data_stream'
// falls out of scope and is destructed.
Scoping objects and cleaning up. The examples haven't bothered
with cleanup of objects that are no longer needed. Instead, most
objects have been allocated as if they were global and eternal.
You'll put the objects in more appropriate feature objects and
clean them up as a group. Here's a checklist for actions you may
need to take on cleanup:
* Call delete on:
o HttpHandlers created on the heap
o HttpRequest objects
* Call release() on:
o BufferArray objects
o HttpHeaders objects
o HttpOptions objects
o HttpResponse objects
On program exit, as threads wind down, the library continues to
operate safely. Threads don't interact via the library and even
dangling references to HttpHandler objects are safe. If you don't
call HttpRequest::update(), handler references are never
dereferenced.
You can take a more thorough approach to wind-down. Keep a list
of HttpHandles (not HttpHandlers) of outstanding requests. For
each of these, call HttpRequest::requestCancel() to cancel the
operation. (Don't add the cancel requests' handled to the list.)
This will cancel the outstanding requests that haven't completed.
Canceled or completed, all requests will queue notifications. You
can now cycle calling update() discarding responses. Continue
until all requests notify or a few seconds have passed.
Global startup and shutdown is handled in the viewer. But you can
learn about it in the code or in the documentation in the headers.
6. Choosing a Policy Class
Now it's time to get rid of the default policy class. Take a look
at the policy class definitions in newview/llappcorehttp.h.
Ideally, you'll find one that's compatible with what you're doing.
Some of the compatibility guidelines are:
* Destination: Pair of host and port. Mixing requests with
different destinations may cause more connection setup and tear
down.
* Method: http or https. Usually moot given destination. But
mixing these may also cause connection churn.
* Transfer size: If you're moving 100MB at a time and you make your
requests to the same policy class as a lot of small, fast event
information that fast traffic is going to get stuck behind you
and someone's experience is going to be miserable.
* Long poll requests: These are long-lived, must- do operations.
They have a special home called AP_LONG_POLL.
* Concurrency: High concurrency (5 or more) and large transfer
sizes are incompatible. Another head-of-the-line problem. High
concurrency is tolerated when it's desired to get maximal
throughput. Mesh and texture downloads, for example.
* Pipelined: If your requests are not idempotent, stay away from
anything marked 'soon' or 'yes'. Hidden retries may be a
problem for you. For now, would also recommend keeping PUT and
POST requests out of classes that may be pipelined. Support for
that is still a bit new.
If you haven't found a compatible match, you can either create a
new class (llappcorehttp.*) or just use AP_DEFAULT, the catchall
class when all else fails. Inventory query operations might be a
candidate for a new class that supported pipelining on https:.
Same with display name lookups and other bursty-at-login
operations. For other things, AP_DEFAULT will do what it can and
will, in some way or another, tolerate any usage. Whether the
users' experiences are good are for you to determine.
7. FAQ
Q1. What do these policy classes achieve?
A1. Previously, HTTP-using code in the viewer was written as if
it were some isolated, local operation that didn't have to
consider resources, contention or impact on services and the
larger environment. The result was an application with on the
order of 100 HTTP launch points in its codebase that could create
dozens or even 100's of TCP connections zeroing in on grid
services and disrupting networking equipment, web services and
innocent users. The use of policy classes (modeled on
http://en.wikipedia.org/wiki/Class-based_queueing) is a means to
restrict connection concurrency, good and necessary in itself. In
turn, that reduces demands on an expensive resource (connection
setup and concurrency) which relieves strain on network points.
That enables connection keepalive and opportunites for true
improvements in throughput and user experience.
Another aspect of the classes is that they give some control over
how competing demands for the network will be apportioned. If
mesh fetches, texture fetches and inventory queries are all being
made at once, the relative weights of their classes' concurrency
limits established that apportioning. We now have an opportunity
to balance the entire viewer system.
Q2. How's that data sharing with refcounts working for you?
A2. Meh. It does reduce memory churn and the frequency at which
free blocks must be moved between threads. But it's also a design
for static configuration and dynamic reconfiguration (not
requiring a restart) is favored. Creating new options for every
request isn't too bad, it a sequence of "new, fill, request,
release" for each requested operation. That in contrast to doing
the "new, fill, release" at startup. The bad comes in getting at
the source data. One rule in this work was "no new thread
problems." And one source for those is pulling setting values out
of gSettings in threads. None of that is thread safe though we
tend to get away with it.
Q3. What needs to be done?
A3. There's a To-Do list in _httpinternal.h. It has both large
and small projects here if someone would like to try changes.
+171
View File
@@ -0,0 +1,171 @@
/**
* @file _httpinternal.h
* @brief Implementation constants and magic numbers
*
* $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 _LLCORE_HTTP_INTERNAL_H_
#define _LLCORE_HTTP_INTERNAL_H_
// If you find this included in a public interface header,
// something wrong is probably happening.
// --------------------------------------------------------------------
// General library to-do list
//
// - Implement policy classes. Structure is mostly there just didn't
// need it for the first consumer. [Classes are there. More
// advanced features, like borrowing, aren't there yet.]
// - Consider Removing 'priority' from the request interface. Its use
// in an always active class can lead to starvation of low-priority
// requests. Requires coodination of priority values across all
// components that share a class. Changing priority across threads
// is slightly expensive (relative to gain) and hasn't been completely
// implemented. And the major user of priority, texture fetches,
// may not really need it.
// - Set/get for global policy and policy classes is clumsy. Rework
// it heading in a direction that allows for more dynamic behavior.
// [Mostly fixed]
// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the
// pedantic.
// - Update downloader and other long-duration services are going to
// need a progress notification. Initial idea is to introduce a
// 'repeating request' which can piggyback on another request and
// persist until canceled or carrier completes. Current queue
// structures allow an HttpOperation object to be enqueued
// repeatedly, so...
// - Investigate making c-ares' re-implementation of a resolver library
// more resilient or more intelligent on Mac. Part of the DNS failure
// lies in here. The mechanism also looks a little less dynamic
// than needed in an environments where networking is changing.
// - Global optimizations: 'borrowing' connections from other classes,
// HTTP pipelining.
// - Dynamic/control system stuff: detect problems and self-adjust.
// This won't help in the face of the router problems we've looked
// at, however. Detect starvation due to UDP activity and provide
// feedback to it.
// - Change the transfer timeout scheme. We're less interested in
// absolute time, in most cases, than in continuous progress.
// - Many of the policy class settings are currently applied to the
// entire class. Some, like connection limits, would be better
// applied to each destination target making multiple targets
// independent.
//
// Integration to-do list
// - LLTextureFetch still needs a major refactor. The use of
// LLQueuedThread makes it hard to inspect workers and do the
// resource waiting we're now doing. Rebuild along simpler lines
// some of which are suggested in new commentary at the top of
// the main source file.
// - Expand areas of usage eventually leading to the removal of LLCurl.
// Rough order of expansion:
// . Avatar names
// . Group membership lists
// . Caps access in general
// . 'The rest'
// - Adapt texture cache, image decode and other image consumers to
// the BufferArray model to reduce data copying. Alternatively,
// adapt this library to something else.
//
// --------------------------------------------------------------------
// If '1', internal ready queues will not order ready
// requests by priority, instead it's first-come-first-served.
// Reprioritization requests have the side-effect of then
// putting the modified request at the back of the ready queue.
#define LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY 1
namespace LLCore
{
// Maxium number of policy classes that can be defined.
// *TODO: Currently limited to the default class + 1, extend.
// (TSN: should this be more dynamically sized. Is there a reason to hard limit the number of policies?)
constexpr int HTTP_POLICY_CLASS_LIMIT = 32;
// Debug/informational tracing. Used both
// as a global option and in per-request traces.
constexpr int HTTP_TRACE_OFF = 0;
constexpr int HTTP_TRACE_LOW = 1;
constexpr int HTTP_TRACE_CURL_HEADERS = 2;
constexpr int HTTP_TRACE_CURL_BODIES = 3;
constexpr int HTTP_TRACE_MIN = HTTP_TRACE_OFF;
constexpr int HTTP_TRACE_MAX = HTTP_TRACE_CURL_BODIES;
// Request retry limits
//
// At a minimum, retries need to extend past any throttling
// window we're expecting from central services. In the case
// of Linden services running through the caps routers, there's
// a five-second or so window for throttling with some spillover.
// We want to span a few windows to allow transport to slow
// after onset of the throttles and then recover without a final
// failure. Other systems may need other constants.
constexpr int HTTP_RETRY_COUNT_DEFAULT = 5;
constexpr int HTTP_RETRY_COUNT_MIN = 0;
constexpr int HTTP_RETRY_COUNT_MAX = 100;
constexpr HttpTime HTTP_RETRY_BACKOFF_MIN_DEFAULT = 1000000UL; // 1 sec
constexpr HttpTime HTTP_RETRY_BACKOFF_MAX_DEFAULT = 50000006UL; // 5 sec
constexpr HttpTime HTTP_RETRY_BACKOFF_MAX = 20000000UL; // 20 sec
constexpr int HTTP_REDIRECTS_DEFAULT = 10;
// Timeout value used for both connect and protocol exchange.
// Retries and time-on-queue are not included and aren't
// accounted for.
constexpr long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L;
constexpr long HTTP_REQUEST_XFER_TIMEOUT_DEFAULT = 0L;
constexpr long HTTP_REQUEST_TIMEOUT_MIN = 0L;
constexpr long HTTP_REQUEST_TIMEOUT_MAX = 3600L;
// Limits on connection counts
constexpr int HTTP_CONNECTION_LIMIT_DEFAULT = 8;
constexpr int HTTP_CONNECTION_LIMIT_MIN = 1;
constexpr int HTTP_CONNECTION_LIMIT_MAX = 256;
// Pipelining limits
constexpr long HTTP_PIPELINING_DEFAULT = 0L;
constexpr long HTTP_PIPELINING_MAX = 20L;
// Miscellaneous defaults
constexpr bool HTTP_USE_RETRY_AFTER_DEFAULT = true;
constexpr long HTTP_THROTTLE_RATE_DEFAULT = 0L;
// Tuning parameters
// Time worker thread sleeps after a pass through the
// request, ready and active queues.
constexpr int HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS = 2;
// Block allocation size (a tuning parameter) is found
// in bufferarray.h.
} // end namespace LLCore
#endif // _LLCORE_HTTP_INTERNAL_H_
+711
View File
@@ -0,0 +1,711 @@
/**
* @file _httplibcurl.cpp
* @brief Internal definitions of the Http libcurl thread
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httplibcurl.h"
#include "httpheaders.h"
#include "bufferarray.h"
#include "_httpoprequest.h"
#include "_httppolicy.h"
#include "llhttpconstants.h"
namespace
{
// Error testing and reporting for libcurl status codes
void check_curl_multi_code(CURLMcode code);
void check_curl_multi_code(CURLMcode code, int curl_setopt_option);
// This is a template because different 'option' values require different
// types for 'ARG'. Just pass them through unchanged (by value).
template <typename ARG>
void check_curl_multi_setopt(CURLM* handle, CURLMoption option, ARG argument)
{
CURLMcode code = curl_multi_setopt(handle, option, argument);
check_curl_multi_code(code, option);
}
static const char * const LOG_CORE("CoreHttp");
} // end anonymous namespace
namespace LLCore
{
HttpLibcurl::HttpLibcurl(HttpService * service)
: mService(service),
mHandleCache(),
mPolicyCount(0),
mMultiHandles(NULL),
mActiveHandles(NULL),
mDirtyPolicy(NULL)
{}
HttpLibcurl::~HttpLibcurl()
{
shutdown();
mService = NULL;
}
void HttpLibcurl::shutdown()
{
while (! mActiveOps.empty())
{
HttpOpRequest::ptr_t op(* mActiveOps.begin());
mActiveOps.erase(mActiveOps.begin());
cancelRequest(op);
}
if (mMultiHandles)
{
for (unsigned int policy_class(0); policy_class < mPolicyCount; ++policy_class)
{
if (mMultiHandles[policy_class])
{
curl_multi_cleanup(mMultiHandles[policy_class]);
mMultiHandles[policy_class] = 0;
}
}
delete [] mMultiHandles;
mMultiHandles = NULL;
delete [] mActiveHandles;
mActiveHandles = NULL;
delete [] mDirtyPolicy;
mDirtyPolicy = NULL;
}
mPolicyCount = 0;
}
void HttpLibcurl::start(int policy_count)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT);
llassert_always(! mMultiHandles); // One-time call only
mPolicyCount = policy_count;
mMultiHandles = new CURLM * [mPolicyCount];
mActiveHandles = new int [mPolicyCount];
mDirtyPolicy = new bool [mPolicyCount];
for (unsigned int policy_class(0); policy_class < mPolicyCount; ++policy_class)
{
if (NULL == (mMultiHandles[policy_class] = curl_multi_init()))
{
LL_ERRS(LOG_CORE) << "Failed to allocate multi handle in libcurl."
<< LL_ENDL;
}
mActiveHandles[policy_class] = 0;
mDirtyPolicy[policy_class] = false;
policyUpdated(policy_class);
}
}
// Give libcurl some cycles, invoke it's callbacks, process
// completed requests finalizing or issuing retries as needed.
//
// If active list goes empty *and* we didn't queue any
// requests for retry, we return a request for a hard
// sleep otherwise ask for a normal polling interval.
HttpService::ELoopSpeed HttpLibcurl::processTransport()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP);
// Give libcurl some cycles to do I/O & callbacks
for (unsigned int policy_class(0); policy_class < mPolicyCount; ++policy_class)
{
if (! mMultiHandles[policy_class])
{
// No handle, nothing to do.
continue;
}
if (! mActiveHandles[policy_class])
{
// If we've gone quiet and there's a dirty update, apply it,
// otherwise we're done.
if (mDirtyPolicy[policy_class])
{
policyUpdated(policy_class);
}
continue;
}
int running(0);
CURLMcode status(CURLM_CALL_MULTI_PERFORM);
do
{
LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("httppt - curl_multi_perform");
running = 0;
status = curl_multi_perform(mMultiHandles[policy_class], &running);
}
while (0 != running && CURLM_CALL_MULTI_PERFORM == status);
// Run completion on anything done
CURLMsg * msg(NULL);
int msgs_in_queue(0);
{
LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("httppt - curl_multi_info_read");
while ((msg = curl_multi_info_read(mMultiHandles[policy_class], &msgs_in_queue)))
{
if (CURLMSG_DONE == msg->msg)
{
CURL* handle(msg->easy_handle);
CURLcode result(msg->data.result);
completeRequest(mMultiHandles[policy_class], handle, result);
handle = NULL; // No longer valid on return
ret = HttpService::NORMAL; // If anything completes, we may have a free slot.
// Turning around quickly reduces connection gap by 7-10mS.
}
else if (CURLMSG_NONE == msg->msg)
{
// Ignore this... it shouldn't mean anything.
;
}
else
{
LL_WARNS_ONCE(LOG_CORE) << "Unexpected message from libcurl. Msg code: "
<< msg->msg
<< LL_ENDL;
}
msgs_in_queue = 0;
}
}
}
if (! mActiveOps.empty())
{
ret = HttpService::NORMAL;
}
return ret;
}
// Caller has provided us with a ref count on op.
void HttpLibcurl::addOp(const HttpOpRequest::ptr_t &op)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(op->mReqPolicy < mPolicyCount);
llassert_always(mMultiHandles[op->mReqPolicy] != NULL);
// Create standard handle
if (! op->prepareRequest(mService))
{
// Couldn't issue request, fail with notification
// *TODO: Need failure path
return;
}
// Make the request live
CURLMcode code;
code = curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle);
if (CURLM_OK != code)
{
// *TODO: Better cleanup and recovery but not much we can do here.
check_curl_multi_code(code);
return;
}
op->mCurlActive = true;
mActiveOps.insert(op);
++mActiveHandles[op->mReqPolicy];
if (op->mTracing > HTTP_TRACE_OFF)
{
HttpPolicy & policy(mService->getPolicy());
LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle: "
<< op->getHandle()
<< ", Actives: " << mActiveOps.size()
<< ", Readies: " << policy.getReadyCount(op->mReqPolicy)
<< LL_ENDL;
}
}
// Implements the transport part of any cancel operation.
// See if the handle is an active operation and if so,
// use the more complicated transport-based cancellation
// method to kill the request.
bool HttpLibcurl::cancel(HttpHandle handle)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op = HttpOpRequest::fromHandle<HttpOpRequest>(handle);
active_set_t::iterator it(mActiveOps.find(op));
if (mActiveOps.end() == it)
{
return false;
}
// Cancel request
cancelRequest(op);
// Drop references
mActiveOps.erase(it);
--mActiveHandles[op->mReqPolicy];
return true;
}
// *NOTE: cancelRequest logic parallels completeRequest logic.
// Keep them synchronized as necessary. Caller is expected to
// remove the op from the active list and release the op *after*
// calling this method. It must be called first to deliver the
// op to the reply queue with refcount intact.
void HttpLibcurl::cancelRequest(const HttpOpRequest::ptr_t &op)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
// Deactivate request
op->mCurlActive = false;
// Detach from multi and recycle handle
curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle);
mHandleCache.freeHandle(op->mCurlHandle);
op->mCurlHandle = NULL;
// Tracing
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, RequestCanceled, Handle: "
<< op->getHandle()
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
}
// Cancel op and deliver for notification
op->cancel();
}
// *NOTE: cancelRequest logic parallels completeRequest logic.
// Keep them synchronized as necessary.
bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpHandle ophandle(NULL);
CURLcode ccode(CURLE_OK);
ccode = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &ophandle);
if (ccode)
{
LL_WARNS(LOG_CORE) << "libcurl error: " << ccode << " Unable to retrieve operation handle from CURL handle" << LL_ENDL;
return false;
}
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(ophandle));
if (!op)
{
LL_WARNS() << "Unable to locate operation by handle. May have expired!" << LL_ENDL;
return false;
}
if (handle != op->mCurlHandle || ! op->mCurlActive)
{
LL_WARNS(LOG_CORE) << "libcurl handle and HttpOpRequest handle in disagreement or inactive request."
<< " Handle: " << static_cast<HttpHandle>(handle)
<< LL_ENDL;
return false;
}
active_set_t::iterator it(mActiveOps.find(op));
if (mActiveOps.end() == it)
{
LL_WARNS(LOG_CORE) << "libcurl completion for request not on active list. Continuing."
<< " Handle: " << static_cast<HttpHandle>(handle)
<< LL_ENDL;
return false;
}
// Deactivate request
mActiveOps.erase(it);
--mActiveHandles[op->mReqPolicy];
op->mCurlActive = false;
// Set final status of request if it hasn't failed by other mechanisms yet
if (op->mStatus)
{
op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status);
}
if (op->mStatus)
{
// note: CURLINFO_RESPONSE_CODE requires a long - https://curl.haxx.se/libcurl/c/CURLINFO_RESPONSE_CODE.html
long http_status(HTTP_OK);
if (handle)
{
ccode = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status);
if (ccode == CURLE_OK)
{
if (http_status >= 100 && http_status <= 999)
{
char * cont_type(NULL);
ccode = curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type);
if (ccode == CURLE_OK)
{
if (cont_type)
{
op->mReplyConType = cont_type;
}
}
else
{
LL_WARNS(LOG_CORE) << "CURL error:" << ccode << " Attempting to get content type." << LL_ENDL;
}
op->mStatus = HttpStatus(http_status);
}
else
{
LL_WARNS(LOG_CORE) << "Invalid HTTP response code ("
<< http_status << ") received from server."
<< LL_ENDL;
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
}
}
else
{
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
}
}
else
{
LL_WARNS(LOG_CORE) << "Attempt to retrieve status from NULL handle!" << LL_ENDL;
}
}
// <FS:ND> See if the requested URL matches a X-LL-URL header (if present) and the requested range.
// If not, we assume http pipelining havng gone out of sync. If yes, yield a 503 status and switch
// pipelining off.
bool bFailed = false;
if (op->mXLLURL.size())
{
std::string strURI = op->mReqURL;
size_t i = strURI.find("://");
if (i != std::string::npos)
i = strURI.find("/", i + 3);
if (i != std::string::npos)
strURI = strURI.substr(i);
if (strURI != op->mXLLURL)
{
LL_WARNS() << "HTTP pipelining out of sync! Asked for: " << strURI << " got " << op->mXLLURL << LL_ENDL;
op->mStatus = HttpStatus(HTTP_SERVICE_UNAVAILABLE);
bFailed = true;
}
}
if (!bFailed && (op->mReqOffset || op->mReqLength))
{
if (op->mReqOffset != op->mReplyOffset || (op->mReqLength && op->mReqLength < op->mReplyLength))
{
std::stringstream strm;
strm << "HTTP pipelining possibly out of sync, request wanted: " << op->mReqOffset << "-";
if (op->mReqLength)
strm << op->mReqLength + op->mReqLength -1;
strm << " got: " << op->mReplyOffset << "-" << op->mReplyOffset+op->mReplyLength-1;
strm << " url: " << op->mReqURL;
LL_WARNS() << strm.str() << LL_ENDL;
op->mStatus = HttpStatus(HTTP_SERVICE_UNAVAILABLE);
bFailed = true;
}
}
if (bFailed)
{
HttpPolicy & policy(mService->getPolicy());
for (unsigned int i = 0; i < mPolicyCount; ++ i)
{
HttpPolicyClass & options(policy.getClassOptions(i));
long lVal;
if (options.get(LLCore::HttpRequest::PO_PIPELINING_DEPTH, &lVal) && lVal)
{
options.set(LLCore::HttpRequest::PO_PIPELINING_DEPTH, 0);
mDirtyPolicy[i] = true;
policyUpdated(i);
}
}
}
// /</FS:ND>
if (multi_handle && handle)
{
// Detach from multi and recycle handle
curl_multi_remove_handle(multi_handle, handle);
mHandleCache.freeHandle(op->mCurlHandle);
}
else
{
LL_WARNS(LOG_CORE) << "Curl multi_handle or handle is NULL on remove! multi:"
<< std::hex << multi_handle << " h:" << std::hex << handle << std::dec << LL_ENDL;
}
op->mCurlHandle = NULL;
// Tracing
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, RequestComplete, Handle: "
<< op->getHandle()
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
}
// Dispatch to next stage
HttpPolicy & policy(mService->getPolicy());
bool still_active(policy.stageAfterCompletion(op));
return still_active;
}
int HttpLibcurl::getActiveCount() const
{
return static_cast<int>(mActiveOps.size());
}
int HttpLibcurl::getActiveCountInClass(unsigned int policy_class) const
{
llassert_always(policy_class < mPolicyCount);
return mActiveHandles ? mActiveHandles[policy_class] : 0;
}
void HttpLibcurl::policyUpdated(unsigned int policy_class)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (policy_class < 0 || policy_class >= mPolicyCount || ! mMultiHandles)
{
return;
}
HttpPolicy & policy(mService->getPolicy());
if (! mActiveHandles[policy_class])
{
// Clear to set options. As of libcurl 7.37.0, if a pipelining
// multi handle has active requests and you try to set the
// multi handle to non-pipelining, the library gets very angry
// and goes off the rails corrupting memory. A clue that you're
// about to crash is that you'll get a missing server response
// error (curl code 9). So, if options are to be set, we let
// the multi handle run out of requests, then set options, and
// re-enable request processing.
//
// All of this stall mechanism exists for this reason. If
// libcurl becomes more resilient later, it should be possible
// to remove all of this. The connection limit settings are fine,
// it's just that pipelined-to-non-pipelined transition that
// is fatal at the moment.
HttpPolicyClass & options(policy.getClassOptions(policy_class));
CURLM * multi_handle(mMultiHandles[policy_class]);
// Enable policy if stalled
policy.stallPolicy(policy_class, false);
mDirtyPolicy[policy_class] = false;
if (options.mPipelining > 1)
{
// We'll try to do pipelining on this multihandle
check_curl_multi_setopt(multi_handle,
CURLMOPT_PIPELINING,
1L);
check_curl_multi_setopt(multi_handle,
CURLMOPT_MAX_PIPELINE_LENGTH,
long(options.mPipelining));
check_curl_multi_setopt(multi_handle,
CURLMOPT_MAX_HOST_CONNECTIONS,
long(options.mPerHostConnectionLimit));
check_curl_multi_setopt(multi_handle,
CURLMOPT_MAX_TOTAL_CONNECTIONS,
long(options.mConnectionLimit));
}
else
{
check_curl_multi_setopt(multi_handle,
CURLMOPT_PIPELINING,
0L);
check_curl_multi_setopt(multi_handle,
CURLMOPT_MAX_HOST_CONNECTIONS,
0L);
check_curl_multi_setopt(multi_handle,
CURLMOPT_MAX_TOTAL_CONNECTIONS,
long(options.mConnectionLimit));
}
}
else if (! mDirtyPolicy[policy_class])
{
// Mark policy dirty and request a stall in the policy.
// When policy goes idle, we'll re-invoke this method
// and perform the change. Don't allow this thread to
// sleep while we're waiting for quiescence, we'll just
// stop processing.
mDirtyPolicy[policy_class] = true;
policy.stallPolicy(policy_class, true);
}
}
// ---------------------------------------
// HttpLibcurl::HandleCache
// ---------------------------------------
HttpLibcurl::HandleCache::HandleCache()
: mHandleTemplate(NULL)
{
mCache.reserve(50);
}
HttpLibcurl::HandleCache::~HandleCache()
{
if (mHandleTemplate)
{
curl_easy_cleanup(mHandleTemplate);
mHandleTemplate = NULL;
}
for (handle_cache_t::iterator it(mCache.begin()); mCache.end() != it; ++it)
{
curl_easy_cleanup(*it);
}
mCache.clear();
}
CURL * HttpLibcurl::HandleCache::getHandle()
{
CURL * ret(NULL);
if (! mCache.empty())
{
// Fastest path to handle
ret = mCache.back();
mCache.pop_back();
}
else if (mHandleTemplate)
{
// Still fast path
ret = curl_easy_duphandle(mHandleTemplate);
}
else
{
// When all else fails
ret = curl_easy_init();
}
return ret;
}
void HttpLibcurl::HandleCache::freeHandle(CURL * handle)
{
if (! handle)
{
return;
}
curl_easy_reset(handle);
if (! mHandleTemplate)
{
// Save the first freed handle as a template.
mHandleTemplate = handle;
}
else
{
// Otherwise add it to the cache
if (mCache.size() >= mCache.capacity())
{
mCache.reserve(mCache.capacity() + 50);
}
mCache.push_back(handle);
}
}
// ---------------------------------------
// Free functions
// ---------------------------------------
struct curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &headers, struct curl_slist * slist)
{
const HttpHeaders::const_iterator end(headers->end());
for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
{
static const char sep[] = ": ";
std::string header;
header.reserve((*it).first.size() + (*it).second.size() + sizeof(sep));
header.append((*it).first);
header.append(sep);
header.append((*it).second);
slist = curl_slist_append(slist, header.c_str());
}
return slist;
}
} // end namespace LLCore
namespace
{
void check_curl_multi_code(CURLMcode code, int curl_setopt_option)
{
if (CURLM_OK != code)
{
LL_WARNS(LOG_CORE) << "libcurl multi error detected: " << curl_multi_strerror(code)
<< ", curl_multi_setopt option: " << curl_setopt_option
<< LL_ENDL;
}
}
void check_curl_multi_code(CURLMcode code)
{
if (CURLM_OK != code)
{
LL_WARNS(LOG_CORE) << "libcurl multi error detected: " << curl_multi_strerror(code)
<< LL_ENDL;
}
}
} // end anonymous namespace
+223
View File
@@ -0,0 +1,223 @@
/**
* @file _httplibcurl.h
* @brief Declarations for internal class providing libcurl transport.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_LIBCURL_H_
#define _LLCORE_HTTP_LIBCURL_H_
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include <curl/curl.h>
#include <curl/multi.h>
#include <set>
#include "httprequest.h"
#include "_httpservice.h"
#include "_httpinternal.h"
namespace LLCore
{
class HttpPolicy;
class HttpOpRequest;
class HttpHeaders;
/// Implements libcurl-based transport for an HttpService instance.
///
/// Threading: Single-threaded. Other than for construction/destruction,
/// all methods are expected to be invoked in a single thread, typically
/// a worker thread of some sort.
class HttpLibcurl
{
public:
HttpLibcurl(HttpService * service);
virtual ~HttpLibcurl();
private:
HttpLibcurl(const HttpLibcurl &); // Not defined
void operator=(const HttpLibcurl &); // Not defined
public:
typedef std::shared_ptr<HttpOpRequest> opReqPtr_t;
/// Give cycles to libcurl to run active requests. Completed
/// operations (successful or failed) will be retried or handed
/// over to the reply queue as final responses.
///
/// @return Indication of how long this method is
/// willing to wait for next service call.
///
/// Threading: called by worker thread.
HttpService::ELoopSpeed processTransport();
/// Add request to the active list. Caller is expected to have
/// provided us with a reference count on the op to hold the
/// request. (No additional references will be added.)
///
/// Threading: called by worker thread.
void addOp(const opReqPtr_t & op);
/// One-time call to set the number of policy classes to be
/// serviced and to create the resources for each. Value
/// must agree with HttpPolicy::setPolicies() call.
///
/// Threading: called by init thread.
void start(int policy_count);
/// Synchronously stop libcurl operations. All active requests
/// are canceled and removed from libcurl's handling. Easy
/// handles are detached from their multi handles and released.
/// Multi handles are also released. Canceled requests are
/// completed with canceled status and made available on their
/// respective reply queues.
///
/// Can be restarted with a start() call.
///
/// Threading: called by worker thread.
void shutdown();
/// Return global and per-class counts of active requests.
///
/// Threading: called by worker thread.
int getActiveCount() const;
int getActiveCountInClass(unsigned int policy_class) const;
/// Attempt to cancel a request identified by handle.
///
/// Interface shadows HttpService's method.
///
/// @return True if handle was found and operation canceled.
///
/// Threading: called by worker thread.
bool cancel(HttpHandle handle);
/// Informs transport that a particular policy class has had
/// options changed and so should effect any transport state
/// change necessary to effect those changes. Used mainly for
/// initialization and dynamic option setting.
///
/// Threading: called by worker thread.
void policyUpdated(unsigned int policy_class);
/// Allocate a curl handle for caller. May be freed using
/// either the freeHandle() method or calling curl_easy_cleanup()
/// directly.
///
/// @return Libcurl handle (CURL *) or NULL on allocation
/// problem. Handle will be in curl_easy_reset()
/// condition.
///
/// Threading: callable by worker thread.
///
/// Deprecation: Expect this to go away after _httpoprequest is
/// refactored bringing code into this class.
CURL * getHandle()
{
return mHandleCache.getHandle();
}
protected:
/// Invoked when libcurl has indicated a request has been processed
/// to completion and we need to move the request to a new state.
bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status);
/// Invoked to cancel an active request, mainly during shutdown
/// and destroy.
void cancelRequest(const opReqPtr_t &op);
protected:
typedef std::set<opReqPtr_t> active_set_t;
/// Simple request handle cache for libcurl.
///
/// Handle creation is somewhat slow and chunky in libcurl and there's
/// a pretty good speedup to be had from handle re-use. So, a simple
/// vector is kept of 'freed' handles to be reused as needed. When
/// that is empty, the first freed handle is kept as a template for
/// handle duplication. This is still faster than creation from nothing.
/// And when that fails, we init fresh from curl_easy_init().
///
/// Handles allocated with getHandle() may be freed with either
/// freeHandle() or curl_easy_cleanup(). Choice may be dictated
/// by thread constraints.
///
/// Threading: Single-threaded. May only be used by a single thread,
/// typically the worker thread. If freeing requests' handles in an
/// unknown threading context, use curl_easy_cleanup() for safety.
class HandleCache
{
public:
HandleCache();
~HandleCache();
private:
HandleCache(const HandleCache &); // Not defined
void operator=(const HandleCache &); // Not defined
public:
/// Allocate a curl handle for caller. May be freed using
/// either the freeHandle() method or calling curl_easy_cleanup()
/// directly.
///
/// @return Libcurl handle (CURL *) or NULL on allocation
/// problem.
///
/// Threading: Single-thread (worker) only.
CURL * getHandle();
/// Free a libcurl handle acquired by whatever means. Thread
/// safety is left to the caller.
///
/// Threading: Single-thread (worker) only.
void freeHandle(CURL * handle);
protected:
typedef std::vector<CURL *> handle_cache_t;
protected:
CURL * mHandleTemplate; // Template for duplicating new handles
handle_cache_t mCache; // Cache of old handles
}; // end class HandleCache
protected:
HttpService * mService; // Simple reference, not owner
HandleCache mHandleCache; // Handle allocator, owner
active_set_t mActiveOps;
unsigned int mPolicyCount;
CURLM ** mMultiHandles; // One handle per policy class
int * mActiveHandles; // Active count per policy class
bool * mDirtyPolicy; // Dirty policy update waiting for stall (per pc)
}; // end class HttpLibcurl
} // end namespace LLCore
#endif // _LLCORE_HTTP_LIBCURL_H_
+73
View File
@@ -0,0 +1,73 @@
/**
* @file _httpopcancel.cpp
* @brief Definitions for internal class HttpOpCancel
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httpopcancel.h"
#include "httpcommon.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "_httpservice.h"
namespace LLCore
{
// ==================================
// HttpOpCancel
// ==================================
HttpOpCancel::HttpOpCancel(HttpHandle handle)
: HttpOperation(),
mHandle(handle)
{}
HttpOpCancel::~HttpOpCancel()
{}
// Immediately search for the request on various queues
// and cancel operations if found. Return the status of
// the search and cancel as the status of this request.
// The canceled request will return a canceled status to
// its handler.
void HttpOpCancel::stageFromRequest(HttpService * service)
{
if (! service->cancel(mHandle))
{
mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND);
}
addAsReply();
}
} // end namespace LLCore
+73
View File
@@ -0,0 +1,73 @@
/**
* @file _httpopcancel.h
* @brief Internal declarations for the HttpOpCancel subclass
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_OPCANCEL_H_
#define _LLCORE_HTTP_OPCANCEL_H_
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "httpcommon.h"
#include <curl/curl.h>
#include "_httpoperation.h"
#include "_refcounted.h"
namespace LLCore
{
/// HttpOpCancel requests that a previously issued request
/// be canceled, if possible. This includes active requests
/// that may be in the middle of an HTTP transaction. Any
/// completed request will not be canceled and will return
/// its final status unchanged and *this* request will complete
/// with an HE_HANDLE_NOT_FOUND error status.
class HttpOpCancel : public HttpOperation
{
public:
/// @param handle Handle of previously-issued request to
/// be canceled.
HttpOpCancel(HttpHandle handle);
virtual ~HttpOpCancel(); // Use release()
public:
virtual void stageFromRequest(HttpService *);
public:
// Request data
HttpHandle mHandle;
}; // end class HttpOpCancel
} // end namespace LLCore
#endif // _LLCORE_HTTP_OPCANCEL_H_
+305
View File
@@ -0,0 +1,305 @@
/**
* @file _httpoperation.cpp
* @brief Definitions for internal classes based on HttpOperation
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httpoperation.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "httprequest.h"
#include "_httprequestqueue.h"
#include "_httpreplyqueue.h"
#include "_httpservice.h"
#include "_httpinternal.h"
#include "lltimer.h"
namespace
{
static const char * const LOG_CORE("CoreHttp");
} // end anonymous namespace
namespace LLCore
{
// ==================================
// HttpOperation
// ==================================
/*static*/
HttpOperation::handleMap_t HttpOperation::mHandleMap;
LLCoreInt::HttpMutex HttpOperation::mOpMutex;
HttpOperation::HttpOperation():
std::enable_shared_from_this<HttpOperation>(),
mReplyQueue(),
mUserHandler(),
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
mTracing(HTTP_TRACE_OFF),
mMyHandle(LLCORE_HTTP_HANDLE_INVALID)
{
mMetricCreated = totalTime();
}
HttpOperation::~HttpOperation()
{
destroyHandle();
mReplyQueue.reset();
mUserHandler.reset();
}
void HttpOperation::setReplyPath(HttpReplyQueue::ptr_t reply_queue,
HttpHandler::ptr_t user_handler)
{
mReplyQueue.swap(reply_queue);
mUserHandler.swap(user_handler);
}
void HttpOperation::stageFromRequest(HttpService *)
{
// Default implementation should never be called. This
// indicates an operation making a transition that isn't
// defined.
LL_ERRS(LOG_CORE) << "Default stageFromRequest method may not be called."
<< LL_ENDL;
}
void HttpOperation::stageFromReady(HttpService *)
{
// Default implementation should never be called. This
// indicates an operation making a transition that isn't
// defined.
LL_ERRS(LOG_CORE) << "Default stageFromReady method may not be called."
<< LL_ENDL;
}
void HttpOperation::stageFromActive(HttpService *)
{
// Default implementation should never be called. This
// indicates an operation making a transition that isn't
// defined.
LL_ERRS(LOG_CORE) << "Default stageFromActive method may not be called."
<< LL_ENDL;
}
void HttpOperation::visitNotifier(HttpRequest *)
{
if (mUserHandler)
{
HttpResponse * response = new HttpResponse();
response->setStatus(mStatus);
mUserHandler->onCompleted(getHandle(), response);
response->release();
}
}
HttpStatus HttpOperation::cancel()
{
HttpStatus status;
return status;
}
// Handle methods
HttpHandle HttpOperation::getHandle()
{
if (mMyHandle == LLCORE_HTTP_HANDLE_INVALID)
return createHandle();
return mMyHandle;
}
HttpHandle HttpOperation::createHandle()
{
HttpHandle handle = static_cast<HttpHandle>(this);
{
LLCoreInt::HttpScopedLock lock(mOpMutex);
mHandleMap[handle] = shared_from_this();
mMyHandle = handle;
}
return mMyHandle;
}
void HttpOperation::destroyHandle()
{
if (mMyHandle == LLCORE_HTTP_HANDLE_INVALID)
return;
{
LLCoreInt::HttpScopedLock lock(mOpMutex);
handleMap_t::iterator it = mHandleMap.find(mMyHandle);
if (it != mHandleMap.end())
mHandleMap.erase(it);
}
}
/*static*/
HttpOperation::ptr_t HttpOperation::findByHandle(HttpHandle handle)
{
wptr_t weak;
if (!handle)
return ptr_t();
{
LLCoreInt::HttpScopedLock lock(mOpMutex);
handleMap_t::iterator it = mHandleMap.find(handle);
if (it == mHandleMap.end())
{
LL_WARNS("LLCore::HTTP") << "Could not find operation for handle " << handle << LL_ENDL;
return ptr_t();
}
weak = (*it).second;
}
if (!weak.expired())
return weak.lock();
return ptr_t();
}
void HttpOperation::addAsReply()
{
if (mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, ToReplyQueue, Handle: "
<< getHandle()
<< LL_ENDL;
}
if (mReplyQueue)
{
HttpOperation::ptr_t op = shared_from_this();
mReplyQueue->addOp(op);
}
}
// ==================================
// HttpOpStop
// ==================================
HttpOpStop::HttpOpStop()
: HttpOperation()
{}
HttpOpStop::~HttpOpStop()
{}
void HttpOpStop::stageFromRequest(HttpService * service)
{
// Do operations
service->stopRequested();
// Prepare response if needed
addAsReply();
}
// ==================================
// HttpOpNull
// ==================================
HttpOpNull::HttpOpNull()
: HttpOperation()
{}
HttpOpNull::~HttpOpNull()
{}
void HttpOpNull::stageFromRequest(HttpService * service)
{
// Perform op
// Nothing to perform. This doesn't fall into the libcurl
// ready/active queues, it just bounces over to the reply
// queue directly.
// Prepare response if needed
addAsReply();
}
// ==================================
// HttpOpSpin
// ==================================
HttpOpSpin::HttpOpSpin(int mode)
: HttpOperation(),
mMode(mode)
{}
HttpOpSpin::~HttpOpSpin()
{}
void HttpOpSpin::stageFromRequest(HttpService * service)
{
if (0 == mMode)
{
// Spin forever
while (true)
{
ms_sleep(100);
}
}
else
{
ms_sleep(1); // backoff interlock plumbing a bit
HttpOperation::ptr_t opptr = shared_from_this();
service->getRequestQueue().addOp(opptr);
}
}
} // end namespace LLCore
+285
View File
@@ -0,0 +1,285 @@
/**
* @file _httpoperation.h
* @brief Internal declarations for HttpOperation and sub-classes
*
* $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 _LLCORE_HTTP_OPERATION_H_
#define _LLCORE_HTTP_OPERATION_H_
#include "httpcommon.h"
#include "httprequest.h"
#include "_mutex.h"
namespace LLCore
{
class HttpReplyQueue;
class HttpHandler;
class HttpService;
/// HttpOperation is the base class for all request/reply
/// pairs.
///
/// Operations are expected to be of two types: immediate
/// and queued. Immediate requests go to the singleton
/// request queue and when picked up by the worker thread
/// are executed immediately and there results placed on
/// the supplied reply queue. Queued requests (namely for
/// HTTP operations), go to the request queue, are picked
/// up and moved to a ready queue where they're ordered by
/// priority and managed by the policy component, are
/// then activated issuing HTTP requests and moved to an
/// active list managed by the transport (libcurl) component
/// and eventually finalized when a response is available
/// and status and data return via reply queue.
///
/// To manage these transitions, derived classes implement
/// three methods: stageFromRequest, stageFromReady and
/// stageFromActive. Immediate requests will only override
/// stageFromRequest which will perform the operation and
/// return the result by invoking addAsReply() to put the
/// request on a reply queue. Queued requests will involve
/// all three stage methods.
///
/// Threading: not thread-safe. Base and derived classes
/// provide no locking. Instances move across threads
/// via queue-like interfaces that are thread compatible
/// and those interfaces establish the access rules.
class HttpOperation : private boost::noncopyable,
public std::enable_shared_from_this<HttpOperation>
{
public:
typedef std::shared_ptr<HttpOperation> ptr_t;
typedef std::weak_ptr<HttpOperation> wptr_t;
typedef std::shared_ptr<HttpReplyQueue> HttpReplyQueuePtr_t;
/// Threading: called by consumer thread.
HttpOperation();
/// Threading: called by any thread.
virtual ~HttpOperation(); // Use release()
public:
/// Register a reply queue and a handler for completion notifications.
///
/// Invokers of operations that want to receive notification that an
/// operation has been completed do so by binding a reply queue and
/// a handler object to the request.
///
/// @param reply_queue Pointer to the reply queue where completion
/// notifications are to be queued (typically
/// by addAsReply()). This will typically be
/// the reply queue referenced by the request
/// object. This method will increment the
/// refcount on the queue holding the queue
/// until delivery is complete. Using a reply_queue
/// even if the handler is NULL has some benefits
/// for memory deallocation by keeping it in the
/// originating thread.
///
/// @param handler Possibly NULL pointer to a non-refcounted
//// handler object to be invoked (onCompleted)
/// when the operation is finished. Note that
/// the handler object is never dereferenced
/// by the worker thread. This is passible data
/// until notification is performed.
///
/// Threading: called by consumer thread.
///
void setReplyPath(HttpReplyQueuePtr_t reply_queue,
HttpHandler::ptr_t handler);
/// The three possible staging steps in an operation's lifecycle.
/// Asynchronous requests like HTTP operations move from the
/// request queue to the ready queue via stageFromRequest. Then
/// from the ready queue to the active queue by stageFromReady. And
/// when complete, to the reply queue via stageFromActive and the
/// addAsReply utility.
///
/// Immediate mode operations (everything else) move from the
/// request queue to the reply queue directly via stageFromRequest
/// and addAsReply with no existence on the ready or active queues.
///
/// These methods will take out a reference count on the request,
/// caller only needs to dispose of its reference when done with
/// the request.
///
/// Threading: called by worker thread.
///
virtual void stageFromRequest(HttpService *);
virtual void stageFromReady(HttpService *);
virtual void stageFromActive(HttpService *);
/// Delivers a notification to a handler object on completion.
///
/// Once a request is complete and it has been removed from its
/// reply queue, a handler notification may be delivered by a
/// call to HttpRequest::update(). This method does the necessary
/// dispatching.
///
/// Threading: called by consumer thread.
///
virtual void visitNotifier(HttpRequest *);
/// Cancels the operation whether queued or active.
/// Final status of the request becomes canceled (an error) and
/// that will be delivered to caller via notification scheme.
///
/// Threading: called by worker thread.
///
virtual HttpStatus cancel();
/// Retrieves a unique handle for this operation.
HttpHandle getHandle();
template< class OPT >
static std::shared_ptr< OPT > fromHandle(HttpHandle handle)
{
ptr_t ptr = findByHandle(handle);
if (!ptr)
return std::shared_ptr< OPT >();
return std::dynamic_pointer_cast< OPT >(ptr);
}
protected:
/// Delivers request to reply queue on completion. After this
/// call, worker thread no longer accesses the object and it
/// is owned by the reply queue.
///
/// Threading: called by worker thread.
///
void addAsReply();
protected:
HttpReplyQueuePtr_t mReplyQueue;
HttpHandler::ptr_t mUserHandler;
public:
// Request Data
HttpRequest::policy_t mReqPolicy;
// Reply Data
HttpStatus mStatus;
// Tracing, debug and metrics
HttpTime mMetricCreated;
int mTracing;
private:
typedef std::map<HttpHandle, wptr_t> handleMap_t;
HttpHandle createHandle();
void destroyHandle();
HttpHandle mMyHandle;
static handleMap_t mHandleMap;
static LLCoreInt::HttpMutex mOpMutex;
protected:
static ptr_t findByHandle(HttpHandle handle);
}; // end class HttpOperation
/// HttpOpStop requests the servicing thread to shutdown
/// operations, cease pulling requests from the request
/// queue and release shared resources (particularly
/// those shared via reference count). The servicing
/// thread will then exit. The underlying thread object
/// remains so that another thread can join on the
/// servicing thread prior to final cleanup. The
/// request *does* generate a reply on the response
/// queue, if requested.
class HttpOpStop : public HttpOperation
{
public:
HttpOpStop();
virtual ~HttpOpStop();
private:
HttpOpStop(const HttpOpStop &); // Not defined
void operator=(const HttpOpStop &); // Not defined
public:
virtual void stageFromRequest(HttpService *);
}; // end class HttpOpStop
/// HttpOpNull is a do-nothing operation used for testing via
/// a basic loopback pattern. It's executed immediately by
/// the servicing thread which bounces a reply back to the
/// caller without any further delay.
class HttpOpNull : public HttpOperation
{
public:
HttpOpNull();
virtual ~HttpOpNull();
private:
HttpOpNull(const HttpOpNull &); // Not defined
void operator=(const HttpOpNull &); // Not defined
public:
virtual void stageFromRequest(HttpService *);
}; // end class HttpOpNull
/// HttpOpSpin is a test-only request that puts the worker
/// thread into a cpu spin. Used for unit tests and cleanup
/// evaluation. You do not want to use this in production.
class HttpOpSpin : public HttpOperation
{
public:
// 0 does a hard spin in the operation
// 1 does a soft spin continuously requeuing itself
HttpOpSpin(int mode);
virtual ~HttpOpSpin();
private:
HttpOpSpin(const HttpOpSpin &); // Not defined
void operator=(const HttpOpSpin &); // Not defined
public:
virtual void stageFromRequest(HttpService *);
protected:
int mMode;
}; // end class HttpOpSpin
} // end namespace LLCore
#endif // _LLCORE_HTTP_OPERATION_H_
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
/**
* @file _httpoprequest.h
* @brief Internal declarations for the HttpOpRequest subclass
*
* $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 _LLCORE_HTTP_OPREQUEST_H_
#define _LLCORE_HTTP_OPREQUEST_H_
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include <string>
#include <curl/curl.h>
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
#include "httpcommon.h"
#include "httprequest.h"
#include "_httpoperation.h"
#include "_refcounted.h"
#include "httpheaders.h"
#include "httpoptions.h"
namespace LLCore
{
class BufferArray;
/// HttpOpRequest requests a supported HTTP method invocation with
/// option and header overrides.
///
/// Essentially an RPC to get an HTTP GET, POST or PUT executed
/// asynchronously with options to override behaviors and HTTP
/// headers.
///
/// Constructor creates a raw object incapable of useful work.
/// A subsequent call to one of the setupXXX() methods provides
/// the information needed to make a working request which can
/// then be enqueued to a request queue.
///
class HttpOpRequest : public HttpOperation
{
public:
typedef std::shared_ptr<HttpOpRequest> ptr_t;
HttpOpRequest();
virtual ~HttpOpRequest(); // Use release()
private:
HttpOpRequest(const HttpOpRequest &); // Not defined
void operator=(const HttpOpRequest &); // Not defined
public:
enum EMethod
{
HOR_GET,
HOR_POST,
HOR_PUT,
HOR_DELETE,
HOR_PATCH,
HOR_COPY,
HOR_MOVE
};
static std::string methodToString(const EMethod &);
virtual void stageFromRequest(HttpService *);
virtual void stageFromReady(HttpService *);
virtual void stageFromActive(HttpService *);
virtual void visitNotifier(HttpRequest * request);
public:
/// Setup Methods
///
/// Basically an RPC setup for each type of HTTP method
/// invocation with one per method type. These are
/// generally invoked right after construction.
///
/// Threading: called by application thread
///
HttpStatus setupGet(HttpRequest::policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id,
const std::string & url,
size_t offset,
size_t len,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPost(HttpRequest::policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPut(HttpRequest::policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupDelete(HttpRequest::policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPatch(HttpRequest::policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupCopy(HttpRequest::policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupMove(HttpRequest::policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
// Internal method used to setup the libcurl options for a request.
// Does all the libcurl handle setup in one place.
//
// Threading: called by worker thread
//
HttpStatus prepareRequest(HttpService * service);
virtual HttpStatus cancel();
protected:
// Common setup for all the request methods.
//
// Threading: called by application thread
//
void setupCommon(HttpRequest::policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
// libcurl operational callbacks
//
// Threading: called by worker thread
//
static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata);
static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata);
static int seekCallback(void *data, curl_off_t offset, int origin);
static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata);
static CURLcode curlSslCtxCallback(CURL *curl, void *ssl_ctx, void *userptr);
static int sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param);
static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata);
protected:
unsigned int mProcFlags;
static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U;
static const unsigned int PF_SAVE_HEADERS = 0x00000002U;
static const unsigned int PF_USE_RETRY_AFTER = 0x00000004U;
HttpRequest::policyCallback_t mCallbackSSLVerify;
public:
// Request data
EMethod mReqMethod;
std::string mReqURL;
BufferArray * mReqBody;
off_t mReqOffset;
size_t mReqLength;
HttpHeaders::ptr_t mReqHeaders;
HttpOptions::ptr_t mReqOptions;
// Transport data
bool mCurlActive;
CURL * mCurlHandle;
HttpService * mCurlService;
curl_slist * mCurlHeaders;
size_t mCurlBodyPos;
char * mCurlTemp; // Scratch buffer for header processing
size_t mCurlTempLen;
// Result data
HttpStatus mStatus;
BufferArray * mReplyBody;
off_t mReplyOffset;
size_t mReplyLength;
size_t mReplyFullLength;
HttpHeaders::ptr_t mReplyHeaders;
std::string mReplyConType;
int mReplyRetryAfter;
std::string mXLLURL; // <FS:ND/> If we get a x-ll-url header, save it here, even if mReplyHeaders is not filled.
// Policy data
int mPolicyRetries;
int mPolicy503Retries;
HttpTime mPolicyRetryAt;
int mPolicyRetryLimit;
HttpTime mPolicyMinRetryBackoff; // initial delay between retries (mcs)
HttpTime mPolicyMaxRetryBackoff;
}; // end class HttpOpRequest
// ---------------------------------------
// Free functions
// ---------------------------------------
// Internal function to append the contents of an HttpHeaders
// instance to a curl_slist object.
curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &, curl_slist * slist);
} // end namespace LLCore
#endif // _LLCORE_HTTP_OPREQUEST_H_
+146
View File
@@ -0,0 +1,146 @@
/**
* @file _httpopsetget.cpp
* @brief Definitions for internal class HttpOpSetGet
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httpopsetget.h"
#include "httpcommon.h"
#include "httprequest.h"
#include "_httpservice.h"
#include "_httppolicy.h"
namespace LLCore
{
// ==================================
// HttpOpSetget
// ==================================
HttpOpSetGet::HttpOpSetGet()
: HttpOperation(),
mReqOption(HttpRequest::PO_CONNECTION_LIMIT),
mReqClass(HttpRequest::INVALID_POLICY_ID),
mReqDoSet(false),
mReqLongValue(0L),
mReplyLongValue(0L)
{}
HttpOpSetGet::~HttpOpSetGet()
{}
HttpStatus HttpOpSetGet::setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass)
{
HttpStatus status;
mReqOption = opt;
mReqClass = pclass;
return status;
}
HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value)
{
HttpStatus status;
if (! HttpService::sOptionDesc[opt].mIsLong)
{
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
if (! HttpService::sOptionDesc[opt].mIsDynamic)
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
mReqOption = opt;
mReqClass = pclass;
mReqDoSet = true;
mReqLongValue = value;
return status;
}
HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value)
{
HttpStatus status;
if (HttpService::sOptionDesc[opt].mIsLong)
{
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
if (! HttpService::sOptionDesc[opt].mIsDynamic)
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
mReqOption = opt;
mReqClass = pclass;
mReqDoSet = true;
mReqStrValue = value;
return status;
}
void HttpOpSetGet::stageFromRequest(HttpService * service)
{
if (mReqDoSet)
{
if (HttpService::sOptionDesc[mReqOption].mIsLong)
{
mStatus = service->setPolicyOption(mReqOption, mReqClass,
mReqLongValue, &mReplyLongValue);
}
else
{
mStatus = service->setPolicyOption(mReqOption, mReqClass,
mReqStrValue, &mReplyStrValue);
}
}
else
{
if (HttpService::sOptionDesc[mReqOption].mIsLong)
{
mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyLongValue);
}
else
{
mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyStrValue);
}
}
addAsReply();
}
} // end namespace LLCore
+91
View File
@@ -0,0 +1,91 @@
/**
* @file _httpopsetget.h
* @brief Internal declarations for the HttpOpSetGet subclass
*
* $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 _LLCORE_HTTP_OPSETGET_H_
#define _LLCORE_HTTP_OPSETGET_H_
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "httpcommon.h"
#include <curl/curl.h>
#include "_httpoperation.h"
#include "_refcounted.h"
namespace LLCore
{
/// HttpOpSetGet requests dynamic changes to policy and
/// configuration settings.
///
/// *NOTE: Expect this to change. Don't really like it yet.
///
/// *TODO: Can't return values to caller yet. Need to do
/// something better with HttpResponse and visitNotifier().
///
class HttpOpSetGet : public HttpOperation
{
public:
typedef std::shared_ptr<HttpOpSetGet> ptr_t;
HttpOpSetGet();
virtual ~HttpOpSetGet(); // Use release()
private:
HttpOpSetGet(const HttpOpSetGet &); // Not defined
void operator=(const HttpOpSetGet &); // Not defined
public:
/// Threading: called by application thread
HttpStatus setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass);
HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value);
HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value);
virtual void stageFromRequest(HttpService *);
public:
// Request data
HttpRequest::EPolicyOption mReqOption;
HttpRequest::policy_t mReqClass;
bool mReqDoSet;
long mReqLongValue;
std::string mReqStrValue;
// Reply Data
long mReplyLongValue;
std::string mReplyStrValue;
}; // end class HttpOpSetGet
} // end namespace LLCore
#endif // _LLCORE_HTTP_OPSETGET_H_
+66
View File
@@ -0,0 +1,66 @@
/**
* @file _httpopsetpriority.cpp
* @brief Definitions for internal classes based on HttpOpSetPriority
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if 0 // DEPRECATED
#include "_httpopsetpriority.h"
#include "httpresponse.h"
#include "httphandler.h"
#include "_httpservice.h"
namespace LLCore
{
HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority)
: HttpOperation(),
mHandle(handle),
mPriority(priority)
{}
HttpOpSetPriority::~HttpOpSetPriority()
{}
void HttpOpSetPriority::stageFromRequest(HttpService * service)
{
// Do operations
if (! service->changePriority(mHandle, mPriority))
{
// Request not found, fail the final status
mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND);
}
// Move directly to response queue
addAsReply();
}
} // end namespace LLCore
#endif
+72
View File
@@ -0,0 +1,72 @@
/**
* @file _httpsetpriority.h
* @brief Internal declarations for HttpSetPriority
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_SETPRIORITY_H_
#define _LLCORE_HTTP_SETPRIORITY_H_
#if 0 // DEPRECATED
#include "httpcommon.h"
#include "httprequest.h"
#include "_httpoperation.h"
#include "_refcounted.h"
namespace LLCore
{
/// HttpOpSetPriority is an immediate request that
/// searches the various queues looking for a given
/// request handle and changing it's priority if
/// found.
///
/// *NOTE: This will very likely be removed in the near future
/// when priority is removed from the library.
class HttpOpSetPriority : public HttpOperation
{
public:
HttpOpSetPriority(HttpHandle handle);
virtual ~HttpOpSetPriority();
private:
HttpOpSetPriority(const HttpOpSetPriority &); // Not defined
void operator=(const HttpOpSetPriority &); // Not defined
public:
virtual void stageFromRequest(HttpService *);
protected:
// Request Data
HttpHandle mHandle;
}; // end class HttpOpSetPriority
} // end namespace LLCore
#endif
#endif // _LLCORE_HTTP_SETPRIORITY_H_
+456
View File
@@ -0,0 +1,456 @@
/**
* @file _httppolicy.cpp
* @brief Internal definitions of the Http policy thread
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "_httppolicy.h"
#include "_httpoprequest.h"
#include "_httpservice.h"
#include "_httplibcurl.h"
#include "_httppolicyclass.h"
#include "lltimer.h"
#include "httpstats.h"
namespace
{
static const char * const LOG_CORE("CoreHttp");
} // end anonymous namespace
namespace LLCore
{
// Per-policy-class data for a running system.
// Collection of queues, options and other data
// for a single policy class.
//
// Threading: accessed only by worker thread
struct HttpPolicy::ClassState
{
public:
ClassState()
: mThrottleEnd(0),
mThrottleLeft(0L),
mRequestCount(0L),
mStallStaging(false)
{}
HttpReadyQueue mReadyQueue;
HttpRetryQueue mRetryQueue;
HttpPolicyClass mOptions;
HttpTime mThrottleEnd;
long mThrottleLeft;
long mRequestCount;
bool mStallStaging;
};
HttpPolicy::HttpPolicy(HttpService * service)
: mService(service)
{
// Create default class
mClasses.push_back(new ClassState());
}
HttpPolicy::~HttpPolicy()
{
shutdown();
for (class_list_t::iterator it(mClasses.begin()); it != mClasses.end(); ++it)
{
delete (*it);
}
mClasses.clear();
mService = NULL;
}
HttpRequest::policy_t HttpPolicy::createPolicyClass()
{
const HttpRequest::policy_t policy_class(static_cast<HttpRequest::policy_t>(mClasses.size()));
if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
{
return HttpRequest::INVALID_POLICY_ID;
}
mClasses.push_back(new ClassState());
return policy_class;
}
void HttpPolicy::shutdown()
{
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
ClassState & state(*mClasses[policy_class]);
HttpRetryQueue & retryq(state.mRetryQueue);
while (! retryq.empty())
{
HttpOpRequest::ptr_t op(retryq.top());
retryq.pop();
op->cancel();
}
HttpReadyQueue & readyq(state.mReadyQueue);
while (! readyq.empty())
{
HttpOpRequest::ptr_t op(readyq.top());
readyq.pop();
op->cancel();
}
}
}
void HttpPolicy::start()
{
}
void HttpPolicy::addOp(const HttpOpRequest::ptr_t &op)
{
const int policy_class(op->mReqPolicy);
op->mPolicyRetries = 0;
op->mPolicy503Retries = 0;
mClasses[policy_class]->mReadyQueue.push(op);
}
void HttpPolicy::retryOp(const HttpOpRequest::ptr_t &op)
{
static const HttpStatus error_503(503);
const HttpTime now(totalTime());
const int policy_class(op->mReqPolicy);
HttpTime delta_min = op->mPolicyMinRetryBackoff;
HttpTime delta_max = op->mPolicyMaxRetryBackoff;
// mPolicyRetries limited to 100
U32 delta_factor = op->mPolicyRetries <= 10 ? 1 << op->mPolicyRetries : 1024;
HttpTime delta = llmin(delta_min * delta_factor, delta_max);
bool external_delta(false);
if (op->mReplyRetryAfter > 0 && op->mReplyRetryAfter < 30)
{
delta = op->mReplyRetryAfter * U64L(1000000);
external_delta = true;
}
op->mPolicyRetryAt = now + delta;
++op->mPolicyRetries;
if (error_503 == op->mStatus)
{
++op->mPolicy503Retries;
}
LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " retry " << op->mPolicyRetries
<< " scheduled in " << (delta / HttpTime(1000))
<< " mS (" << (external_delta ? "external" : "internal")
<< "). Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, ToRetryQueue, Handle: "
<< op->getHandle()
<< ", Delta: " << (delta / HttpTime(1000))
<< ", Retries: " << op->mPolicyRetries
<< LL_ENDL;
}
mClasses[policy_class]->mRetryQueue.push(op);
}
// Attempt to deliver requests to the transport layer.
//
// Tries to find HTTP requests for each policy class with
// available capacity. Starts with the retry queue first
// looking for requests that have waited long enough then
// moves on to the ready queue.
//
// If all queues are empty, will return an indication that
// the worker thread may sleep hard otherwise will ask for
// normal polling frequency.
//
// Implements a client-side request rate throttle as well.
// This is intended to mimic and predict throttling behavior
// of grid services but that is difficult to do with different
// time bases. This also represents a rigid coupling between
// viewer and server that makes it hard to change parameters
// and I hope we can make this go away with pipelining.
//
HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;// <FS:Beq/> missing http trace
const HttpTime now(totalTime());
HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP);
HttpLibcurl & transport(mService->getTransport());
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
ClassState & state(*mClasses[policy_class]);
HttpRetryQueue & retryq(state.mRetryQueue);
HttpReadyQueue & readyq(state.mReadyQueue);
if (state.mStallStaging)
{
// Stalling but don't sleep. Need to complete operations
// and get back to servicing queues. Do this test before
// the retryq/readyq test or you'll get stalls until you
// click a setting or an asset request comes in.
result = HttpService::NORMAL;
continue;
}
if (retryq.empty() && readyq.empty())
{
continue;
}
const bool throttle_enabled(state.mOptions.mThrottleRate > 0L);
const bool throttle_current(throttle_enabled && now < state.mThrottleEnd);
if (throttle_current && state.mThrottleLeft <= 0)
{
// Throttled condition, don't serve this class but don't sleep hard.
result = HttpService::NORMAL;
continue;
}
int active(transport.getActiveCountInClass(policy_class));
int active_limit(state.mOptions.mPipelining > 1L
? (state.mOptions.mPerHostConnectionLimit
* state.mOptions.mPipelining)
: state.mOptions.mConnectionLimit);
int needed(active_limit - active); // Expect negatives here
if (needed > 0)
{
// First see if we have any retries...
while (needed > 0 && ! retryq.empty())
{
HttpOpRequest::ptr_t op(retryq.top());
if (op->mPolicyRetryAt > now)
break;
retryq.pop();
op->stageFromReady(mService);
op.reset();
++state.mRequestCount;
--needed;
if (throttle_enabled)
{
if (now >= state.mThrottleEnd)
{
// Throttle expired, move to next window
LL_DEBUGS(LOG_CORE) << "Throttle expired with " << state.mThrottleLeft
<< " requests to go and " << state.mRequestCount
<< " requests issued." << LL_ENDL;
state.mThrottleLeft = state.mOptions.mThrottleRate;
state.mThrottleEnd = now + HttpTime(1000000);
}
if (--state.mThrottleLeft <= 0)
{
goto throttle_on;
}
}
}
// Now go on to the new requests...
while (needed > 0 && ! readyq.empty())
{
HttpOpRequest::ptr_t op(readyq.top());
readyq.pop();
op->stageFromReady(mService);
op.reset();
++state.mRequestCount;
--needed;
if (throttle_enabled)
{
if (now >= state.mThrottleEnd)
{
// Throttle expired, move to next window
LL_DEBUGS(LOG_CORE) << "Throttle expired with " << state.mThrottleLeft
<< " requests to go and " << state.mRequestCount
<< " requests issued." << LL_ENDL;
state.mThrottleLeft = state.mOptions.mThrottleRate;
state.mThrottleEnd = now + HttpTime(1000000);
}
if (--state.mThrottleLeft <= 0)
{
goto throttle_on;
}
}
}
}
throttle_on:
if (! readyq.empty() || ! retryq.empty())
{
// If anything is ready, continue looping...
result = HttpService::NORMAL;
}
} // end foreach policy_class
return result;
}
bool HttpPolicy::cancel(HttpHandle handle)
{
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
ClassState & state(*mClasses[policy_class]);
// Scan retry queue
HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;)
{
HttpRetryQueue::container_type::iterator cur(iter++);
if ((*cur)->getHandle() == handle)
{
HttpOpRequest::ptr_t op(*cur);
c1.erase(cur); // All iterators are now invalidated
op->cancel();
return true;
}
}
// Scan ready queue
HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container());
for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;)
{
HttpReadyQueue::container_type::iterator cur(iter++);
if ((*cur)->getHandle() == handle)
{
HttpOpRequest::ptr_t op(*cur);
c2.erase(cur); // All iterators are now invalidated
op->cancel();
return true;
}
}
}
return false;
}
bool HttpPolicy::stageAfterCompletion(const HttpOpRequest::ptr_t &op)
{
// Retry or finalize
if (! op->mStatus)
{
// *DEBUG: For "[curl:bugs] #1420" tests. This will interfere
// with unit tests due to allocation retention by logging code.
// But you won't be checking this in enabled.
#if 0
if (op->mStatus == HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT))
{
LL_WARNS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " timed out."
<< LL_ENDL;
}
#endif
// If this failed, we might want to retry.
if (op->mPolicyRetries < op->mPolicyRetryLimit && op->mStatus.isRetryable())
{
// Okay, worth a retry.
retryOp(op);
return true; // still active/ready
}
}
// This op is done, finalize it delivering it to the reply queue...
if (! op->mStatus)
{
LL_WARNS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " failed after " << op->mPolicyRetries
<< " retries. Reason: " << op->mStatus.toString()
<< " (" << op->mStatus.toTerseString() << ")"
<< LL_ENDL;
}
else if (op->mPolicyRetries)
{
LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " succeeded on retry " << op->mPolicyRetries << "."
<< LL_ENDL;
}
op->stageFromActive(mService);
HTTPStats::instance().recordResultCode(op->mStatus.getType());
return false; // not active
}
HttpPolicyClass & HttpPolicy::getClassOptions(HttpRequest::policy_t pclass)
{
llassert_always(pclass >= 0 && pclass < mClasses.size());
return mClasses[pclass]->mOptions;
}
int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
{
if (policy_class < mClasses.size())
{
return static_cast<int>((mClasses[policy_class]->mReadyQueue.size()
+ mClasses[policy_class]->mRetryQueue.size()));
}
return 0;
}
bool HttpPolicy::stallPolicy(HttpRequest::policy_t policy_class, bool stall)
{
bool ret(false);
if (policy_class < mClasses.size())
{
ret = mClasses[policy_class]->mStallStaging;
mClasses[policy_class]->mStallStaging = stall;
}
return ret;
}
} // end namespace LLCore
+176
View File
@@ -0,0 +1,176 @@
/**
* @file _httppolicy.h
* @brief Declarations for internal class enforcing policy decisions.
*
* $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 _LLCORE_HTTP_POLICY_H_
#define _LLCORE_HTTP_POLICY_H_
#include "httprequest.h"
#include "_httpservice.h"
#include "_httpreadyqueue.h"
#include "_httpretryqueue.h"
#include "_httppolicyglobal.h"
#include "_httppolicyclass.h"
#include "_httpinternal.h"
namespace LLCore
{
class HttpReadyQueue;
class HttpOpRequest;
/// Implements class-based queuing policies for an HttpService instance.
///
/// Threading: Single-threaded. Other than for construction/destruction,
/// all methods are expected to be invoked in a single thread, typically
/// a worker thread of some sort.
class HttpPolicy
{
public:
HttpPolicy(HttpService *);
virtual ~HttpPolicy();
private:
HttpPolicy(const HttpPolicy &); // Not defined
void operator=(const HttpPolicy &); // Not defined
public:
typedef std::shared_ptr<HttpOpRequest> opReqPtr_t;
/// Threading: called by init thread.
HttpRequest::policy_t createPolicyClass();
/// Cancel all ready and retry requests sending them to
/// their notification queues. Release state resources
/// making further request handling impossible.
///
/// Threading: called by worker thread
void shutdown();
/// Deliver policy definitions and enable handling of
/// requests. One-time call invoked before starting
/// the worker thread.
///
/// Threading: called by init thread
void start();
/// Give the policy layer some cycles to scan the ready
/// queue promoting higher-priority requests to active
/// as permited.
///
/// @return Indication of how soon this method
/// should be called again.
///
/// Threading: called by worker thread
HttpService::ELoopSpeed processReadyQueue();
/// Add request to a ready queue. Caller is expected to have
/// provided us with a reference count to hold the request. (No
/// additional references will be added.)
///
/// OpRequest is owned by the request queue after this call
/// and should not be modified by anyone until retrieved
/// from queue.
///
/// Threading: called by worker thread
void addOp(const opReqPtr_t &);
/// Similar to addOp, used when a caller wants to retry a
/// request that has failed. It's placed on a special retry
/// queue but ordered by retry time not priority. Otherwise,
/// handling is the same and retried operations are considered
/// before new ones but that doesn't guarantee completion
/// order.
///
/// Threading: called by worker thread
void retryOp(const opReqPtr_t &);
/// Attempt to cancel a previous request.
/// Shadows HttpService's method as well
///
/// Threading: called by worker thread
bool cancel(HttpHandle handle);
/// When transport is finished with an op and takes it off the
/// active queue, it is delivered here for dispatch. Policy
/// may send it back to the ready/retry queues if it needs another
/// go or we may finalize it and send it on to the reply queue.
///
/// @return Returns true of the request is still active
/// or ready after staging, false if has been
/// sent on to the reply queue.
///
/// Threading: called by worker thread
bool stageAfterCompletion(const opReqPtr_t &op);
/// Get a reference to global policy options. Caller is expected
/// to do context checks like no setting once running. These
/// are done, for example, in @see HttpService interfaces.
///
/// Threading: called by any thread *but* the object may
/// only be modified by the worker thread once running.
HttpPolicyGlobal & getGlobalOptions()
{
return mGlobalOptions;
}
/// Get a reference to class policy options. Caller is expected
/// to do context checks like no setting once running. These
/// are done, for example, in @see HttpService interfaces.
///
/// Threading: called by any thread *but* the object may
/// only be modified by the worker thread once running and
/// read accesses by other threads are exposed to races at
/// that point.
HttpPolicyClass & getClassOptions(HttpRequest::policy_t pclass);
/// Get ready counts for a particular policy class
///
/// Threading: called by worker thread
int getReadyCount(HttpRequest::policy_t policy_class) const;
/// Stall (or unstall) a policy class preventing requests from
/// transitioning to an active state. Used to allow an HTTP
/// request policy to empty prior to changing settings or state
/// that isn't tolerant of changes when work is outstanding.
///
/// Threading: called by worker thread
bool stallPolicy(HttpRequest::policy_t policy_class, bool stall);
protected:
struct ClassState;
typedef std::vector<ClassState *> class_list_t;
HttpPolicyGlobal mGlobalOptions;
class_list_t mClasses;
HttpService * mService; // Naked pointer, not refcounted, not owner
}; // end class HttpPolicy
} // end namespace LLCore
#endif // _LLCORE_HTTP_POLICY_H_
+125
View File
@@ -0,0 +1,125 @@
/**
* @file _httppolicyclass.cpp
* @brief Definitions for internal class defining class policy option.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httppolicyclass.h"
#include "_httpinternal.h"
namespace LLCore
{
HttpPolicyClass::HttpPolicyClass()
: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mPipelining(HTTP_PIPELINING_DEFAULT),
mThrottleRate(HTTP_THROTTLE_RATE_DEFAULT)
{}
HttpPolicyClass::~HttpPolicyClass()
{}
HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
{
if (this != &other)
{
mConnectionLimit = other.mConnectionLimit;
mPerHostConnectionLimit = other.mPerHostConnectionLimit;
mPipelining = other.mPipelining;
mThrottleRate = other.mThrottleRate;
}
return *this;
}
HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
: mConnectionLimit(other.mConnectionLimit),
mPerHostConnectionLimit(other.mPerHostConnectionLimit),
mPipelining(other.mPipelining),
mThrottleRate(other.mThrottleRate)
{}
HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)
{
switch (opt)
{
case HttpRequest::PO_CONNECTION_LIMIT:
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
break;
case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);
break;
case HttpRequest::PO_PIPELINING_DEPTH:
mPipelining = llclamp(value, 0L, HTTP_PIPELINING_MAX);
break;
case HttpRequest::PO_THROTTLE_RATE:
mThrottleRate = llclamp(value, 0L, 1000000L);
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) const
{
switch (opt)
{
case HttpRequest::PO_CONNECTION_LIMIT:
*value = mConnectionLimit;
break;
case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
*value = mPerHostConnectionLimit;
break;
case HttpRequest::PO_PIPELINING_DEPTH:
*value = mPipelining;
break;
case HttpRequest::PO_THROTTLE_RATE:
*value = mThrottleRate;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
} // end namespace LLCore
+71
View File
@@ -0,0 +1,71 @@
/**
* @file _httppolicyclass.h
* @brief Declarations for internal class defining policy class options.
*
* $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 _LLCORE_HTTP_POLICY_CLASS_H_
#define _LLCORE_HTTP_POLICY_CLASS_H_
#include "httprequest.h"
namespace LLCore
{
/// Options struct for per-class policy options.
///
/// Combines both raw blob data access with semantics-enforcing
/// set/get interfaces. For internal operations by the worker
/// thread, just grab the setting directly from instance and test/use
/// as needed. When attached to external APIs (the public API
/// options interfaces) the set/get methods are available to
/// enforce correct ranges, data types, contexts, etc. and suitable
/// status values are returned.
///
/// Threading: Single-threaded. In practice, init thread before
/// worker starts, worker thread after.
class HttpPolicyClass
{
public:
HttpPolicyClass();
~HttpPolicyClass();
HttpPolicyClass & operator=(const HttpPolicyClass &);
HttpPolicyClass(const HttpPolicyClass &); // Not defined
public:
HttpStatus set(HttpRequest::EPolicyOption opt, long value);
HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
public:
long mConnectionLimit;
long mPerHostConnectionLimit;
long mPipelining;
long mThrottleRate;
}; // end class HttpPolicyClass
} // end namespace LLCore
#endif // _LLCORE_HTTP_POLICY_CLASS_H_
+190
View File
@@ -0,0 +1,190 @@
/**
* @file _httppolicyglobal.cpp
* @brief Definitions for internal class defining global policy option.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httppolicyglobal.h"
#include "_httpinternal.h"
namespace LLCore
{
HttpPolicyGlobal::HttpPolicyGlobal()
: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mTrace(HTTP_TRACE_OFF),
mUseLLProxy(0)
{}
HttpPolicyGlobal::~HttpPolicyGlobal()
{}
HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
{
if (this != &other)
{
mConnectionLimit = other.mConnectionLimit;
mCAPath = other.mCAPath;
mCAFile = other.mCAFile;
mHttpProxy = other.mHttpProxy;
mTrace = other.mTrace;
mUseLLProxy = other.mUseLLProxy;
}
return *this;
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, long value)
{
switch (opt)
{
case HttpRequest::PO_CONNECTION_LIMIT:
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
break;
case HttpRequest::PO_TRACE:
mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));
break;
case HttpRequest::PO_LLPROXY:
mUseLLProxy = llclamp(value, 0L, 1L);
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::string & value)
{
switch (opt)
{
case HttpRequest::PO_CA_PATH:
LL_DEBUGS("CoreHttp") << "Setting global CA Path to " << value << LL_ENDL;
mCAPath = value;
break;
case HttpRequest::PO_CA_FILE:
LL_DEBUGS("CoreHttp") << "Setting global CA File to " << value << LL_ENDL;
mCAFile = value;
break;
case HttpRequest::PO_HTTP_PROXY:
LL_DEBUGS("CoreHttp") << "Setting global Proxy to " << value << LL_ENDL;
mHttpProxy = value;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value)
{
switch (opt)
{
case HttpRequest::PO_SSL_VERIFY_CALLBACK:
mSslCtxCallback = value;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
{
switch (opt)
{
case HttpRequest::PO_CONNECTION_LIMIT:
*value = mConnectionLimit;
break;
case HttpRequest::PO_TRACE:
*value = mTrace;
break;
case HttpRequest::PO_LLPROXY:
*value = mUseLLProxy;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * value) const
{
switch (opt)
{
case HttpRequest::PO_CA_PATH:
*value = mCAPath;
break;
case HttpRequest::PO_CA_FILE:
*value = mCAFile;
break;
case HttpRequest::PO_HTTP_PROXY:
*value = mHttpProxy;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const
{
switch (opt)
{
case HttpRequest::PO_SSL_VERIFY_CALLBACK:
*value = mSslCtxCallback;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
} // end namespace LLCore
+80
View File
@@ -0,0 +1,80 @@
/**
* @file _httppolicyglobal.h
* @brief Declarations for internal class defining global policy option.
*
* $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 _LLCORE_HTTP_POLICY_GLOBAL_H_
#define _LLCORE_HTTP_POLICY_GLOBAL_H_
#include "httprequest.h"
namespace LLCore
{
/// Options struct for global policy options.
///
/// Combines both raw blob data access with semantics-enforcing
/// set/get interfaces. For internal operations by the worker
/// thread, just grab the setting directly from instance and test/use
/// as needed. When attached to external APIs (the public API
/// options interfaces) the set/get methods are available to
/// enforce correct ranges, data types, contexts, etc. and suitable
/// status values are returned.
///
/// Threading: Single-threaded. In practice, init thread before
/// worker starts, worker thread after.
class HttpPolicyGlobal
{
public:
HttpPolicyGlobal();
~HttpPolicyGlobal();
HttpPolicyGlobal & operator=(const HttpPolicyGlobal &);
private:
HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined
public:
HttpStatus set(HttpRequest::EPolicyOption opt, long value);
HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
HttpStatus set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value);
HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
HttpStatus get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const;
public:
long mConnectionLimit;
std::string mCAPath;
std::string mCAFile;
std::string mHttpProxy;
long mTrace;
long mUseLLProxy;
HttpRequest::policyCallback_t mSslCtxCallback;
}; // end class HttpPolicyGlobal
} // end namespace LLCore
#endif // _LLCORE_HTTP_POLICY_GLOBAL_H_
+124
View File
@@ -0,0 +1,124 @@
/**
* @file _httpreadyqueue.h
* @brief Internal declaration for the operation ready queue
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_READY_QUEUE_H_
#define _LLCORE_HTTP_READY_QUEUE_H_
#include <queue>
#include "_httpinternal.h"
#include "_httpoprequest.h"
namespace LLCore
{
/// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects.
///
/// This uses the priority_queue adaptor class to provide the queue
/// as well as the ordering scheme while allowing us access to the
/// raw container if we follow a few simple rules. One of the more
/// important of those rules is that any iterator becomes invalid
/// on element erasure. So pay attention.
///
/// If LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY tests true, the class
/// implements a std::priority_queue interface but on std::deque
/// behavior to eliminate sensitivity to priority. In the future,
/// this will likely become the only behavior or it may become
/// a run-time election.
///
/// Threading: not thread-safe. Expected to be used entirely by
/// a single thread, typically a worker thread of some sort.
#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
typedef std::deque<HttpOpRequest::ptr_t> HttpReadyQueueBase;
#else
typedef std::priority_queue<HttpOpRequest::ptr_t,
std::deque<HttpOpRequest::ptr_t>,
LLCore::HttpOpRequestCompare> HttpReadyQueueBase;
#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
class HttpReadyQueue : public HttpReadyQueueBase
{
public:
HttpReadyQueue()
: HttpReadyQueueBase()
{}
~HttpReadyQueue()
{}
protected:
HttpReadyQueue(const HttpReadyQueue &); // Not defined
void operator=(const HttpReadyQueue &); // Not defined
public:
#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
// Types and methods needed to make a std::deque look
// more like a std::priority_queue, at least for our
// purposes.
typedef HttpReadyQueueBase container_type;
const_reference top() const
{
return front();
}
void pop()
{
pop_front();
}
void push(const value_type & v)
{
push_back(v);
}
#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
const container_type & get_container() const
{
return *this;
}
container_type & get_container()
{
return *this;
}
}; // end class HttpReadyQueue
} // end namespace LLCore
#endif // _LLCORE_HTTP_READY_QUEUE_H_
+97
View File
@@ -0,0 +1,97 @@
/**
* @file _httpreplyqueue.cpp
* @brief Internal definitions for the operation reply queue
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httpreplyqueue.h"
#include "_mutex.h"
#include "_thread.h"
#include "_httpoperation.h"
using namespace LLCoreInt;
namespace LLCore
{
HttpReplyQueue::HttpReplyQueue()
{
}
HttpReplyQueue::~HttpReplyQueue()
{
mQueue.clear();
}
void HttpReplyQueue::addOp(const HttpReplyQueue::opPtr_t &op)
{
{
HttpScopedLock lock(mQueueMutex);
mQueue.push_back(op);
}
}
HttpReplyQueue::opPtr_t HttpReplyQueue::fetchOp()
{
HttpOperation::ptr_t result;
{
HttpScopedLock lock(mQueueMutex);
if (mQueue.empty())
return opPtr_t();
result = mQueue.front();
mQueue.erase(mQueue.begin());
}
// Caller also acquires the reference count
return result;
}
void HttpReplyQueue::fetchAll(OpContainer & ops)
{
// Not valid putting something back on the queue...
llassert_always(ops.empty());
{
HttpScopedLock lock(mQueueMutex);
if (! mQueue.empty())
{
mQueue.swap(ops);
}
}
}
} // end namespace LLCore
+107
View File
@@ -0,0 +1,107 @@
/**
* @file _httpreplyqueue.h
* @brief Internal declarations for the operation reply queue.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_REPLY_QUEUE_H_
#define _LLCORE_HTTP_REPLY_QUEUE_H_
#include "_refcounted.h"
#include "_mutex.h"
#include "boost/noncopyable.hpp"
namespace LLCore
{
class HttpOperation;
/// Almost identical to the HttpRequestQueue class but
/// whereas that class is a singleton and is known to the
/// HttpService object, this queue is 1:1 with HttpRequest
/// instances and isn't explicitly referenced by the
/// service object. Instead, HttpOperation objects that
/// want to generate replies back to their creators also
/// keep references to the corresponding HttpReplyQueue.
/// The HttpService plumbing then simply delivers replies
/// to the requested reply queue.
///
/// One result of that is that the fetch operations do
/// not have a wait forever option. The service object
/// doesn't keep handles on everything it would need to
/// notify so it can't wake up sleepers should it need to
/// shutdown. So only non-blocking or timed-blocking modes
/// are anticipated. These are how most application consumers
/// will be coded anyway so it shouldn't be too much of a
/// burden.
class HttpReplyQueue : private boost::noncopyable
{
public:
typedef std::shared_ptr<HttpOperation> opPtr_t;
typedef std::shared_ptr<HttpReplyQueue> ptr_t;
HttpReplyQueue();
virtual ~HttpReplyQueue();
public:
typedef std::vector< opPtr_t > OpContainer;
/// Insert an object at the back of the reply queue.
///
/// Library also takes possession of one reference count to pass
/// through the queue.
///
/// Threading: callable by any thread.
void addOp(const opPtr_t &op);
/// Fetch an operation from the head of the queue. Returns
/// NULL if none exists.
///
/// Caller acquires reference count on returned operation.
///
/// Threading: callable by any thread.
opPtr_t fetchOp();
/// Caller acquires reference count on each returned operation
///
/// Threading: callable by any thread.
void fetchAll(OpContainer & ops);
protected:
OpContainer mQueue;
LLCoreInt::HttpMutex mQueueMutex;
}; // end class HttpReplyQueue
} // end namespace LLCore
#endif // _LLCORE_HTTP_REPLY_QUEUE_H_
+162
View File
@@ -0,0 +1,162 @@
/**
* @file _httprequestqueue.cpp
* @brief
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httprequestqueue.h"
#include "_httpoperation.h"
#include "_mutex.h"
using namespace LLCoreInt;
namespace LLCore
{
HttpRequestQueue * HttpRequestQueue::sInstance(NULL);
HttpRequestQueue::HttpRequestQueue()
: RefCounted(true),
mQueueStopped(false)
{
}
HttpRequestQueue::~HttpRequestQueue()
{
mQueue.clear();
}
void HttpRequestQueue::init()
{
llassert_always(! sInstance);
sInstance = new HttpRequestQueue();
}
void HttpRequestQueue::term()
{
if (sInstance)
{
sInstance->release();
sInstance = NULL;
}
}
HttpStatus HttpRequestQueue::addOp(const HttpRequestQueue::opPtr_t &op)
{
bool wake(false);
{
HttpScopedLock lock(mQueueMutex);
if (mQueueStopped)
{
// Return op and error to caller
return HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN);
}
wake = mQueue.empty();
mQueue.push_back(op);
}
if (wake)
{
mQueueCV.notify_all();
}
return HttpStatus();
}
HttpRequestQueue::opPtr_t HttpRequestQueue::fetchOp(bool wait)
{
HttpOperation::ptr_t result;
{
HttpScopedLock lock(mQueueMutex);
while (mQueue.empty())
{
if (! wait || mQueueStopped)
return HttpOperation::ptr_t();
mQueueCV.wait(lock);
}
result = mQueue.front();
mQueue.erase(mQueue.begin());
}
// Caller also acquires the reference count
return result;
}
void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops)
{
// Not valid putting something back on the queue...
llassert_always(ops.empty());
{
HttpScopedLock lock(mQueueMutex);
while (mQueue.empty())
{
if (! wait || mQueueStopped)
return;
mQueueCV.wait(lock);
}
mQueue.swap(ops);
}
// Caller also acquires the reference counts on each op.
return;
}
void HttpRequestQueue::wakeAll()
{
mQueueCV.notify_all();
}
bool HttpRequestQueue::stopQueue()
{
{
HttpScopedLock lock(mQueueMutex);
if (!mQueueStopped)
{
mQueueStopped = true;
wakeAll();
return true;
}
wakeAll();
return false;
}
}
} // end namespace LLCore
+143
View File
@@ -0,0 +1,143 @@
/**
* @file _httprequestqueue.h
* @brief Internal declaration for the operation request queue
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_REQUEST_QUEUE_H_
#define _LLCORE_HTTP_REQUEST_QUEUE_H_
#include <vector>
#include "httpcommon.h"
#include "_refcounted.h"
#include "_mutex.h"
namespace LLCore
{
class HttpOperation;
/// Thread-safe queue of HttpOperation objects. Just
/// a simple queue that handles the transfer of operation
/// requests from all HttpRequest instances into the
/// singleton HttpService instance.
class HttpRequestQueue : public LLCoreInt::RefCounted
{
protected:
/// Caller acquires a Refcount on construction
HttpRequestQueue();
protected:
virtual ~HttpRequestQueue(); // Use release()
private:
HttpRequestQueue(const HttpRequestQueue &); // Not defined
void operator=(const HttpRequestQueue &); // Not defined
public:
typedef std::shared_ptr<HttpOperation> opPtr_t;
static void init();
static void term();
/// Threading: callable by any thread once inited.
inline static HttpRequestQueue * instanceOf()
{
return sInstance;
}
public:
typedef std::vector<opPtr_t> OpContainer;
/// Insert an object at the back of the request queue.
///
/// Caller must provide one refcount to the queue which takes
/// possession of the count on success.
///
/// @return Standard status. On failure, caller
/// must dispose of the operation with
/// an explicit release() call.
///
/// Threading: callable by any thread.
HttpStatus addOp(const opPtr_t &op);
/// Return the operation on the front of the queue. If
/// the queue is empty and @wait is false, call returns
/// immediately and a NULL pointer is returned. If true,
/// caller will sleep until explicitly woken. Wakeups
/// can be spurious and callers must expect NULL pointers
/// even if waiting is indicated.
///
/// Caller acquires reference count any returned operation
///
/// Threading: callable by any thread.
opPtr_t fetchOp(bool wait);
/// Return all queued requests to caller. The @ops argument
/// should be empty when called and will be swap()'d with
/// current contents. Handling of the @wait argument is
/// identical to @fetchOp.
///
/// Caller acquires reference count on each returned operation
///
/// Threading: callable by any thread.
void fetchAll(bool wait, OpContainer & ops);
/// Wake any sleeping threads. Normal queuing operations
/// won't require this but it may be necessary for termination
/// requests.
///
/// Threading: callable by any thread.
void wakeAll();
/// Disallow further request queuing. Callers to @addOp will
/// get a failure status (LLCORE, HE_SHUTTING_DOWN). Callers
/// to @fetchAll or @fetchOp will get requests that are on the
/// queue but the calls will no longer wait. Instead they'll
/// return immediately. Also wakes up all sleepers to send
/// them on their way.
///
/// Threading: callable by any thread.
bool stopQueue();
protected:
static HttpRequestQueue * sInstance;
protected:
OpContainer mQueue;
LLCoreInt::HttpMutex mQueueMutex;
LLCoreInt::HttpConditionVariable mQueueCV;
bool mQueueStopped;
}; // end class HttpRequestQueue
} // end namespace LLCore
#endif // _LLCORE_HTTP_REQUEST_QUEUE_H_
+94
View File
@@ -0,0 +1,94 @@
/**
* @file _httpretryqueue.h
* @brief Internal declaration for the operation retry queue
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_RETRY_QUEUE_H_
#define _LLCORE_HTTP_RETRY_QUEUE_H_
#include <queue>
#include "_httpoprequest.h"
namespace LLCore
{
/// HttpRetryQueue provides a simple priority queue for HttpOpRequest objects.
///
/// This uses the priority_queue adaptor class to provide the queue
/// as well as the ordering scheme while allowing us access to the
/// raw container if we follow a few simple rules. One of the more
/// important of those rules is that any iterator becomes invalid
/// on element erasure. So pay attention.
///
/// Threading: not thread-safe. Expected to be used entirely by
/// a single thread, typically a worker thread of some sort.
struct HttpOpRetryCompare
{
bool operator()(const HttpOpRequest::ptr_t &lhs, const HttpOpRequest::ptr_t &rhs)
{
return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt;
}
};
typedef std::priority_queue<HttpOpRequest::ptr_t,
std::deque<HttpOpRequest::ptr_t>,
LLCore::HttpOpRetryCompare> HttpRetryQueueBase;
class HttpRetryQueue : public HttpRetryQueueBase
{
public:
HttpRetryQueue()
: HttpRetryQueueBase()
{}
~HttpRetryQueue()
{}
protected:
HttpRetryQueue(const HttpRetryQueue &); // Not defined
void operator=(const HttpRetryQueue &); // Not defined
public:
const container_type & get_container() const
{
return c;
}
container_type & get_container()
{
return c;
}
}; // end class HttpRetryQueue
} // end namespace LLCore
#endif // _LLCORE_HTTP_RETRY_QUEUE_H_
+581
View File
@@ -0,0 +1,581 @@
/**
* @file _httpservice.cpp
* @brief Internal definitions of the Http service thread
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httpservice.h"
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include "_httpoperation.h"
#include "_httprequestqueue.h"
#include "_httppolicy.h"
#include "_httplibcurl.h"
#include "_thread.h"
#include "_httpinternal.h"
#include "lltimer.h"
#include "llthread.h"
#include "llexception.h"
#include "llmemory.h"
namespace
{
static const char * const LOG_CORE("CoreHttp");
} // end anonymous namespace
namespace LLCore
{
const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
{ // isLong isDynamic isGlobal isClass
{ true, true, true, true, false }, // PO_CONNECTION_LIMIT
{ true, true, false, true, false }, // PO_PER_HOST_CONNECTION_LIMIT
{ false, false, true, false, false }, // PO_CA_PATH
{ false, false, true, false, false }, // PO_CA_FILE
{ false, true, true, false, false }, // PO_HTTP_PROXY
{ true, true, true, false, false }, // PO_LLPROXY
{ true, true, true, false, false }, // PO_TRACE
{ true, true, false, true, false }, // PO_ENABLE_PIPELINING
{ true, true, false, true, false }, // PO_THROTTLE_RATE
{ false, false, true, false, true } // PO_SSL_VERIFY_CALLBACK
};
HttpService * HttpService::sInstance(NULL);
volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
HttpService::HttpService()
: mRequestQueue(NULL),
mExitRequested(0U),
mThread(NULL),
mPolicy(NULL),
mTransport(NULL),
mLastPolicy(0)
{}
HttpService::~HttpService()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
mExitRequested = 1U;
if (RUNNING == sState)
{
// Trying to kill the service object with a running thread
// is a bit tricky.
if (mRequestQueue)
{
if (mRequestQueue->stopQueue())
{
// Give mRequestQueue a chance to finish
ms_sleep(10);
}
}
if (mThread)
{
if (! mThread->timedJoin(250))
{
// Failed to join, expect problems ahead so do a hard termination.
LL_WARNS(LOG_CORE) << "Destroying HttpService with running thread. Expect problems." << LL_NEWLINE
<< "State: " << S32(sState)
<< " Last policy: " << U32(mLastPolicy)
<< LL_ENDL;
mThread->cancel();
}
}
}
if (mRequestQueue)
{
mRequestQueue->release();
mRequestQueue = NULL;
}
delete mTransport;
mTransport = NULL;
delete mPolicy;
mPolicy = NULL;
if (mThread)
{
mThread->release();
mThread = NULL;
}
}
void HttpService::init(HttpRequestQueue * queue)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(! sInstance);
llassert_always(NOT_INITIALIZED == sState);
sInstance = new HttpService();
queue->addRef();
sInstance->mRequestQueue = queue;
sInstance->mPolicy = new HttpPolicy(sInstance);
sInstance->mTransport = new HttpLibcurl(sInstance);
sState = INITIALIZED;
}
void HttpService::term()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (sInstance)
{
if (RUNNING == sState && sInstance->mThread)
{
// Unclean termination. Thread appears to be running. We'll
// try to give the worker thread a chance to cancel using the
// exit flag...
sInstance->mExitRequested = 1U;
sInstance->mRequestQueue->stopQueue();
// And a little sleep
for (int i(0); i < 10 && RUNNING == sState; ++i)
{
ms_sleep(100);
}
}
delete sInstance;
sInstance = NULL;
}
sState = NOT_INITIALIZED;
}
HttpRequest::policy_t HttpService::createPolicyClass()
{
mLastPolicy = mPolicy->createPolicyClass();
return mLastPolicy;
}
bool HttpService::isStopped()
{
// What is really wanted here is something like:
//
// HttpService * service = instanceOf();
// return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable());
//
// But boost::thread is not giving me a consistent story on joinability
// of a thread after it returns. Debug and non-debug builds are showing
// different behavior on Linux/Etch so we do a weaker test that may
// not be globally correct (i.e. thread *is* stopping, may not have
// stopped but will very soon):
return STOPPED == sState;
}
/// Threading: callable by consumer thread *once*.
void HttpService::startThread()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(! mThread || STOPPED == sState);
llassert_always(INITIALIZED == sState || STOPPED == sState);
if (mThread)
{
mThread->release();
}
// Push current policy definitions, enable policy & transport components
mPolicy->start();
mTransport->start(mLastPolicy + 1);
mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
sState = RUNNING;
}
/// Threading: callable by worker thread.
void HttpService::stopRequested()
{
mExitRequested = 1U;
}
/// Try to find the given request handle on any of the request
/// queues and cancel the operation.
///
/// @return True if the request was canceled.
///
/// Threading: callable by worker thread.
bool HttpService::cancel(HttpHandle handle)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
bool canceled(false);
// Request can't be on request queue so skip that.
// Check the policy component's queues first
canceled = mPolicy->cancel(handle);
if (! canceled)
{
// If that didn't work, check transport's.
canceled = mTransport->cancel(handle);
}
return canceled;
}
/// Threading: callable by worker thread.
void HttpService::shutdown()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
// Disallow future enqueue of requests
mRequestQueue->stopQueue();
// Cancel requests already on the request queue
HttpRequestQueue::OpContainer ops;
mRequestQueue->fetchAll(false, ops);
for (HttpRequestQueue::OpContainer::iterator it = ops.begin();
it != ops.end(); ++it)
{
(*it)->cancel();
}
ops.clear();
// Shutdown transport canceling requests, freeing resources
mTransport->shutdown();
// And now policy
mPolicy->shutdown();
}
// Working thread loop-forever method. Gives time to
// each of the request queue, policy layer and transport
// layer pieces and then either sleeps for a small time
// or waits for a request to come in. Repeats until
// requested to stop.
void HttpService::threadRun(LLCoreInt::HttpThread * thread)
{
LL_PROFILER_SET_THREAD_NAME("HttpService");
boost::this_thread::disable_interruption di;
LLThread::registerThreadID();
ELoopSpeed loop(REQUEST_SLEEP);
while (! mExitRequested)
{
// LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; // <FS:Beq/> remove pointless profiling
try
{
loop = processRequestQueue(loop);
// Process ready queue issuing new requests as needed
ELoopSpeed new_loop = mPolicy->processReadyQueue();
loop = (std::min)(loop, new_loop);
// Give libcurl some cycles
new_loop = mTransport->processTransport();
loop = (std::min)(loop, new_loop);
// Determine whether to spin, sleep briefly or sleep for next request
if (REQUEST_SLEEP != loop)
{
ms_sleep(HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS);
}
}
catch (const LLContinueError&)
{
LOG_UNHANDLED_EXCEPTION("");
}
catch (std::bad_alloc&)
{
LLMemory::logMemoryInfo(true);
//output possible call stacks to log file.
LLError::LLUserWarningMsg::showOutOfMemory();
LLError::LLCallStacks::print();
LL_ERRS() << "Bad memory allocation in HttpService::threadRun()!" << LL_ENDL;
}
catch (...)
{
CRASH_ON_UNHANDLED_EXCEPTION("");
}
}
shutdown();
sState = STOPPED;
}
HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpRequestQueue::OpContainer ops;
const bool wait_for_req(REQUEST_SLEEP == loop);
mRequestQueue->fetchAll(wait_for_req, ops);
while (! ops.empty())
{
HttpOperation::ptr_t op(ops.front());
ops.erase(ops.begin());
// Process operation
if (! mExitRequested)
{
// Setup for subsequent tracing
long tracing(HTTP_TRACE_OFF);
mPolicy->getGlobalOptions().get(HttpRequest::PO_TRACE, &tracing);
op->mTracing = (std::max)(op->mTracing, int(tracing));
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, FromRequestQueue, Handle: "
<< op->getHandle()
<< LL_ENDL;
}
// Stage
op->stageFromRequest(this);
}
// Done with operation
op.reset();
}
// Queue emptied, allow polling loop to sleep
return REQUEST_SLEEP;
}
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (! sOptionDesc[opt].mIsLong) // datatype is long
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)) // class setting permitted
// can always get, no dynamic check
{
return HttpStatus(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
}
HttpStatus status;
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.get(opt, ret_value);
}
else
{
HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
status = opts.get(opt, ret_value);
}
return status;
}
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
std::string * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)) // class setting permitted
// can always get, no dynamic check
{
return status;
}
// Only global has string values
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.get(opt, ret_value);
}
return status;
}
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
HttpRequest::policyCallback_t * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass)) // class setting permitted
// can always get, no dynamic check
{
return status;
}
// Only global has callback values
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.get(opt, ret_value);
}
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long value, long * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (! sOptionDesc[opt].mIsLong) // datatype is long
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass) // class setting permitted
|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
{
return status;
}
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
else
{
HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
status = opts.set(opt, value);
if (status)
{
mTransport->policyUpdated(pclass);
if (ret_value)
{
status = opts.get(opt, ret_value);
}
}
}
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
const std::string & value, std::string * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass) // class setting permitted
|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
{
return status;
}
// String values are always global (at this time).
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
HttpRequest::policyCallback_t value, HttpRequest::policyCallback_t * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass) // class setting permitted
|| (RUNNING == sState && !sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
{
return status;
}
// Callbacks values are always global (at this time).
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
return status;
}
} // end namespace LLCore
+234
View File
@@ -0,0 +1,234 @@
/**
* @file _httpservice.h
* @brief Declarations for internal class providing HTTP service.
*
* $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 _LLCORE_HTTP_SERVICE_H_
#define _LLCORE_HTTP_SERVICE_H_
#include <vector>
#include "linden_common.h"
#include "llatomic.h"
#include "httpcommon.h"
#include "httprequest.h"
#include "_httppolicyglobal.h"
#include "_httppolicyclass.h"
namespace LLCoreInt
{
class HttpThread;
}
namespace LLCore
{
class HttpRequestQueue;
class HttpPolicy;
class HttpLibcurl;
class HttpOpSetGet;
/// The HttpService class does the work behind the request queue. It
/// oversees the HTTP workflow carrying out a number of tasks:
/// - Pulling requests from the global request queue
/// - Executing 'immediate' requests directly
/// - Prioritizing and re-queuing on internal queues the slower requests
/// - Providing cpu cycles to the libcurl plumbing
/// - Overseeing retry operations
///
/// Note that the service object doesn't have a pointer to any
/// reply queue. These are kept by HttpRequest and HttpOperation
/// only.
///
/// Service, Policy and Transport
///
/// HttpService could have been a monolithic class combining a request
/// queue servicer, request policy manager and network transport.
/// Instead, to prevent monolithic growth and allow for easier
/// replacement, it was developed as three separate classes: HttpService,
/// HttpPolicy and HttpLibcurl (transport). These always exist in a
/// 1:1:1 relationship with HttpService managing instances of the other
/// two. So, these classes do not use reference counting to refer
/// to one another, their lifecycles are always managed together.
class HttpService
{
protected:
HttpService();
virtual ~HttpService();
private:
HttpService(const HttpService &); // Not defined
void operator=(const HttpService &); // Not defined
public:
enum EState
{
NOT_INITIALIZED = -1,
INITIALIZED, ///< init() has been called
RUNNING, ///< thread created and running
STOPPED ///< thread has committed to exiting
};
// Ordered enumeration of idling strategies available to
// threadRun's loop. Ordered so that std::min on values
// produces the most conservative result of multiple
// requests.
enum ELoopSpeed
{
NORMAL, ///< continuous polling of request, ready, active queues
REQUEST_SLEEP ///< can sleep indefinitely waiting for request queue write
};
static void init(HttpRequestQueue *);
static void term();
/// Threading: callable by any thread once inited.
inline static HttpService * instanceOf()
{
return sInstance;
}
/// Return the state of the worker thread. Note that the
/// transition from RUNNING to STOPPED is performed by the
/// worker thread itself. This has two weaknesses:
/// - race where the thread hasn't really stopped but will
/// - data ordering between threads where a non-worker thread
/// may see a stale RUNNING status.
///
/// This transition is generally of interest only to unit tests
/// and these weaknesses shouldn't be any real burden.
///
/// Threading: callable by any thread with above exceptions.
static EState getState()
{
return sState;
}
/// Threading: callable by any thread but uses @see getState() and
/// acquires its weaknesses.
static bool isStopped();
/// Threading: callable by init thread *once*.
void startThread();
/// Threading: callable by worker thread.
void stopRequested();
/// Threading: callable by worker thread.
void shutdown();
/// Try to find the given request handle on any of the request
/// queues and cancel the operation.
///
/// @return True if the request was found and canceled.
///
/// Threading: callable by worker thread.
bool cancel(HttpHandle handle);
/// Threading: callable by worker thread.
HttpPolicy & getPolicy()
{
return *mPolicy;
}
/// Threading: callable by worker thread.
HttpLibcurl & getTransport()
{
return *mTransport;
}
/// Threading: callable by worker thread.
HttpRequestQueue & getRequestQueue()
{
return *mRequestQueue;
}
/// Threading: callable by consumer thread.
HttpRequest::policy_t createPolicyClass();
protected:
void threadRun(LLCoreInt::HttpThread * thread);
ELoopSpeed processRequestQueue(ELoopSpeed loop);
protected:
friend class HttpOpSetGet;
friend class HttpRequest;
// Used internally to describe what operations are allowed
// on each policy option.
struct OptionDescriptor
{
bool mIsLong;
bool mIsDynamic;
bool mIsGlobal;
bool mIsClass;
bool mIsCallback;
};
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long * ret_value);
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
std::string * ret_value);
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
HttpRequest::policyCallback_t * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long value, long * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
const std::string & value, std::string * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
HttpRequest::policyCallback_t value,
HttpRequest::policyCallback_t * ret_value);
protected:
static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST];
static HttpService * sInstance;
// === shared data ===
static volatile EState sState;
HttpRequestQueue * mRequestQueue; // Refcounted
LLAtomicU32 mExitRequested;
LLCoreInt::HttpThread * mThread;
// === working-thread-only data ===
HttpPolicy * mPolicy; // Simple pointer, has ownership
HttpLibcurl * mTransport; // Simple pointer, has ownership
// === main-thread-only data ===
HttpRequest::policy_t mLastPolicy;
}; // end class HttpService
} // end namespace LLCore
#endif // _LLCORE_HTTP_SERVICE_H_
+55
View File
@@ -0,0 +1,55 @@
/**
* @file _mutex.hpp
* @brief mutex type abstraction
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLCOREINT_MUTEX_H_
#define LLCOREINT_MUTEX_H_
#include <boost/thread.hpp>
namespace LLCoreInt
{
// MUTEX TYPES
// unique mutex type
typedef boost::mutex HttpMutex;
// CONDITION VARIABLES
// standard condition variable
typedef boost::condition_variable HttpConditionVariable;
// LOCKS AND FENCES
// scoped unique lock
typedef boost::unique_lock<HttpMutex> HttpScopedLock;
}
#endif // LLCOREINT_MUTEX_H
+45
View File
@@ -0,0 +1,45 @@
/**
* @file _refcounted.cpp
* @brief Atomic, thread-safe ref counting and destruction mixin class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_refcounted.h"
namespace LLCoreInt
{
#if ! LL_WINDOWS
const S32 RefCounted::NOT_REF_COUNTED;
#endif // ! LL_WINDOWS
RefCounted::~RefCounted()
{}
} // end namespace LLCoreInt
+156
View File
@@ -0,0 +1,156 @@
/**
* @file _refcounted.h
* @brief Atomic, thread-safe ref counting and destruction mixin class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLCOREINT__REFCOUNTED_H_
#define LLCOREINT__REFCOUNTED_H_
#include "linden_common.h"
#include "fix_macros.h"
#include <boost/thread.hpp>
#include <boost/intrusive_ptr.hpp>
#include "llatomic.h"
namespace LLCoreInt
{
class RefCounted
{
private:
RefCounted(); // Not defined - may not be default constructed
void operator=(const RefCounted &); // Not defined
public:
explicit RefCounted(bool const implicit)
: mRefCount(implicit)
{}
// ref-count interface
void addRef() const;
void release() const;
bool isLastRef() const;
S32 getRefCount() const;
void noRef() const;
static const S32 NOT_REF_COUNTED = -1;
protected:
virtual ~RefCounted();
virtual void destroySelf();
private:
mutable LLAtomicS32 mRefCount;
}; // end class RefCounted
inline void RefCounted::addRef() const
{
S32 count(++mRefCount);
llassert_always(count >= 0);
}
inline void RefCounted::release() const
{
S32 count(mRefCount);
llassert_always(count != NOT_REF_COUNTED);
llassert_always(count > 0);
count = --mRefCount;
// clean ourselves up if that was the last reference
if (0 == count)
{
const_cast<RefCounted *>(this)->destroySelf();
}
}
inline bool RefCounted::isLastRef() const
{
const S32 count(mRefCount);
llassert_always(count != NOT_REF_COUNTED);
llassert_always(count >= 1);
return (1 == count);
}
inline S32 RefCounted::getRefCount() const
{
const S32 result(mRefCount);
return result;
}
inline void RefCounted::noRef() const
{
llassert_always(mRefCount <= 1);
mRefCount = NOT_REF_COUNTED;
}
inline void RefCounted::destroySelf()
{
delete this;
}
/**
* boost::intrusive_ptr may be used to manage RefCounted classes.
* Unfortunately RefCounted and boost::intrusive_ptr use different conventions
* for the initial refcount value. To avoid leaky (immortal) objects, you
* should really construct boost::intrusive_ptr<RefCounted*>(rawptr, false).
* IntrusivePtr<T> encapsulates that for you.
*/
template <typename T>
struct IntrusivePtr: public boost::intrusive_ptr<T>
{
IntrusivePtr():
boost::intrusive_ptr<T>()
{}
IntrusivePtr(T* p):
boost::intrusive_ptr<T>(p, false)
{}
};
inline void intrusive_ptr_add_ref(RefCounted* p)
{
p->addRef();
}
inline void intrusive_ptr_release(RefCounted* p)
{
p->release();
}
} // end namespace LLCoreInt
#endif // LLCOREINT__REFCOUNTED_H_
+132
View File
@@ -0,0 +1,132 @@
/**
* @file _thread.h
* @brief thread type abstraction
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLCOREINT_THREAD_H_
#define LLCOREINT_THREAD_H_
#include "linden_common.h"
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "apr.h" // thread-related functions
#include "_refcounted.h"
#include "llprofiler.h"
namespace LLCoreInt
{
class HttpThread : public RefCounted
{
private:
HttpThread(); // Not defined
void operator=(const HttpThread &); // Not defined
void at_exit()
{
// the thread function has exited so we need to release our reference
// to ourself so that we will be automagically cleaned up.
release();
}
void run()
{ // THREAD CONTEXT
// <FS:Beq> - Add threadnames
LL_INFOS("THREAD") << "Started unnamed HTTP thread " << LL_ENDL;
LL_PROFILER_THREAD_BEGIN("HTTP");
// </FS:Beq>
// Take out additional reference for the at_exit handler
addRef();
boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this));
// run the thread function
mThreadFunc(this);
// <FS:Beq> - Add threadnames
LL_PROFILER_THREAD_END("HTTP");
// </FS:Beq>
} // THREAD CONTEXT
protected:
virtual ~HttpThread()
{
delete mThread;
}
public:
/// Constructs a thread object for concurrent execution but does
/// not start running. Caller receives on refcount on the thread
/// instance. If the thread is started, another will be taken
/// out for the exit handler.
explicit HttpThread(boost::function<void (HttpThread *)> threadFunc)
: RefCounted(true), // implicit reference
mThreadFunc(threadFunc)
{
// this creates a boost thread that will call HttpThread::run on this instance
// and pass it the threadfunc callable...
boost::function<void()> f = boost::bind(&HttpThread::run, this);
mThread = new boost::thread(f);
}
inline void join()
{
mThread->join();
}
inline bool timedJoin(S32 millis)
{
return mThread->timed_join(boost::posix_time::milliseconds(millis));
}
inline bool joinable() const
{
return mThread->joinable();
}
// A very hostile method to force a thread to quit
inline void cancel()
{
boost::thread::native_handle_type thread(mThread->native_handle());
#if LL_WINDOWS
TerminateThread(thread, 0);
#else
pthread_cancel(thread);
#endif
}
private:
boost::function<void(HttpThread *)> mThreadFunc;
boost::thread * mThread;
}; // end class HttpThread
} // end namespace LLCoreInt
#endif // LLCOREINT_THREAD_H_
+368
View File
@@ -0,0 +1,368 @@
/**
* @file bufferarray.cpp
* @brief Implements the BufferArray scatter/gather buffer
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "bufferarray.h"
#include "llexception.h"
#include "llmemory.h"
// BufferArray is a list of chunks, each a BufferArray::Block, of contiguous
// data presented as a single array. Chunks are at least BufferArray::BLOCK_ALLOC_SIZE
// in length and can be larger. Any chunk may be partially filled or even
// empty.
//
// The BufferArray itself is sharable as a RefCounted entity. As shared
// reads don't work with the concept of a current position/seek value,
// none is kept with the object. Instead, the read and write operations
// all take position arguments. Single write/shared read isn't supported
// directly and any such attempts have to be serialized outside of this
// implementation.
namespace LLCore
{
// ==================================
// BufferArray::Block Declaration
// ==================================
class BufferArray::Block
{
public:
~Block();
void operator delete(void *);
void operator delete(void *, size_t len);
protected:
Block(size_t len);
Block(const Block &); // Not defined
void operator=(const Block &); // Not defined
// Allocate the block with the additional space for the
// buffered data at the end of the object.
void * operator new(size_t len, size_t addl_len);
public:
// Only public entry to get a block.
static Block * alloc(size_t len);
public:
size_t mUsed;
size_t mAlloced;
// *NOTE: Must be last member of the object. We'll
// overallocate as requested via operator new and index
// into the array at will.
char mData[1];
};
// ==================================
// BufferArray Definitions
// ==================================
#if ! LL_WINDOWS
const size_t BufferArray::BLOCK_ALLOC_SIZE;
#endif // ! LL_WINDOWS
BufferArray::BufferArray()
: LLCoreInt::RefCounted(true),
mLen(0)
{}
BufferArray::~BufferArray()
{
for (container_t::iterator it(mBlocks.begin());
it != mBlocks.end();
++it)
{
delete *it;
*it = NULL;
}
mBlocks.clear();
}
size_t BufferArray::append(const void * src, size_t len)
{
const size_t ret(len);
const char * c_src(static_cast<const char *>(src));
// First, try to copy into the last block
if (len && ! mBlocks.empty())
{
Block & last(*mBlocks.back());
if (last.mUsed < last.mAlloced)
{
// Some will fit...
const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed)));
memcpy(&last.mData[last.mUsed], c_src, copy_len);
last.mUsed += copy_len;
llassert_always(last.mUsed <= last.mAlloced);
mLen += copy_len;
c_src += copy_len;
len -= copy_len;
}
}
// Then get new blocks as needed
while (len)
{
const size_t copy_len((std::min)(len, BLOCK_ALLOC_SIZE));
if (mBlocks.size() >= mBlocks.capacity())
{
mBlocks.reserve(mBlocks.size() + 5);
}
Block * block;
try
{
block = Block::alloc(BLOCK_ALLOC_SIZE);
}
catch (std::bad_alloc&)
{
LLMemory::logMemoryInfo(true);
//output possible call stacks to log file.
LLError::LLCallStacks::print();
LL_WARNS() << "Bad memory allocation in thrown by Block::alloc in read!" << LL_ENDL;
break;
}
memcpy(block->mData, c_src, copy_len);
block->mUsed = copy_len;
llassert_always(block->mUsed <= block->mAlloced);
mBlocks.push_back(block);
mLen += copy_len;
c_src += copy_len;
len -= copy_len;
}
return ret - len;
}
void * BufferArray::appendBufferAlloc(size_t len)
{
// If someone asks for zero-length, we give them a valid pointer.
if (mBlocks.size() >= mBlocks.capacity())
{
mBlocks.reserve(mBlocks.size() + 5);
}
Block * block = Block::alloc((std::max)(BLOCK_ALLOC_SIZE, len));
block->mUsed = len;
mBlocks.push_back(block);
mLen += len;
return block->mData;
}
size_t BufferArray::read(size_t pos, void * dst, size_t len)
{
char * c_dst(static_cast<char *>(dst));
if (pos >= mLen)
return 0;
size_t len_limit(mLen - pos);
len = (std::min)(len, len_limit);
if (0 == len)
return 0;
size_t result(0), offset(0);
const auto block_limit(mBlocks.size());
int block_start(findBlock(pos, &offset));
if (block_start < 0)
return 0;
do
{
Block & block(*mBlocks[block_start]);
size_t block_limit(block.mUsed - offset);
size_t block_len((std::min)(block_limit, len));
memcpy(c_dst, &block.mData[offset], block_len);
result += block_len;
len -= block_len;
c_dst += block_len;
offset = 0;
++block_start;
}
while (len && block_start < block_limit);
return result;
}
size_t BufferArray::write(size_t pos, const void * src, size_t len)
{
const char * c_src(static_cast<const char *>(src));
if (pos > mLen || 0 == len)
return 0;
size_t result(0), offset(0);
const auto block_limit(mBlocks.size());
int block_start(findBlock(pos, &offset));
if (block_start >= 0)
{
// Some or all of the write will be on top of
// existing data.
do
{
Block & block(*mBlocks[block_start]);
size_t block_limit(block.mUsed - offset);
size_t block_len((std::min)(block_limit, len));
memcpy(&block.mData[offset], c_src, block_len);
result += block_len;
c_src += block_len;
len -= block_len;
offset = 0;
++block_start;
}
while (len && block_start < block_limit);
}
// Something left, see if it will fit in the free
// space of the last block.
if (len && ! mBlocks.empty())
{
Block & last(*mBlocks.back());
if (last.mUsed < last.mAlloced)
{
// Some will fit...
const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed)));
memcpy(&last.mData[last.mUsed], c_src, copy_len);
last.mUsed += copy_len;
result += copy_len;
llassert_always(last.mUsed <= last.mAlloced);
mLen += copy_len;
c_src += copy_len;
len -= copy_len;
}
}
if (len)
{
// Some or all of the remaining write data will
// be an append.
result += append(c_src, len);
}
return result;
}
int BufferArray::findBlock(size_t pos, size_t * ret_offset)
{
*ret_offset = 0;
if (pos >= mLen)
return -1; // Doesn't exist
const int block_limit(narrow<size_t>(mBlocks.size()));
for (int i(0); i < block_limit; ++i)
{
if (pos < mBlocks[i]->mUsed)
{
*ret_offset = pos;
return i;
}
pos -= mBlocks[i]->mUsed;
}
// Shouldn't get here but...
return -1;
}
bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end)
{
if (block < 0 || block >= mBlocks.size())
{
return false;
}
const Block & b(*mBlocks[block]);
*start = &b.mData[0];
*end = &b.mData[b.mUsed];
return true;
}
// ==================================
// BufferArray::Block Definitions
// ==================================
BufferArray::Block::Block(size_t len)
: mUsed(0),
mAlloced(len)
{
memset(mData, 0, len);
}
BufferArray::Block::~Block()
{
mUsed = 0;
mAlloced = 0;
}
void * BufferArray::Block::operator new(size_t len, size_t addl_len)
{
void * mem = new char[len + addl_len + sizeof(void *)];
return mem;
}
void BufferArray::Block::operator delete(void * mem)
{
char * cmem = static_cast<char *>(mem);
delete [] cmem;
}
void BufferArray::Block::operator delete(void * mem, size_t)
{
operator delete(mem);
}
BufferArray::Block * BufferArray::Block::alloc(size_t len)
{
Block * block = new (len) Block(len);
return block;
}
} // end namespace LLCore
+140
View File
@@ -0,0 +1,140 @@
/**
* @file bufferarray.h
* @brief Public-facing declaration for the BufferArray scatter/gather class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_BUFFER_ARRAY_H_
#define _LLCORE_BUFFER_ARRAY_H_
#include <cstdlib>
#include <vector>
#include "_refcounted.h"
namespace LLCore
{
class BufferArrayStreamBuf;
/// A very simple scatter/gather type map for bulk data. The motivation
/// for this class is the writedata callback used by libcurl. Response
/// bodies are delivered to the caller in a sequence of sequential write
/// operations and this class captures them without having to reallocate
/// and move data.
///
/// The interface looks a little like a unix file descriptor but only
/// just. There is a notion of a current position, starting from 0,
/// which is used as the position in the data when performing read and
/// write operations. The position also moves after various operations:
/// - seek(...)
/// - read(...)
/// - write(...)
/// - append(...)
/// - appendBufferAlloc(...)
/// The object also keeps a total length value which is updated after
/// write and append operations and beyond which the current position
/// cannot be set.
///
/// Threading: not thread-safe
///
/// Allocation: Refcounted, heap only. Caller of the constructor
/// is given a single refcount.
///
class BufferArray : public LLCoreInt::RefCounted
{
public:
// BufferArrayStreamBuf has intimate knowledge of this
// implementation to implement a buffer-free adapter.
// Changes here will likely need to be reflected there.
friend class BufferArrayStreamBuf;
BufferArray();
typedef LLCoreInt::IntrusivePtr<BufferArray> ptr_t;
protected:
virtual ~BufferArray(); // Use release()
private:
BufferArray(const BufferArray &); // Not defined
void operator=(const BufferArray &); // Not defined
public:
// Internal magic number, may be used by unit tests.
static const size_t BLOCK_ALLOC_SIZE = 65540;
/// Appends the indicated data to the BufferArray
/// modifying current position and total size. New
/// position is one beyond the final byte of the buffer.
///
/// @return Count of bytes copied to BufferArray
size_t append(const void * src, size_t len);
/// Similar to @see append(), this call guarantees a
/// contiguous block of memory of requested size placed
/// at the current end of the BufferArray. On return,
/// the data in the memory is considered valid whether
/// the caller writes to it or not.
///
/// @return Pointer to contiguous region at end
/// of BufferArray of 'len' size.
void * appendBufferAlloc(size_t len);
/// Current count of bytes in BufferArray instance.
size_t size() const
{
return mLen;
}
/// Copies data from the given position in the instance
/// to the caller's buffer. Will return a short count of
/// bytes copied if the 'len' extends beyond the data.
size_t read(size_t pos, void * dst, size_t len);
/// Copies data from the caller's buffer to the instance
/// at the current position. May overwrite existing data,
/// append data when current position is equal to the
/// size of the instance or do a mix of both.
size_t write(size_t pos, const void * src, size_t len);
protected:
int findBlock(size_t pos, size_t * ret_offset);
bool getBlockStartEnd(int block, const char ** start, const char ** end);
protected:
class Block;
typedef std::vector<Block *> container_t;
container_t mBlocks;
size_t mLen;
}; // end class BufferArray
} // end namespace LLCore
#endif // _LLCORE_BUFFER_ARRAY_H_
+283
View File
@@ -0,0 +1,283 @@
/**
* @file bufferstream.cpp
* @brief Implements the BufferStream adapter class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "bufferstream.h"
#include "bufferarray.h"
namespace LLCore
{
BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array)
: mBufferArray(array),
mReadCurPos(0),
mReadCurBlock(-1),
mReadBegin(NULL),
mReadCur(NULL),
mReadEnd(NULL),
mWriteCurPos(0)
{
if (array)
{
array->addRef();
mWriteCurPos = array->mLen;
}
}
BufferArrayStreamBuf::~BufferArrayStreamBuf()
{
if (mBufferArray)
{
mBufferArray->release();
mBufferArray = NULL;
}
}
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow()
{
if (! mBufferArray)
{
return traits_type::eof();
}
if (mReadCur == mReadEnd)
{
// Find the next block with actual data or leave
// mCurBlock/mCur/mEnd unchanged if we're at the end
// of any block chain.
const char * new_begin(NULL), * new_end(NULL);
int new_cur_block(mReadCurBlock + 1);
while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end))
{
if (new_begin != new_end)
{
break;
}
++new_cur_block;
}
if (new_begin == new_end)
{
return traits_type::eof();
}
mReadCurBlock = new_cur_block;
mReadBegin = mReadCur = new_begin;
mReadEnd = new_end;
}
return traits_type::to_int_type(*mReadCur);
}
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow()
{
const int_type ret(underflow());
if (traits_type::eof() != ret)
{
++mReadCur;
++mReadCurPos;
}
return ret;
}
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch)
{
if (! mBufferArray)
{
return traits_type::eof();
}
if (mReadCur == mReadBegin)
{
// Find the previous block with actual data or leave
// mCurBlock/mBegin/mCur/mEnd unchanged if we're at the
// beginning of any block chain.
const char * new_begin(NULL), * new_end(NULL);
int new_cur_block(mReadCurBlock - 1);
while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end))
{
if (new_begin != new_end)
{
break;
}
--new_cur_block;
}
if (new_begin == new_end)
{
return traits_type::eof();
}
mReadCurBlock = new_cur_block;
mReadBegin = new_begin;
mReadEnd = mReadCur = new_end;
}
if (traits_type::eof() != ch && mReadCur[-1] != ch)
{
return traits_type::eof();
}
--mReadCurPos;
return traits_type::to_int_type(*--mReadCur);
}
std::streamsize BufferArrayStreamBuf::showmanyc()
{
if (! mBufferArray)
{
return -1;
}
return mBufferArray->mLen - mReadCurPos;
}
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c)
{
if (! mBufferArray || mWriteCurPos > mBufferArray->mLen)
{
return traits_type::eof();
}
const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1));
mWriteCurPos += wrote;
return wrote ? c : traits_type::eof();
}
std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count)
{
if (! mBufferArray || mWriteCurPos > mBufferArray->mLen)
{
return 0;
}
const size_t wrote(mBufferArray->write(mWriteCurPos, src, count));
mWriteCurPos += wrote;
return wrote;
}
std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off,
std::ios_base::seekdir way,
std::ios_base::openmode which)
{
std::streampos ret(-1);
if (! mBufferArray)
{
return ret;
}
if (std::ios_base::in == which)
{
size_t pos(0);
switch (way)
{
case std::ios_base::beg:
pos = off;
break;
case std::ios_base::cur:
pos = mReadCurPos += off;
break;
case std::ios_base::end:
pos = mBufferArray->mLen - off;
break;
default:
return ret;
}
if (pos >= mBufferArray->size())
{
pos = (std::max)(size_t(0), mBufferArray->size() - 1);
}
size_t ba_offset(0);
int block(mBufferArray->findBlock(pos, &ba_offset));
if (block < 0)
return ret;
const char * start(NULL), * end(NULL);
if (! mBufferArray->getBlockStartEnd(block, &start, &end))
return ret;
mReadCurBlock = block;
mReadBegin = start;
mReadCur = start + ba_offset;
mReadEnd = end;
ret = mReadCurPos = pos;
}
else if (std::ios_base::out == which)
{
size_t pos(0);
switch (way)
{
case std::ios_base::beg:
pos = off;
break;
case std::ios_base::cur:
pos = mWriteCurPos += off;
break;
case std::ios_base::end:
pos = mBufferArray->mLen - off;
break;
default:
return ret;
}
if (pos > mBufferArray->size())
{
pos = mBufferArray->size();
}
ret = mWriteCurPos = pos;
}
return ret;
}
BufferArrayStream::BufferArrayStream(BufferArray * ba)
: std::iostream(&mStreamBuf),
mStreamBuf(ba)
{}
BufferArrayStream::~BufferArrayStream()
{}
} // end namespace LLCore
+153
View File
@@ -0,0 +1,153 @@
/**
* @file bufferstream.h
* @brief Public-facing declaration for the BufferStream adapter class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_BUFFER_STREAM_H_
#define _LLCORE_BUFFER_STREAM_H_
#include <sstream>
#include <cstdlib>
#include "bufferarray.h"
/// @file bufferstream.h
///
/// std::streambuf and std::iostream adapters for BufferArray
/// objects.
///
/// BufferArrayStreamBuf inherits std::streambuf and implements
/// an unbuffered interface for streambuf. This may or may not
/// be the most time efficient implementation and it is a little
/// challenging.
///
/// BufferArrayStream inherits std::iostream and will be the
/// adapter object most callers will be interested in (though
/// it uses BufferArrayStreamBuf internally). Instances allow
/// for the usual streaming operators ('<<', '>>') and serialization
/// methods.
///
/// Example of LLSD serialization to a BufferArray:
///
/// BufferArray * ba = new BufferArray;
/// BufferArrayStream bas(ba);
/// LLSDSerialize::toXML(llsd, bas);
/// operationOnBufferArray(ba);
/// ba->release();
/// ba = NULL;
/// // operationOnBufferArray and bas are each holding
/// // references to the ba instance at this point.
///
namespace LLCore
{
// =====================================================
// BufferArrayStreamBuf
// =====================================================
/// Adapter class to put a std::streambuf interface on a BufferArray
///
/// Application developers will rarely be interested in anything
/// other than the constructor and even that will rarely be used
/// except indirectly via the @BufferArrayStream class. The
/// choice of interfaces implemented yields a bufferless adapter
/// that doesn't used either the input or output pointer triplets
/// of the more common buffered implementations. This may or may
/// not be faster and that question could stand to be looked at
/// sometime.
///
class BufferArrayStreamBuf : public std::streambuf
{
public:
/// Constructor increments the reference count on the
/// BufferArray argument and calls release() on destruction.
BufferArrayStreamBuf(BufferArray * array);
virtual ~BufferArrayStreamBuf();
private:
BufferArrayStreamBuf(const BufferArrayStreamBuf &); // Not defined
void operator=(const BufferArrayStreamBuf &); // Not defined
public:
// Input interfaces from std::streambuf
int_type underflow();
int_type uflow();
int_type pbackfail(int_type ch);
std::streamsize showmanyc();
// Output interfaces from std::streambuf
int_type overflow(int c);
std::streamsize xsputn(const char * src, std::streamsize count);
// Common/misc interfaces from std::streambuf
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which);
protected:
BufferArray * mBufferArray; // Ref counted
size_t mReadCurPos;
int mReadCurBlock;
const char * mReadBegin;
const char * mReadCur;
const char * mReadEnd;
size_t mWriteCurPos;
}; // end class BufferArrayStreamBuf
// =====================================================
// BufferArrayStream
// =====================================================
/// Adapter class that supplies streaming operators to BufferArray
///
/// Provides a streaming adapter to an existing BufferArray
/// instance so that the convenient '<<' and '>>' conversions
/// can be applied to a BufferArray. Very convenient for LLSD
/// serialization and parsing as well.
class BufferArrayStream : public std::iostream
{
public:
/// Constructor increments the reference count on the
/// BufferArray argument and calls release() on destruction.
BufferArrayStream(BufferArray * ba);
~BufferArrayStream();
protected:
BufferArrayStream(const BufferArrayStream &);
void operator=(const BufferArrayStream &);
protected:
BufferArrayStreamBuf mStreamBuf;
}; // end class BufferArrayStream
} // end namespace LLCore
#endif // _LLCORE_BUFFER_STREAM_H_
File diff suppressed because it is too large Load Diff
+391
View File
@@ -0,0 +1,391 @@
/**
* @file httpcommon.cpp
* @brief
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "httpcommon.h"
#include "llmutex.h"
#include "llthread.h"
#include <curl/curl.h>
#include <string>
#include <sstream>
namespace LLCore
{
HttpStatus::type_enum_t EXT_CURL_EASY;
HttpStatus::type_enum_t EXT_CURL_MULTI;
HttpStatus::type_enum_t LLCORE;
HttpStatus::operator U32() const
{
// Effectively, concatenate mType (high) with mStatus (low).
static const int shift(sizeof(mDetails->mStatus) * 8);
U32 result(U32(mDetails->mType) << shift | U32((int)mDetails->mStatus));
return result;
}
std::string HttpStatus::toHex() const
{
std::ostringstream result;
result.width(8);
result.fill('0');
result << std::hex << operator U32();
return result.str();
}
std::string HttpStatus::toString() const
{
static const char * llcore_errors[] =
{
"",
"HTTP error reply status",
"Services shutting down",
"Operation canceled",
"Invalid Content-Range header encountered",
"Request handle not found",
"Invalid datatype for argument or option",
"Option has not been explicitly set",
"Option is not dynamic and must be set early",
"Invalid HTTP status code received from server",
"Could not allocate required resource"
};
static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0]));
static const struct
{
type_enum_t mCode;
const char * mText;
}
http_errors[] =
{
// Keep sorted by mCode, we binary search this list.
{ 100, "Continue" },
{ 101, "Switching Protocols" },
{ 200, "OK" },
{ 201, "Created" },
{ 202, "Accepted" },
{ 203, "Non-Authoritative Information" },
{ 204, "No Content" },
{ 205, "Reset Content" },
{ 206, "Partial Content" },
{ 300, "Multiple Choices" },
{ 301, "Moved Permanently" },
{ 302, "Found" },
{ 303, "See Other" },
{ 304, "Not Modified" },
{ 305, "Use Proxy" },
{ 307, "Temporary Redirect" },
{ 400, "Bad Request" },
{ 401, "Unauthorized" },
{ 402, "Payment Required" },
{ 403, "Forbidden" },
{ 404, "Not Found" },
{ 405, "Method Not Allowed" },
{ 406, "Not Acceptable" },
{ 407, "Proxy Authentication Required" },
{ 408, "Request Time-out" },
{ 409, "Conflict" },
{ 410, "Gone" },
{ 411, "Length Required" },
{ 412, "Precondition Failed" },
{ 413, "Request Entity Too Large" },
{ 414, "Request-URI Too Large" },
{ 415, "Unsupported Media Type" },
{ 416, "Requested range not satisfiable" },
{ 417, "Expectation Failed" },
{ 499, "Linden Catch-All" },
{ 500, "Internal Server Error" },
{ 501, "Not Implemented" },
{ 502, "Bad Gateway" },
{ 503, "Service Unavailable" },
{ 504, "Gateway Time-out" },
{ 505, "HTTP Version not supported" }
};
static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0]));
if (*this)
{
return std::string("");
}
switch (getType())
{
case EXT_CURL_EASY:
return std::string(curl_easy_strerror(CURLcode(getStatus())));
case EXT_CURL_MULTI:
return std::string(curl_multi_strerror(CURLMcode(getStatus())));
case LLCORE:
if (getStatus() >= 0 && getStatus() < llcore_errors_count)
{
return std::string(llcore_errors[getStatus()]);
}
break;
default:
if (isHttpStatus())
{
// special handling for status 499 "Linden Catchall"
if ((getType() == 499) && (!getMessage().empty()))
return getMessage();
// Binary search for the error code and string
int bottom(0), top(http_errors_count);
while (true)
{
int at((bottom + top) / 2);
if (getType() == http_errors[at].mCode)
{
return std::string(http_errors[at].mText);
}
if (at == bottom)
{
break;
}
else if (getType() < http_errors[at].mCode)
{
top = at;
}
else
{
bottom = at;
}
}
}
break;
}
return std::string("Unknown error");
}
std::string HttpStatus::toTerseString() const
{
std::ostringstream result;
unsigned int error_value((unsigned short)getStatus());
switch (getType())
{
case EXT_CURL_EASY:
result << "Easy_";
break;
case EXT_CURL_MULTI:
result << "Multi_";
break;
case LLCORE:
result << "Core_";
break;
default:
if (isHttpStatus())
{
result << "Http_";
error_value = getType();
}
else
{
result << "Unknown_";
}
break;
}
result << error_value;
return result.str();
}
// Pass true on statuses that might actually be cleared by a
// retry. Library failures, calling problems, etc. aren't
// going to be fixed by squirting bits all over the Net.
//
// HE_INVALID_HTTP_STATUS is special. As of 7.37.0, there are
// some scenarios where response processing in libcurl appear
// to go wrong and response data is corrupted. A side-effect
// of this is that the HTTP status is read as 0 from the library.
// See libcurl bug report 1420 (https://sourceforge.net/p/curl/bugs/1420/)
// for details.
bool HttpStatus::isRetryable() const
{
static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY);
static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST);
static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR);
static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR);
static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED);
static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR);
static const HttpStatus partial_file(HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE);
static const HttpStatus inv_cont_range(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
static const HttpStatus inv_status(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
// *DEBUG: For "[curl:bugs] #1420" tests.
// Disable the '*this == inv_status' test and look for 'Core_9'
// failures in log files.
return ((isHttpStatus() && getType() >= 499 && getType() <= 599) || // Include special 499 in retryables
*this == cant_connect || // Connection reset/endpoint problems
*this == cant_res_proxy || // DNS problems
*this == cant_res_host || // DNS problems
*this == send_error || // General socket problems
*this == recv_error || // General socket problems
*this == upload_failed || // Transport problem
*this == op_timedout || // Timer expired
*this == post_error || // Transport problem
*this == partial_file || // Data inconsistency in response
// *DEBUG: Comment out 'inv_status' test for [curl:bugs] #1420 testing.
*this == inv_status || // Inv status can reflect internal state problem in libcurl
*this == inv_cont_range); // Short data read disagrees with content-range
}
namespace LLHttp
{
namespace
{
CURL *getCurlTemplateHandle()
{
static CURL *curlpTemplateHandle = NULL;
if (curlpTemplateHandle == NULL)
{ // Late creation of the template curl handle
curlpTemplateHandle = curl_easy_init();
if (curlpTemplateHandle == NULL)
{
LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL;
}
else
{
CURLcode result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
check_curl_code(result, CURLOPT_IPRESOLVE);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOSIGNAL, 1);
check_curl_code(result, CURLOPT_NOSIGNAL);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOPROGRESS, 1);
check_curl_code(result, CURLOPT_NOPROGRESS);
// <FS:ND/> Newer versions of curl are stricter with checkinng Cotent-Encoding: header
// Aws returns Content-Encoding: binary/octet-stream which is no valid scheme defined by HTTP/1.1 (compress,deflate, gzip)
#if LIBCURL_VERSION_NUM < 0x075100
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_ENCODING, "");
check_curl_code(result, CURLOPT_ENCODING);
#endif
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_AUTOREFERER, 1);
check_curl_code(result, CURLOPT_AUTOREFERER);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_FOLLOWLOCATION, 1);
check_curl_code(result, CURLOPT_FOLLOWLOCATION);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYPEER, 1);
check_curl_code(result, CURLOPT_SSL_VERIFYPEER);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYHOST, 0);
check_curl_code(result, CURLOPT_SSL_VERIFYHOST);
// The Linksys WRT54G V5 router has an issue with frequent
// DNS lookups from LAN machines. If they happen too often,
// like for every HTTP request, the router gets annoyed after
// about 700 or so requests and starts issuing TCP RSTs to
// new connections. Reuse the DNS lookups for even a few
// seconds and no RSTs.
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
check_curl_code(result, CURLOPT_DNS_CACHE_TIMEOUT);
}
}
return curlpTemplateHandle;
}
LLMutex *getCurlMutex()
{
static LLMutex* sHandleMutexp = NULL;
if (!sHandleMutexp)
{
sHandleMutexp = new LLMutex();
}
return sHandleMutexp;
}
void deallocateEasyCurl(CURL *curlp)
{
LLMutexLock lock(getCurlMutex());
curl_easy_cleanup(curlp);
}
}
void initialize()
{
// Do not change this "unless you are familiar with and mean to control
// internal operations of libcurl"
// - http://curl.haxx.se/libcurl/c/curl_global_init.html
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
check_curl_code(code, CURL_GLOBAL_ALL);
}
void cleanup()
{
curl_global_cleanup();
}
CURL_ptr createEasyHandle()
{
LLMutexLock lock(getCurlMutex());
CURL* handle = curl_easy_duphandle(getCurlTemplateHandle());
return CURL_ptr(handle, &deallocateEasyCurl);
}
std::string getCURLVersion()
{
return std::string(curl_version());
}
void check_curl_code(CURLcode code, int curl_setopt_option)
{
if (CURLE_OK != code)
{
// Comment from old llcurl code which may no longer apply:
//
// linux appears to throw a curl error once per session for a bad initialization
// at a pretty random time (when enabling cookies).
LL_WARNS() << "libcurl error detected: " << curl_easy_strerror(code)
<< ", curl_easy_setopt option: " << curl_setopt_option
<< LL_ENDL;
}
}
}
} // end namespace LLCore
+509
View File
@@ -0,0 +1,509 @@
/**
* @file httpcommon.h
* @brief Public-facing declarations and definitions of common types
*
* $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 _LLCORE_HTTP_COMMON_H_
#define _LLCORE_HTTP_COMMON_H_
/// @package LLCore::HTTP
///
/// This library implements a high-level, Indra-code-free (somewhat) client
/// interface to HTTP services based on actual patterns found in the viewer
/// and simulator. Interfaces are similar to those supplied by the legacy classes
/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that
/// allows an application to specify connection behaviors: limits on
/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc.
///
/// Features of the library include:
/// - Single, private working thread where all transport and processing occurs.
/// - Support for multiple consumers running in multiple threads.
/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement.
/// - Reference counting used for many object instance lifetimes.
/// - Minimal data sharing across threads for correctness and low latency.
///
/// The public interface is declared in a few key header files:
/// - "llcorehttp/bufferarray.h"
/// - "llcorehttp/httpcommon.h"
/// - "llcorehttp/httphandler.h"
/// - "llcorehttp/httpheaders.h"
/// - "llcorehttp/httpoptions.h"
/// - "llcorehttp/httprequest.h"
/// - "llcorehttp/httpresponse.h"
///
/// The library is still under development and particular users
/// may need access to internal implementation details that are found
/// in the _*.h header files. But this is a crutch to be avoided if at
/// all possible and probably indicates some interface work is neeeded.
///
/// Using the library is fairly easy. Global setup needs a few
/// steps:
///
/// - libcurl initialization including thread-safely callbacks for SSL:
/// . curl_global_init(...)
/// . CRYPTO_set_locking_callback(...)
/// . CRYPTO_set_id_callback(...)
/// - HttpRequest::createService() called to instantiate singletons
/// and support objects.
/// - HttpRequest::startThread() to kick off the worker thread and
/// begin servicing requests.
///
/// An HTTP consumer in an application, and an application may have many
/// consumers, does a few things:
///
/// - Instantiate and retain an object based on HttpRequest. This
/// object becomes the portal into runtime services for the consumer.
/// - Derive or mixin the HttpHandler class if you want notification
/// when requests succeed or fail. This object's onCompleted()
/// method is invoked and an instance can be shared across
/// requests.
///
/// Issuing a request is straightforward:
/// - Construct a suitable URL.
/// - Configure HTTP options for the request. (optional)
/// - Build a list of additional headers. (optional)
/// - Invoke one of the requestXXXX() methods (requestGetByteRange,
/// requestPost, etc.) on the HttpRequest instance supplying the
/// above along with a policy class, a priority and an optional
/// pointer to an HttpHandler instance. Work is then queued to
/// the worker thread and occurs asynchronously.
/// - Periodically invoke the update() method on the HttpRequest
/// instance which performs completion notification to HttpHandler
/// objects.
/// - Do completion processing in your onCompletion() method.
///
/// Code fragments.
///
/// Initialization. Rather than a poorly-maintained example in
/// comments, look in the example subdirectory which is a minimal
/// yet functional tool to do GET request performance testing.
/// With four calls:
///
/// init_curl();
/// LLCore::HttpRequest::createService();
/// LLCore::HttpRequest::startThread();
/// LLCore::HttpRequest * hr = new LLCore::HttpRequest();
///
/// the program is basically ready to issue requests.
///
/// HttpHandler. Having started life as a non-indra library,
/// this code broke away from the classic Responder model and
/// introduced a handler class to represent an interface for
/// request responses. This is a non-reference-counted entity
/// which can be used as a base class or a mixin. An instance
/// of a handler can be used for each request or can be shared
/// among any number of requests. Your choice but expect to
/// code something like the following:
///
/// class AppHandler : public LLCore::HttpHandler
/// {
/// public:
/// virtual void onCompleted(HttpHandle handle,
/// HttpResponse * response)
/// {
/// ...
/// }
/// ...
/// };
/// ...
/// handler = new handler(...);
///
///
/// Issuing requests. Using 'hr' above,
///
/// hr->requestGet(HttpRequest::DEFAULT_POLICY_ID,
/// 0, // Priority, not used yet
/// url,
/// NULL, // options
/// NULL, // additional headers
/// handler);
///
/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID,
/// the request was successfully issued and there will eventally
/// be a status delivered to the handler. If invalid is returnedd,
/// the actual status can be retrieved by calling hr->getStatus().
///
/// Completing requests and delivering notifications. Operations
/// are all performed by the worker thread and will be driven to
/// completion regardless of caller actions. Notification of
/// completion (success or failure) is done by calls to
/// HttpRequest::update() which will invoke handlers for completed
/// requests:
///
/// hr->update(0);
/// // Callbacks into handler->onCompleted()
///
///
/// Threads.
///
/// Threads are supported and used by this library. The various
/// classes, methods and members are documented with thread
/// constraints which programmers must follow and which are
/// defined as follows:
///
/// consumer Any thread that has instanced HttpRequest and is
/// issuing requests. A particular instance can only
/// be used by one consumer thread but a consumer may
/// have many instances available to it.
/// init Special consumer thread, usually the main thread,
/// involved in setting up the library at startup.
/// worker Thread used internally by the library to perform
/// HTTP operations. Consumers will not have to deal
/// with this thread directly but some APIs are reserved
/// to it.
/// any Consumer or worker thread.
///
/// For the most part, API users will not have to do much in the
/// way of ensuring thread safely. However, there is a tremendous
/// amount of sharing between threads of read-only data. So when
/// documentation declares that an option or header instance
/// becomes shared between consumer and worker, the consumer must
/// not modify the shared object.
///
/// Internally, there is almost no thread synchronization. During
/// normal operations (non-init, non-term), only the request queue
/// and the multiple reply queues are shared between threads and
/// only here are mutexes used.
///
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "llsd.h"
#include <string>
#include <curl/curl.h>
#include "boost/noncopyable.hpp"
namespace LLCore
{
/// All queued requests are represented by an HttpHandle value.
/// The invalid value is returned when a request failed to queue.
/// The actual status for these failures is then fetched with
/// HttpRequest::getStatus().
///
/// The handle is valid only for the life of a request. On
/// return from any HttpHandler notification, the handle immediately
/// becomes invalid and may be recycled for other queued requests.
typedef void * HttpHandle;
#define LLCORE_HTTP_HANDLE_INVALID (NULL)
/// For internal scheduling and metrics, we use a microsecond
/// timebase compatible with the environment.
typedef U64 HttpTime;
/// Error codes defined by the library itself as distinct from
/// libcurl (or any other transport provider).
enum HttpError
{
// Successful value compatible with the libcurl codes.
HE_SUCCESS = 0,
// Intended for HTTP reply codes 100-999, indicates that
// the reply should be considered an error by the application.
HE_REPLY_ERROR = 1,
// Service is shutting down and requested operation will
// not be queued or performed.
HE_SHUTTING_DOWN = 2,
// Operation was canceled by request.
HE_OP_CANCELED = 3,
// Invalid content range header received.
HE_INV_CONTENT_RANGE_HDR = 4,
// Request handle not found
HE_HANDLE_NOT_FOUND = 5,
// Invalid datatype for option/setting
HE_INVALID_ARG = 6,
// Option hasn't been explicitly set
HE_OPT_NOT_SET = 7,
// Option not dynamic, must be set during init phase
HE_OPT_NOT_DYNAMIC = 8,
// Invalid HTTP status code returned by server
HE_INVALID_HTTP_STATUS = 9,
// Couldn't allocate resource, typically libcurl handle
HE_BAD_ALLOC = 10
}; // end enum HttpError
/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP
/// reply status codes and internal errors as well. The encapsulation
/// isn't expected to completely isolate the caller from libcurl but
/// basic operational tests (success or failure) are provided.
///
/// Non-HTTP status are encoded as (type, status) with type being
/// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status
/// being the success/error code from that domain. HTTP status
/// is encoded as (status, error_flag). Status should be in the
/// range [100, 999] and error_flag is either HE_SUCCESS or
/// HE_REPLY_ERROR to indicate whether this should be treated as
/// a successful status or an error. The application is responsible
/// for making that determination and a range like [200, 299] isn't
/// automatically assumed to be definitive.
///
/// Examples:
///
/// 1. Construct a default, successful status code:
/// HttpStatus();
///
/// 2. Construct a successful, HTTP 200 status code:
/// HttpStatus(200);
///
/// 3. Construct a failed, HTTP 404 not-found status code:
/// HttpStatus(404);
///
/// 4. Construct a failed libcurl couldn't connect status code:
/// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
///
/// 5. Construct an HTTP 301 status code to be treated as success:
/// HttpStatus(301, HE_SUCCESS);
///
/// 6. Construct a failed status of HTTP Status 499 with a custom error message
/// HttpStatus(499, "Failed LLSD Response");
struct HttpStatus
{
typedef unsigned short type_enum_t;
HttpStatus()
{
mDetails = std::shared_ptr<Details>(new Details(LLCORE, HE_SUCCESS));
}
HttpStatus(type_enum_t type, short status)
{
mDetails = std::shared_ptr<Details>(new Details(type, status));
}
HttpStatus(int http_status)
{
mDetails = std::shared_ptr<Details>(new Details(http_status,
(http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR));
llassert(http_status >= 100 && http_status <= 999);
}
HttpStatus(int http_status, const std::string &message)
{
mDetails = std::shared_ptr<Details>(new Details(http_status,
(http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR));
llassert(http_status >= 100 && http_status <= 999);
mDetails->mMessage = message;
}
HttpStatus(const HttpStatus & rhs)
{
mDetails = rhs.mDetails;
}
~HttpStatus()
{
}
HttpStatus & operator=(const HttpStatus & rhs)
{
mDetails = rhs.mDetails;
return *this;
}
HttpStatus & clone(const HttpStatus &rhs)
{
mDetails = std::shared_ptr<Details>(new Details(*rhs.mDetails));
return *this;
}
static const type_enum_t EXT_CURL_EASY = 0; ///< mStatus is an error from a curl_easy_*() call
static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call
static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code
///< 100-999 directly represent HTTP status codes
/// Test for successful status in the code regardless
/// of error source (internal, libcurl).
///
/// @return 'true' when status is successful.
///
operator bool() const
{
return 0 == mDetails->mStatus;
}
/// Inverse of previous operator.
///
/// @return 'true' on any error condition
bool operator !() const
{
return 0 != mDetails->mStatus;
}
/// Equality and inequality tests to bypass bool conversion
/// which will do the wrong thing in conditional expressions.
bool operator==(const HttpStatus & rhs) const
{
return (*mDetails == *rhs.mDetails);
}
bool operator!=(const HttpStatus & rhs) const
{
return ! operator==(rhs);
}
/// Convert to single numeric representation. Mainly
/// for logging or other informal purposes. Also
/// creates an ambiguous second path to integer conversion
/// which tends to find programming errors such as formatting
/// the status to a stream (operator<<).
operator U32() const;
U32 toULong() const
{
return operator U32();
}
/// And to convert to a hex string.
std::string toHex() const;
/// Convert status to a string representation. For
/// success, returns an empty string. For failure
/// statuses, a string as appropriate for the source of
/// the error code (libcurl easy, libcurl multi, or
/// LLCore itself).
std::string toString() const;
/// Convert status to a compact string representation
/// of the form: "<type>_<value>". The <type> will be
/// one of: Core, Http, Easy, Multi, Unknown. And
/// <value> will be an unsigned integer. More easily
/// interpreted than the hex representation, it's still
/// compact and easily searched.
std::string toTerseString() const;
/// Returns true if the status value represents an
/// HTTP response status (100 - 999).
bool isHttpStatus() const
{
return mDetails->mType >= type_enum_t(100) && mDetails->mType <= type_enum_t(999);
}
/// Returns true if the status is one that will be retried
/// internally. Provided for external consumption for cases
/// where that logic needs to be replicated. Only applies
/// to failed statuses, successful statuses will return false.
bool isRetryable() const;
/// Returns the currently set status code as a raw number
///
short getStatus() const
{
return mDetails->mStatus;
}
/// Returns the currently set status type
///
type_enum_t getType() const
{
return mDetails->mType;
}
/// Returns an optional error message if one has been set.
///
std::string getMessage() const
{
return mDetails->mMessage;
}
/// Sets an optional error message
///
void setMessage(const std::string &message)
{
mDetails->mMessage = message;
}
/// Retrieves data about an optionally recorded SSL certificate.
LLSD getErrorData() const
{
return mDetails->mErrorData;
}
/// Optionally sets an SSL certificate on this status.
void setErrorData(LLSD data)
{
mDetails->mErrorData = data;
}
private:
struct Details
{
Details(type_enum_t type, short status):
mType(type),
mStatus(status),
mMessage(),
mErrorData()
{}
Details(const Details &rhs) :
mType(rhs.mType),
mStatus(rhs.mStatus),
mMessage(rhs.mMessage),
mErrorData(rhs.mErrorData)
{}
bool operator == (const Details &rhs) const
{
return (mType == rhs.mType) && (mStatus == rhs.mStatus);
}
type_enum_t mType;
short mStatus;
std::string mMessage;
LLSD mErrorData;
};
std::shared_ptr<Details> mDetails;
}; // end struct HttpStatus
/// A namespace for several free methods and low level utilities.
namespace LLHttp
{
typedef std::shared_ptr<CURL> CURL_ptr;
void initialize();
void cleanup();
CURL_ptr createEasyHandle();
std::string getCURLVersion();
void check_curl_code(CURLcode code, int curl_setopt_option);
}
} // end namespace LLCore
#endif // _LLCORE_HTTP_COMMON_H_
+92
View File
@@ -0,0 +1,92 @@
/**
* @file httphandler.h
* @brief Public-facing declarations for the HttpHandler class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_HANDLER_H_
#define _LLCORE_HTTP_HANDLER_H_
#include "httpcommon.h"
namespace LLCore
{
class HttpResponse;
/// HttpHandler defines an interface used by the library to
/// notify library callers of significant events, currently
/// request completion. Callers must derive or mixin this class
/// then provide an implementation of the @see onCompleted
/// method to receive such notifications. An instance may
/// be shared by any number of requests and across instances
/// of HttpRequest running in the same thread.
///
/// Threading: HttpHandler itself is interface and is
/// tread-compatible. Most derivations, however, will have
/// different constraints.
///
/// Allocation: Not refcounted, may be stack allocated though
/// that is rarely a good idea. Queued requests and replies keep
/// a naked pointer to the handler and this can result in a
/// dangling pointer if lifetimes aren't managed correctly.
///
/// *TODO: public std::enable_shared_from_this<HttpHandler>
class HttpHandler
{
public:
typedef std::shared_ptr<HttpHandler> ptr_t;
typedef std::weak_ptr<HttpHandler> wptr_t;
virtual ~HttpHandler()
{ }
/// Method invoked during calls to @see update(). Each invocation
/// represents the completion of some requested operation. Caller
/// can identify the request from the handle and interrogate the
/// response argument for success/failure, data and other information.
///
/// @param handle Identifier of the request generating
/// the notification.
/// @param response Supplies detailed information about
/// the request including status codes
/// (both programming and HTTP), HTTP body
/// data and encodings, headers, etc.
/// The response object is refcounted and
/// the called code may retain the object
/// by invoking @see addRef() on it. The
/// library itself drops all references to
/// to object on return and never touches
/// it again.
///
virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0;
}; // end class HttpHandler
} // end namespace LLCore
#endif // _LLCORE_HTTP_HANDLER_H_
+200
View File
@@ -0,0 +1,200 @@
/**
* @file httpheaders.cpp
* @brief Implementation of the HTTPHeaders 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$
*/
#include "httpheaders.h"
#include "llstring.h"
namespace LLCore
{
HttpHeaders::HttpHeaders()
{}
HttpHeaders::~HttpHeaders()
{}
void
HttpHeaders::clear()
{
mHeaders.clear();
}
void HttpHeaders::append(const std::string & name, const std::string & value)
{
mHeaders.push_back(value_type(name, value));
}
void HttpHeaders::append(const char * name, const char * value)
{
mHeaders.push_back(value_type(name, value));
}
void HttpHeaders::appendNormal(const char * header, size_t size)
{
std::string name;
std::string value;
int col_pos(0);
for (; col_pos < size; ++col_pos)
{
if (':' == header[col_pos])
break;
}
if (col_pos < size)
{
// Looks like a header, split it and normalize.
// Name is everything before the colon, may be zero-length.
name.assign(header, col_pos);
// Value is everything after the colon, may also be zero-length.
const size_t val_len(size - col_pos - 1);
if (val_len)
{
value.assign(header + col_pos + 1, val_len);
}
// Clean the strings
LLStringUtil::toLower(name);
LLStringUtil::trim(name);
LLStringUtil::trimHead(value);
}
else
{
// Uncertain what this is, we'll pack it as
// a name without a value. Won't clean as we don't
// know what it is...
name.assign(header, size);
}
mHeaders.push_back(value_type(name, value));
}
// Find from end to simulate a tradition of using single-valued
// std::map for this in the past.
const std::string * HttpHeaders::find(const std::string &name) const
{
const_reverse_iterator iend(rend());
for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
{
if ((*iter).first == name)
{
return &(*iter).second;
}
}
return NULL;
}
void HttpHeaders::remove(const char *name)
{
remove(std::string(name));
}
void HttpHeaders::remove(const std::string &name)
{
iterator iend(end());
for (iterator iter(begin()); iend != iter; ++iter)
{
if ((*iter).first == name)
{
mHeaders.erase(iter);
return;
}
}
}
// Standard Iterators
HttpHeaders::iterator HttpHeaders::begin()
{
return mHeaders.begin();
}
HttpHeaders::const_iterator HttpHeaders::begin() const
{
return mHeaders.begin();
}
HttpHeaders::iterator HttpHeaders::end()
{
return mHeaders.end();
}
HttpHeaders::const_iterator HttpHeaders::end() const
{
return mHeaders.end();
}
// Standard Reverse Iterators
HttpHeaders::reverse_iterator HttpHeaders::rbegin()
{
return mHeaders.rbegin();
}
HttpHeaders::const_reverse_iterator HttpHeaders::rbegin() const
{
return mHeaders.rbegin();
}
HttpHeaders::reverse_iterator HttpHeaders::rend()
{
return mHeaders.rend();
}
HttpHeaders::const_reverse_iterator HttpHeaders::rend() const
{
return mHeaders.rend();
}
// Return the raw container to the caller.
//
// To be used FOR UNIT TESTS ONLY.
//
HttpHeaders::container_t & HttpHeaders::getContainerTESTONLY()
{
return mHeaders;
}
} // end namespace LLCore
+191
View File
@@ -0,0 +1,191 @@
/**
* @file httpheaders.h
* @brief Public-facing declarations for the HttpHeaders 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 _LLCORE_HTTP_HEADERS_H_
#define _LLCORE_HTTP_HEADERS_H_
#include "httpcommon.h"
#include <string>
#include "_refcounted.h"
namespace LLCore
{
///
/// Maintains an ordered list of name/value pairs representing
/// HTTP header lines. This is used both to provide additional
/// headers when making HTTP requests and in responses when the
/// caller has asked that headers be returned (not the default
/// option).
///
/// Class is mostly a thin wrapper around a vector of pairs
/// of strings. Methods provided are few and intended to
/// reflect actual use patterns. These include:
/// - Clearing the list
/// - Appending a name/value pair to the vector
/// - Processing a raw byte string into a normalized name/value
/// pair and appending the result.
/// - Simple case-sensitive find-last-by-name search
/// - Forward and reverse iterators over all pairs
///
/// Container is ordered and multi-valued. Headers are
/// written in the order in which they are appended and
/// are stored in the order in which they're received from
/// the wire. The same header may appear two or more times
/// in any container. Searches using the simple find()
/// interface will find only the last occurrence (somewhat
/// simulates the use of std::map). Fuller searches require
/// the use of an iterator. Headers received from the wire
/// are only returned from the last request when redirections
/// are involved.
///
/// Threading: Not intrinsically thread-safe. It *is* expected
/// that callers will build these objects and then share them
/// via reference counting with the worker thread. The implication
/// is that once an HttpHeader instance is handed to a request,
/// the object must be treated as read-only.
///
/// Allocation: Refcounted, heap only. Caller of the
/// constructor is given a refcount.
///
class HttpHeaders: private boost::noncopyable
{
public:
typedef std::pair<std::string, std::string> header_t;
typedef std::vector<header_t> container_t;
typedef container_t::iterator iterator;
typedef container_t::const_iterator const_iterator;
typedef container_t::reverse_iterator reverse_iterator;
typedef container_t::const_reverse_iterator const_reverse_iterator;
typedef container_t::value_type value_type;
typedef container_t::size_type size_type;
typedef std::shared_ptr<HttpHeaders> ptr_t;
public:
/// @post In addition to the instance, caller has a refcount
/// to the instance. A call to @see release() will destroy
/// the instance.
HttpHeaders();
virtual ~HttpHeaders(); // Use release()
//typedef LLCoreInt::IntrusivePtr<HttpHeaders> ptr_t;
protected:
HttpHeaders(const HttpHeaders &); // Not defined
void operator=(const HttpHeaders &); // Not defined
public:
// Empty the list of headers.
void clear();
// Append a name/value pair supplied as either std::strings
// or NUL-terminated char * to the header list. No normalization
// is performed on the strings. No conformance test is
// performed (names may contain spaces, colons, etc.).
//
void append(const std::string & name, const std::string & value);
void append(const char * name, const char * value);
// Extract a name/value pair from a raw byte array using
// the first colon character as a separator. Input string
// does not need to be NUL-terminated. Resulting name/value
// pair is appended to the header list.
//
// Normalization is performed on the name/value pair as
// follows:
// - name is lower-cased according to mostly ASCII rules
// - name is left- and right-trimmed of spaces and tabs
// - value is left-trimmed of spaces and tabs
// - either or both of name and value may be zero-length
//
// By convention, headers read from the wire will be normalized
// in this fashion prior to delivery to any HttpHandler code.
// Headers to be written to the wire are left as appended to
// the list.
void appendNormal(const char * header, size_t size);
// Perform a simple, case-sensitive search of the header list
// returning a pointer to the value of the last matching header
// in the header list. If none is found, a NULL pointer is returned.
//
// Any pointer returned references objects in the container itself
// and will have the same lifetime as this class. If you want
// the value beyond the lifetime of this instance, make a copy.
//
// @arg name C-style string giving the name of a header
// to search. The comparison is case-sensitive
// though list entries may have been normalized
// to lower-case.
//
// @return NULL if the header wasn't found otherwise
// a pointer to a std::string in the container.
// Pointer is valid only for the lifetime of
// the container or until container is modifed.
const std::string * find(const std::string &name) const;
const std::string * find(const char * name) const
{
return find(std::string(name));
}
// Remove the header from the list if found.
//
void remove(const std::string &name);
void remove(const char *name);
// Count of headers currently in the list.
size_type size() const
{
return mHeaders.size();
}
// Standard std::vector-based forward iterators.
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
// Standard std::vector-based reverse iterators.
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
public:
// For unit tests only - not a public API
container_t & getContainerTESTONLY();
protected:
container_t mHeaders;
}; // end class HttpHeaders
} // end namespace LLCore
#endif // _LLCORE_HTTP_HEADERS_H_
+145
View File
@@ -0,0 +1,145 @@
/**
* @file httpoptions.cpp
* @brief Implementation of the HTTPOptions 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$
*/
#include "httpoptions.h"
#include "lldefs.h"
#include "_httpinternal.h"
namespace LLCore
{
bool HttpOptions::sDefaultVerifyPeer = false;
HttpOptions::HttpOptions() :
mWantHeaders(false),
mTracing(HTTP_TRACE_OFF),
mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
mRetries(HTTP_RETRY_COUNT_DEFAULT),
mMinRetryBackoff(HTTP_RETRY_BACKOFF_MIN_DEFAULT),
mMaxRetryBackoff(HTTP_RETRY_BACKOFF_MAX_DEFAULT),
mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT),
mFollowRedirects(true),
mVerifyPeer(sDefaultVerifyPeer),
mVerifyHost(false),
mDNSCacheTimeout(-1L),
mNoBody(false),
mLastModified(0) // <FS:Ansariel> GetIfModified request
{}
HttpOptions::~HttpOptions()
{}
void HttpOptions::setWantHeaders(bool wanted)
{
mWantHeaders = wanted;
}
void HttpOptions::setTrace(long level)
{
mTracing = int(level);
}
void HttpOptions::setTimeout(unsigned int timeout)
{
mTimeout = timeout;
}
void HttpOptions::setTransferTimeout(unsigned int timeout)
{
mTransferTimeout = timeout;
}
void HttpOptions::setRetries(unsigned int retries)
{
mRetries = retries;
}
void HttpOptions::setMinBackoff(HttpTime delay)
{
mMinRetryBackoff = delay;
}
void HttpOptions::setMaxBackoff(HttpTime delay)
{
mMaxRetryBackoff = delay;
}
void HttpOptions::setUseRetryAfter(bool use_retry)
{
mUseRetryAfter = use_retry;
}
void HttpOptions::setFollowRedirects(bool follow_redirect)
{
mFollowRedirects = follow_redirect;
}
void HttpOptions::setSSLVerifyPeer(bool verify)
{
mVerifyPeer = verify;
}
void HttpOptions::setSSLVerifyHost(bool verify)
{
mVerifyHost = verify;
}
void HttpOptions::setDNSCacheTimeout(int timeout)
{
mDNSCacheTimeout = timeout;
}
void HttpOptions::setHeadersOnly(bool nobody)
{
mNoBody = nobody;
if (mNoBody)
{
setWantHeaders(true);
setSSLVerifyPeer(false);
}
}
void HttpOptions::setDefaultSSLVerifyPeer(bool verify)
{
sDefaultVerifyPeer = verify;
}
// <FS:Ansariel> GetIfModified request
void HttpOptions::setLastModified(long last_modified)
{
mLastModified = last_modified;
}
// </FS:Ansariel>
} // end namespace LLCore
+219
View File
@@ -0,0 +1,219 @@
/**
* @file httpoptions.h
* @brief Public-facing declarations for the HTTPOptions 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 _LLCORE_HTTP_OPTIONS_H_
#define _LLCORE_HTTP_OPTIONS_H_
#include "httpcommon.h"
#include "_refcounted.h"
namespace LLCore
{
/// Really a struct in spirit, it provides options that
/// modify HTTP requests.
///
/// Sharing instances across requests. It's intended that
/// these be shared across requests: caller can create one
/// of these, set it up as needed and then reference it
/// repeatedly in HTTP operations. But see the Threading
/// note about references.
///
/// Threading: While this class does nothing to ensure thread
/// safety, it *is* intended to be shared between the application
/// thread and the worker thread. This means that once an instance
/// is delivered to the library in request operations, the
/// option data must not be written until all such requests
/// complete and relinquish their references.
///
/// Allocation: Refcounted, heap only. Caller of the constructor
/// is given a refcount.
///
class HttpOptions : private boost::noncopyable
{
public:
HttpOptions();
typedef std::shared_ptr<HttpOptions> ptr_t;
virtual ~HttpOptions(); // Use release()
protected:
HttpOptions(const HttpOptions &); // Not defined
void operator=(const HttpOptions &); // Not defined
public:
// Default: false
void setWantHeaders(bool wanted);
bool getWantHeaders() const
{
return mWantHeaders;
}
// Default: 0
void setTrace(int long);
int getTrace() const
{
return mTracing;
}
// Default: 30
void setTimeout(unsigned int timeout);
unsigned int getTimeout() const
{
return mTimeout;
}
// Default: 0
void setTransferTimeout(unsigned int timeout);
unsigned int getTransferTimeout() const
{
return mTransferTimeout;
}
/// Sets the number of retries on an LLCore::HTTPRequest before the
/// request fails.
// Default: 5
void setRetries(unsigned int retries);
unsigned int getRetries() const
{
return mRetries;
}
/// Sets minimal delay before request retries. In microseconds.
/// HttpPolicy will increase delay from min to max with each retry
// Default: 1 000 000 mcs
void setMinBackoff(HttpTime delay);
HttpTime getMinBackoff() const
{
return mMinRetryBackoff;
}
/// Sets maximum delay before request retries. In microseconds.
/// HttpPolicy will increase delay from min to max with each retry
// Default: 5 000 000 mcs
void setMaxBackoff(HttpTime delay);
HttpTime getMaxBackoff() const
{
return mMaxRetryBackoff;
}
// Default: true
void setUseRetryAfter(bool use_retry);
bool getUseRetryAfter() const
{
return mUseRetryAfter;
}
/// Instructs the LLCore::HTTPRequest to follow redirects
/// Default: false
void setFollowRedirects(bool follow_redirect);
bool getFollowRedirects() const
{
return mFollowRedirects;
}
/// Instructs the LLCore::HTTPRequest to verify that the exchanged security
/// certificate is authentic.
/// Default: sDefaultVerifyPeer
void setSSLVerifyPeer(bool verify);
bool getSSLVerifyPeer() const
{
return mVerifyPeer;
}
/// Instructs the LLCore::HTTPRequest to verify that the name in the
/// security certificate matches the name of the host contacted.
/// Default: false
void setSSLVerifyHost(bool verify);
bool getSSLVerifyHost() const
{
return mVerifyHost;
}
/// Sets the time for DNS name caching in seconds. Setting this value
/// to 0 will disable name caching. Setting this value to -1 causes the
/// name cache to never time out.
/// Default: -1
void setDNSCacheTimeout(int timeout);
int getDNSCacheTimeout() const
{
return mDNSCacheTimeout;
}
/// Retrieve only the headers and status from the request. Setting this
/// to true implies setWantHeaders(true) as well.
/// Default: false
void setHeadersOnly(bool nobody);
bool getHeadersOnly() const
{
return mNoBody;
}
/// Sets default behavior for verifying that the name in the
/// security certificate matches the name of the host contacted.
/// Defaults false if not set, but should be set according to
/// viewer's initialization options and command argunments, see
/// NoVerifySSLCert
static void setDefaultSSLVerifyPeer(bool verify);
// <FS:Ansariel> GetIfModified request
void setLastModified(long last_modified);
long getLastModified() const
{
return mLastModified;
}
// </FS:Ansariel>
protected:
bool mWantHeaders;
int mTracing;
unsigned int mTimeout;
unsigned int mTransferTimeout;
unsigned int mRetries;
HttpTime mMinRetryBackoff;
HttpTime mMaxRetryBackoff;
bool mUseRetryAfter;
bool mFollowRedirects;
bool mVerifyPeer;
bool mVerifyHost;
int mDNSCacheTimeout;
bool mNoBody;
static bool sDefaultVerifyPeer;
long mLastModified; // <FS:Ansariel> GetIfModified request
}; // end class HttpOptions
} // end namespace HttpOptions
#endif // _LLCORE_HTTP_OPTIONS_H_
+567
View File
@@ -0,0 +1,567 @@
/**
* @file httprequest.cpp
* @brief Implementation of the 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$
*/
#include "httprequest.h"
#include "_httprequestqueue.h"
#include "_httpreplyqueue.h"
#include "_httpservice.h"
#include "_httppolicy.h"
#include "_httpoperation.h"
#include "_httpoprequest.h"
#include "_httpopcancel.h"
#include "_httpopsetget.h"
#include "lltimer.h"
#include "httpstats.h"
namespace
{
bool has_inited(false);
}
namespace LLCore
{
// ====================================
// HttpRequest Implementation
// ====================================
HttpRequest::HttpRequest()
: mReplyQueue(),
mRequestQueue(NULL)
{
mRequestQueue = HttpRequestQueue::instanceOf();
mRequestQueue->addRef();
mReplyQueue.reset( new HttpReplyQueue() );
HTTPStats::instance().recordHTTPRequest();
}
HttpRequest::~HttpRequest()
{
if (mRequestQueue)
{
mRequestQueue->release();
mRequestQueue = NULL;
}
mReplyQueue.reset();
}
// ====================================
// Policy Methods
// ====================================
HttpRequest::policy_t HttpRequest::createPolicyClass()
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return 0;
}
return HttpService::instanceOf()->createPolicyClass();
}
HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
long value, long * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, std::string * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, policyCallback_t value, policyCallback_t * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
long value, HttpHandler::ptr_t handler)
{
HttpStatus status;
HttpOpSetGet::ptr_t op(new HttpOpSetGet());
if (! (status = op->setupSet(opt, pclass, value)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, HttpHandler::ptr_t handler)
{
HttpStatus status;
HttpOpSetGet::ptr_t op (new HttpOpSetGet());
if (! (status = op->setupSet(opt, pclass, value)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
// ====================================
// Request Methods
// ====================================
HttpStatus HttpRequest::getStatus() const
{
return mLastReqStatus;
}
HttpHandle HttpRequest::requestGet(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupGet(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
const std::string & url,
size_t offset,
size_t len,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupGetByteRange(policy_id, url, offset, len, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestPost(policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupPost(policy_id, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestPut(policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (! (status = op->setupPut(policy_id, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestDelete(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (!(status = op->setupDelete(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestPatch(policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (!(status = op->setupPatch(policy_id, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestCopy(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (!(status = op->setupCopy(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestMove(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (!(status = op->setupMove(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestNoOp(HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOperation::ptr_t op (new HttpOpNull());
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpStatus HttpRequest::update(long usecs)
{
HttpOperation::ptr_t op;
if (usecs)
{
const HttpTime limit(totalTime() + HttpTime(usecs));
while (limit >= totalTime() && (op = mReplyQueue->fetchOp()))
{
// Process operation
op->visitNotifier(this);
// We're done with the operation
op.reset();
}
}
else
{
// Same as above, just no time limit
HttpReplyQueue::OpContainer replies;
mReplyQueue->fetchAll(replies);
if (! replies.empty())
{
for (HttpReplyQueue::OpContainer::iterator iter(replies.begin());
replies.end() != iter;
++iter)
{
// Swap op pointer for NULL;
op.reset();
op.swap(*iter);
// Process operation
op->visitNotifier(this);
// We're done with the operation
}
}
}
return HttpStatus();
}
// ====================================
// Request Management Methods
// ====================================
HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOperation::ptr_t op(new HttpOpCancel(request));
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
// ====================================
// Utility Methods
// ====================================
HttpStatus HttpRequest::createService()
{
HttpStatus status;
if (! has_inited)
{
HttpRequestQueue::init();
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
HttpService::init(rq);
HTTPStats::createInstance();
has_inited = true;
}
return status;
}
HttpStatus HttpRequest::destroyService()
{
HttpStatus status;
if (has_inited)
{
HTTPStats::deleteSingleton();
HttpService::term();
HttpRequestQueue::term();
has_inited = false;
}
return status;
}
HttpStatus HttpRequest::startThread()
{
HttpStatus status;
HttpService::instanceOf()->startThread();
return status;
}
HttpHandle HttpRequest::requestStopThread(HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOperation::ptr_t op(new HttpOpStop());
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return handle;
}
mLastReqStatus = status;
handle = op->getHandle();
return handle;
}
HttpHandle HttpRequest::requestSpin(int mode)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOperation::ptr_t op(new HttpOpSpin(mode));
op->setReplyPath(mReplyQueue, HttpHandler::ptr_t());
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return handle;
}
mLastReqStatus = status;
handle = op->getHandle();
return handle;
}
} // end namespace LLCore
+661
View File
@@ -0,0 +1,661 @@
/**
* @file httprequest.h
* @brief Public-facing declarations for HttpRequest class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCORE_HTTP_REQUEST_H_
#define _LLCORE_HTTP_REQUEST_H_
#include "httpcommon.h"
#include "httphandler.h"
#include "httpheaders.h"
#include "httpoptions.h"
namespace LLCore
{
class HttpRequestQueue;
class HttpReplyQueue;
class HttpService;
class HttpOperation;
class BufferArray;
/// HttpRequest supplies the entry into the HTTP transport
/// services in the LLCore libraries. Services provided include:
///
/// - Some, but not all, global initialization of libcurl.
/// - Starting asynchronous, threaded HTTP requests.
/// - Definition of policy classes affect request handling.
/// - Utilities to control request options and headers
///
/// Requests
///
/// The class supports the current HTTP request operations:
///
/// - requestGetByteRange: GET with Range header for a single range of bytes
/// - requestGet:
/// - requestPost:
/// - requestPut:
///
/// Policy Classes
///
/// <TBD>
///
/// Usage
///
/// <TBD>
///
/// Threading: An instance may only be used by one application/
/// consumer thread. But a thread may have as many instances of
/// this as it likes.
///
/// Allocation: Not refcounted, may be stack allocated though that
/// hasn't been tested. Queued requests can still run and any
/// queued replies will keep refcounts to the reply queue leading
/// to memory leaks.
///
/// @pre Before using this class (static or instances), some global
/// initialization is required. See @see httpcommon.h for more information.
///
/// @nosubgrouping
///
class HttpRequest
{
public:
HttpRequest();
virtual ~HttpRequest();
private:
HttpRequest(const HttpRequest &); // Disallowed
void operator=(const HttpRequest &); // Disallowed
public:
typedef unsigned int policy_t;
typedef std::shared_ptr<HttpRequest> ptr_t;
typedef std::weak_ptr<HttpRequest> wptr_t;
public:
/// @name PolicyMethods
/// @{
/// Represents a default, catch-all policy class that guarantees
/// eventual service for any HTTP request.
static const policy_t DEFAULT_POLICY_ID = 0;
static const policy_t INVALID_POLICY_ID = 0xFFFFFFFFU;
static const policy_t GLOBAL_POLICY_ID = 0xFFFFFFFEU;
/// Create a new policy class into which requests can be made.
///
/// All class creation must occur before threads are started and
/// transport begins. Policy classes are limited to a small value.
/// Currently that limit is the default class + 1.
///
/// @return If positive, the policy_id used to reference
/// the class in other methods. If 0, requests
/// for classes have exceeded internal limits
/// or caller has tried to create a class after
/// threads have been started. Caller must fallback
/// and recover.
///
static policy_t createPolicyClass();
enum EPolicyOption
{
/// Maximum number of connections the library will use to
/// perform operations. This is somewhat soft as the underlying
/// transport will cache some connections (up to 5).
/// A long value setting the maximum number of connections
/// allowed over all policy classes. Note that this will be
/// a somewhat soft value. There may be an additional five
/// connections per policy class depending upon runtime
/// behavior.
///
/// Both global and per-class
PO_CONNECTION_LIMIT,
/// Limits the number of connections used for a single
/// literal address/port pair within the class.
///
/// Per-class only
PO_PER_HOST_CONNECTION_LIMIT,
/// String containing a system-appropriate directory name
/// where SSL certs are stored.
///
/// Global only
PO_CA_PATH,
/// String giving a full path to a file containing SSL certs.
///
/// Global only
PO_CA_FILE,
/// String of host/port to use as simple HTTP proxy. This is
/// going to change in the future into something more elaborate
/// that may support richer schemes.
///
/// Global only
PO_HTTP_PROXY,
/// Long value that if non-zero enables the use of the
/// traditional LLProxy code for http/socks5 support. If
/// enabled, has priority over GP_HTTP_PROXY.
///
/// Global only
PO_LLPROXY,
/// Long value setting the logging trace level for the
/// library. Possible values are:
/// 0 - No tracing (default)
/// 1 - Basic tracing of request start, stop and major events.
/// 2 - Connection, header and payload size information from
/// HTTP transactions.
/// 3 - Partial logging of payload itself.
///
/// These values are also used in the trace modes for
/// individual requests in HttpOptions. Also be aware that
/// tracing tends to impact performance of the viewer.
///
/// Global only
PO_TRACE,
/// If greater than 1, suitable requests are allowed to
/// pipeline on their connections when they ask for it.
/// Value gives the maximum number of outstanding requests
/// on a connection.
///
/// There is some interaction between PO_CONNECTION_LIMIT,
/// PO_PER_HOST_CONNECTION_LIMIT, and PO_PIPELINING_DEPTH.
/// When PIPELINING_DEPTH is 0 or 1 (no pipelining), this
/// library manages connection lifecycle and honors the
/// PO_CONNECTION_LIMIT setting as the maximum in-flight
/// request limit. Libcurl itself may be caching additional
/// connections under its connection cache policy.
///
/// When PIPELINING_DEPTH is 2 or more, libcurl performs
/// connection management and both PO_CONNECTION_LIMIT and
/// PO_PER_HOST_CONNECTION_LIMIT should be set and non-zero.
/// In this case (as of libcurl 7.37.0), libcurl will
/// open new connections in preference to pipelining, up
/// to the above limits at which time pipelining begins.
/// And as usual, an additional cache of open but inactive
/// connections may still be maintained within libcurl.
/// For SL, a good rule-of-thumb is to set
/// PO_PER_HOST_CONNECTION_LIMIT to the user-visible
/// concurrency value and PO_CONNECTION_LIMIT to twice
/// that for baked texture loads and region crossings where
/// additional connection load will be tolerated. If
/// either limit is 0, libcurl will prefer pipelining
/// over connection creation, which is still interesting,
/// but won't be pursued at this time.
///
/// Per-class only
PO_PIPELINING_DEPTH,
/// Controls whether client-side throttling should be
/// performed on this policy class. Positive values
/// enable throttling and specify the request rate
/// (requests per second) that should be targeted.
/// A value of zero, the default, specifies no throttling.
///
/// Per-class only
PO_THROTTLE_RATE,
/// Controls the callback function used to control SSL CTX
/// certificate verification.
///
/// Global only
PO_SSL_VERIFY_CALLBACK,
PO_LAST // Always at end
};
/// Prototype for policy based callbacks. The callback methods will be executed
/// on the worker thread so no modifications should be made to the HttpHandler object.
typedef boost::function<HttpStatus(const std::string &, const HttpHandler::ptr_t &, void *)> policyCallback_t;
/// Set a policy option for a global or class parameter at
/// startup time (prior to thread start).
///
/// @param opt Enum of option to be set.
/// @param pclass For class-based options, the policy class ID to
/// be changed. For globals, specify GLOBAL_POLICY_ID.
/// @param value Desired value of option.
/// @param ret_value Pointer to receive effective set value
/// if successful. May be NULL if effective
/// value not wanted.
/// @return Standard status code.
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
long value, long * ret_value);
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, std::string * ret_value);
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
policyCallback_t value, policyCallback_t * ret_value);;
/// Set a parameter on a class-based policy option. Calls
/// made after the start of the servicing thread are
/// not honored and return an error status.
///
/// @param opt Enum of option to be set.
/// @param pclass For class-based options, the policy class ID to
/// be changed. Ignored for globals but recommend
/// using INVALID_POLICY_ID in this case.
/// @param value Desired value of option.
/// @return Handle of dynamic request. Use @see getStatus() if
/// the returned handle is invalid.
HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value,
HttpHandler::ptr_t handler);
HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value,
HttpHandler::ptr_t handler);
/// @}
/// @name RequestMethods
///
/// @{
/// Some calls expect to succeed as the normal part of operation and so
/// return a useful value rather than a status. When they do fail, the
/// status is saved and can be fetched with this method.
///
/// @return Status of the failing method invocation. If the
/// preceding call succeeded or other HttpStatus
/// returning calls immediately preceded this method,
/// the returned value may not be reliable.
///
HttpStatus getStatus() const;
/// Queue a full HTTP GET request to be issued for entire entity.
/// The request is queued and serviced by the working thread and
/// notification of completion delivered to the optional HttpHandler
/// argument during @see update() calls.
///
/// With a valid handle returned, it can be used to reference the
/// request in other requests (like cancellation) and will be an
/// argument when any HttpHandler object is invoked.
///
/// Headers supplied by default:
/// - Connection: keep-alive
/// - Accept: */*
/// - Accept-Encoding: deflate, gzip
/// - Keep-alive: 300
/// - Host: <stuff>
///
/// Some headers excluded by default:
/// - Pragma:
/// - Cache-control:
/// - Range:
/// - Transfer-Encoding:
/// - Referer:
///
/// @param policy_id Default or user-defined policy class under
/// which this request is to be serviced.
/// @param url URL with any encoded query parameters to
/// be accessed.
/// @param options Optional instance of an HttpOptions object
/// to provide additional controls over the request
/// function for this request only. Any such
/// object then becomes shared-read across threads
/// and no code should modify the HttpOptions
/// instance.
/// @param headers Optional instance of an HttpHeaders object
/// to provide additional and/or overridden
/// headers for the request. As with options,
/// the instance becomes shared-read across threads
/// and no code should modify the HttpHeaders
/// instance.
/// @param handler Optional pointer to an HttpHandler instance
/// whose onCompleted() method will be invoked
/// during calls to update(). This is a non-
/// reference-counted object which would be a
/// problem for shutdown and other edge cases but
/// the pointer is only dereferenced during
/// calls to update().
///
/// @return The handle of the request if successfully
/// queued or LLCORE_HTTP_HANDLE_INVALID if the
/// request could not be queued. In the latter
/// case, @see getStatus() will return more info.
///
HttpHandle requestGet(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP GET request to be issued with a 'Range' header.
/// The request is queued and serviced by the working thread and
/// notification of completion delivered to the optional HttpHandler
/// argument during @see update() calls.
///
/// With a valid handle returned, it can be used to reference the
/// request in other requests (like cancellation) and will be an
/// argument when any HttpHandler object is invoked.
///
/// Headers supplied by default:
/// - Connection: keep-alive
/// - Accept: */*
/// - Accept-Encoding: deflate, gzip
/// - Keep-alive: 300
/// - Host: <stuff>
/// - Range: <stuff> (will be omitted if offset == 0 and len == 0)
///
/// Some headers excluded by default:
/// - Pragma:
/// - Cache-control:
/// - Transfer-Encoding:
/// - Referer:
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param offset Offset of first byte into resource to be returned.
/// @param len Count of bytes to be returned
/// @param options @see requestGet()
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestGetByteRange(policy_t policy_id,
const std::string & url,
size_t offset,
size_t len,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP POST. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// Headers supplied by default:
/// - Connection: keep-alive
/// - Accept: */*
/// - Accept-Encoding: deflate, gzip
/// - Keep-Alive: 300
/// - Host: <stuff>
/// - Content-Length: <digits>
/// - Content-Type: application/x-www-form-urlencoded
///
/// Some headers excluded by default:
/// - Pragma:
/// - Cache-Control:
/// - Transfer-Encoding: ... chunked ...
/// - Referer:
/// - Content-Encoding:
/// - Expect:
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
/// to the content.
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestPost(policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP PUT. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// Headers supplied by default:
/// - Connection: keep-alive
/// - Accept: */*
/// - Accept-Encoding: deflate, gzip
/// - Keep-Alive: 300
/// - Host: <stuff>
/// - Content-Length: <digits>
///
/// Some headers excluded by default:
/// - Pragma:
/// - Cache-Control:
/// - Transfer-Encoding: ... chunked ...
/// - Referer:
/// - Content-Encoding:
/// - Expect:
/// - Content-Type:
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
/// to the content.
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestPut(policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP DELETE. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestDelete(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a full HTTP PATCH. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
/// to the content.
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestPatch(policy_t policy_id,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a full HTTP COPY. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestCopy(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a full HTTP MOVE. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestMove(policy_t policy_id,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a NoOp request.
/// The request is queued and serviced by the working thread which
/// immediately processes it and returns the request to the reply
/// queue.
///
/// @param handler @see requestGet()
/// @return "
///
HttpHandle requestNoOp(HttpHandler::ptr_t handler);
/// While all the heavy work is done by the worker thread, notifications
/// must be performed in the context of the application thread. These
/// are done synchronously during calls to this method which gives the
/// library control so notification can be performed. Application handlers
/// are expected to return 'quickly' and do any significant processing
/// outside of the notification callback to onCompleted().
///
/// @param usecs Maximum number of wallclock microseconds to
/// spend in the call. As hinted at above, this
/// is partly a function of application code so it's
/// a soft limit. A '0' value will run without
/// time limit until everything queued has been
/// delivered.
///
/// @return Standard status code.
HttpStatus update(long usecs);
/// @}
/// @name RequestMgmtMethods
///
/// @{
HttpHandle requestCancel(HttpHandle request, HttpHandler::ptr_t);
/// @}
/// @name UtilityMethods
///
/// @{
/// Initialization method that needs to be called before queueing any
/// requests. Doesn't start the worker thread and may be called befoer
/// or after policy setup.
static HttpStatus createService();
/// Mostly clean shutdown of services prior to exit. Caller is expected
/// to have stopped a running worker thread before calling this.
static HttpStatus destroyService();
/// Called once after @see createService() to start the worker thread.
/// Stopping the thread is achieved by requesting it via @see requestStopThread().
/// May be called before or after requests are issued.
static HttpStatus startThread();
/// Queues a request to the worker thread to have it stop processing
/// and exit (without exiting the program). When the operation is
/// picked up by the worker thread, it immediately processes it and
/// begins detaching from refcounted resources like request and
/// reply queues and then returns to the host OS. It *does* queue a
/// reply to give the calling application thread a notification that
/// the operation has been performed.
///
/// @param handler (optional)
/// @return The handle of the request if successfully
/// queued or LLCORE_HTTP_HANDLE_INVALID if the
/// request could not be queued. In the latter
/// case, @see getStatus() will return more info.
/// As the request cannot be cancelled, the handle
/// is generally not useful.
///
HttpHandle requestStopThread(HttpHandler::ptr_t handler);
/// Queue a Spin request.
/// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for
/// test purposes.
///
/// @param mode 0 for hard spin, 1 for soft spin
/// @return Standard handle return cases.
///
HttpHandle requestSpin(int mode);
/// @}
protected:
private:
typedef std::shared_ptr<HttpReplyQueue> HttpReplyQueuePtr_t;
/// @name InstanceData
///
/// @{
HttpStatus mLastReqStatus;
HttpReplyQueuePtr_t mReplyQueue;
HttpRequestQueue * mRequestQueue;
/// @}
// ====================================
/// @name GlobalState
///
/// @{
///
/// Must be established before any threading is allowed to
/// start.
///
/// @}
// End Global State
// ====================================
}; // end class HttpRequest
} // end namespace LLCore
#endif // _LLCORE_HTTP_REQUEST_H_
+85
View File
@@ -0,0 +1,85 @@
/**
* @file httpresponse.cpp
* @brief
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012-2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "httpresponse.h"
#include "bufferarray.h"
#include "httpheaders.h"
namespace LLCore
{
HttpResponse::HttpResponse()
: LLCoreInt::RefCounted(true),
mReplyOffset(0U),
mReplyLength(0U),
mReplyFullLength(0U),
mBufferArray(NULL),
mHeaders(),
mRetries(0U),
m503Retries(0U),
mRequestUrl()
{}
HttpResponse::~HttpResponse()
{
setBody(NULL);
//setHeaders();
}
void HttpResponse::setBody(BufferArray * ba)
{
if (mBufferArray == ba)
return;
if (mBufferArray)
{
mBufferArray->release();
}
if (ba)
{
ba->addRef();
}
mBufferArray = ba;
}
void HttpResponse::setHeaders(HttpHeaders::ptr_t &headers)
{
mHeaders = headers;
}
size_t HttpResponse::getBodySize() const
{
return (mBufferArray) ? mBufferArray->size() : 0;
}
} // end namespace LLCore
+237
View File
@@ -0,0 +1,237 @@
/**
* @file httpresponse.h
* @brief Public-facing declarations for the HttpResponse 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 _LLCORE_HTTP_RESPONSE_H_
#define _LLCORE_HTTP_RESPONSE_H_
#include <string>
#include "httpcommon.h"
#include "httpheaders.h"
#include "_refcounted.h"
namespace LLCore
{
class BufferArray;
class HttpHeaders;
/// HttpResponse is instantiated by the library and handed to
/// the caller during callbacks to the handler. It supplies
/// all the status, header and HTTP body data the caller is
/// interested in. Methods provide simple getters to return
/// individual pieces of the response.
///
/// Typical usage will have the caller interrogate the object
/// during the handler callback and then simply returning.
/// But instances are refcounted and and callers can add a
/// reference and hold onto the object after the callback.
///
/// Threading: Not intrinsically thread-safe.
///
/// Allocation: Refcounted, heap only. Caller of the constructor
/// is given a refcount.
///
class HttpResponse : public LLCoreInt::RefCounted
{
public:
HttpResponse();
protected:
virtual ~HttpResponse(); // Use release()
HttpResponse(const HttpResponse &); // Not defined
void operator=(const HttpResponse &); // Not defined
public:
/// Statistics for the HTTP
struct TransferStats
{
typedef std::shared_ptr<TransferStats> ptr_t;
TransferStats() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
/// Returns the final status of the requested operation.
///
HttpStatus getStatus() const
{
return mStatus;
}
void setStatus(const HttpStatus & status)
{
mStatus = status;
}
/// Simple getter for the response body returned as a scatter/gather
/// buffer. If the operation doesn't produce data (such as the Null
/// or StopThread operations), this may be NULL.
///
/// Caller can hold onto the response by incrementing the reference
/// count of the returned object.
BufferArray * getBody() const
{
return mBufferArray;
}
/// Safely get the size of the body buffer. If the body buffer is missing
/// return 0 as the size.
size_t getBodySize() const;
/// Set the response data in the instance. Will drop the reference
/// count to any existing data and increment the count of that passed
/// in. It is legal to set the data to NULL.
void setBody(BufferArray * ba);
/// And a getter for the headers. And as with @see getResponse(),
/// if headers aren't available because the operation doesn't produce
/// any or delivery of headers wasn't requested in the options, this
/// will be NULL.
///
/// Caller can hold onto the headers by incrementing the reference
/// count of the returned object.
HttpHeaders::ptr_t getHeaders() const
{
return mHeaders;
}
/// Behaves like @see setResponse() but for header data.
void setHeaders(HttpHeaders::ptr_t &headers);
/// If a 'Range:' header was used, these methods are involved
/// in setting and returning data about the actual response.
/// If both @offset and @length are returned as 0, we probably
/// didn't get a Content-Range header in the response. This
/// occurs with various Capabilities-based services and the
/// caller is going to have to make assumptions on receipt of
/// a 206 status. The @full value may also be zero in cases of
/// parsing problems or a wild-carded length response.
///
/// These values will not necessarily agree with the data in
/// the body itself (if present). The BufferArray object
/// is authoritative for actual data length.
void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const
{
*offset = mReplyOffset;
*length = mReplyLength;
*full = mReplyFullLength;
}
void setRange(unsigned int offset, unsigned int length, unsigned int full_length)
{
mReplyOffset = offset;
mReplyLength = length;
mReplyFullLength = full_length;
}
///
const std::string & getContentType() const
{
return mContentType;
}
void setContentType(const std::string & con_type)
{
mContentType = con_type;
}
/// Get and set retry attempt information on the request.
void getRetries(unsigned int * retries, unsigned int * retries_503) const
{
if (retries)
{
*retries = mRetries;
}
if (retries_503)
{
*retries_503 = m503Retries;
}
}
void setRetries(unsigned int retries, unsigned int retries_503)
{
mRetries = retries;
m503Retries = retries_503;
}
void setTransferStats(TransferStats::ptr_t &stats)
{
mStats = stats;
}
TransferStats::ptr_t getTransferStats()
{
return mStats;
}
void setRequestURL(const std::string &url)
{
mRequestUrl = url;
}
const std::string &getRequestURL() const
{
return mRequestUrl;
}
void setRequestMethod(const std::string &method)
{
mRequestMethod = method;
}
const std::string &getRequestMethod() const
{
return mRequestMethod;
}
protected:
// Response data here
HttpStatus mStatus;
unsigned int mReplyOffset;
unsigned int mReplyLength;
unsigned int mReplyFullLength;
BufferArray * mBufferArray;
HttpHeaders::ptr_t mHeaders;
std::string mContentType;
unsigned int mRetries;
unsigned int m503Retries;
std::string mRequestUrl;
std::string mRequestMethod;
TransferStats::ptr_t mStats;
};
} // end namespace LLCore
#endif // _LLCORE_HTTP_RESPONSE_H_
+108
View File
@@ -0,0 +1,108 @@
/**
* @file llviewerstats.cpp
* @brief LLViewerStats class implementation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "httpstats.h"
#include "llerror.h"
namespace LLCore
{
HTTPStats::HTTPStats()
{
resetStats();
}
HTTPStats::~HTTPStats()
{
}
void HTTPStats::resetStats()
{
mResutCodes.clear();
mDataDown.reset();
mDataUp.reset();
mRequests = 0;
}
void HTTPStats::recordResultCode(S32 code)
{
std::map<S32, S32>::iterator it;
it = mResutCodes.find(code);
if (it == mResutCodes.end())
mResutCodes[code] = 1;
else
(*it).second = (*it).second + 1;
}
namespace
{
std::string byte_count_converter(F32 bytes)
{
static const char unit_suffix[] = { 'B', 'K', 'M', 'G' };
F32 value = bytes;
int suffix = 0;
while ((value > 1024.0) && (suffix < 3))
{
value /= 1024.0;
++suffix;
}
std::stringstream out;
out << std::setprecision(4) << value << unit_suffix[suffix];
return out.str();
}
}
void HTTPStats::dumpStats()
{
std::stringstream out;
out << "HTTP DATA SUMMARY" << std::endl;
out << "HTTP Transfer counts:" << std::endl;
out << "Data Sent: " << byte_count_converter(mDataUp.getSum()) << " (" << mDataUp.getSum() << ")" << std::endl;
out << "Data Recv: " << byte_count_converter(mDataDown.getSum()) << " (" << mDataDown.getSum() << ")" << std::endl;
out << "Total requests: " << mRequests << "(request objects created)" << std::endl;
out << std::endl;
out << "Result Codes:" << std::endl << "--- -----" << std::endl;
for (std::map<S32, S32>::iterator it = mResutCodes.begin(); it != mResutCodes.end(); ++it)
{
out << (*it).first << " " << (*it).second << std::endl;
}
LL_WARNS("HTTPCore") << out.str() << LL_ENDL;
}
}
+74
View File
@@ -0,0 +1,74 @@
/**
* @file llviewerim_peningtats.h
* @brief LLViewerStats class header file
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLVIEWERSTATS_H
#define LL_LLVIEWERSTATS_H
#include "lltracerecording.h"
#include "lltrace.h"
#include "llstatsaccumulator.h"
#include "llsingleton.h"
#include "llsd.h"
namespace LLCore
{
class HTTPStats final : public LLSimpleton<HTTPStats>
{
public:
HTTPStats();
~HTTPStats();
void resetStats();
typedef LLStatsAccumulator StatsAccumulator;
void recordDataDown(size_t bytes)
{
mDataDown.push((F32)bytes);
}
void recordDataUp(size_t bytes)
{
mDataUp.push((F32)bytes);
}
void recordHTTPRequest() { ++mRequests; }
void recordResultCode(S32 code);
void dumpStats();
private:
StatsAccumulator mDataDown;
StatsAccumulator mDataUp;
S32 mRequests;
std::map<S32, S32> mResutCodes;
};
}
#endif // LL_LLVIEWERSTATS_H
+135
View File
@@ -0,0 +1,135 @@
/**
* @file llhttpconstants.cpp
* @brief Implementation of the HTTP request / response constant lookups
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013-2014, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llhttpconstants.h"
#include "lltimer.h"
// for curl_getdate() (apparently parsing RFC 1123 dates is hard)
#include <curl/curl.h>
// Outgoing headers. Do *not* use these to check incoming headers.
// For incoming headers, use the lower-case headers, below.
const std::string HTTP_OUT_HEADER_ACCEPT("Accept");
const std::string HTTP_OUT_HEADER_ACCEPT_CHARSET("Accept-Charset");
const std::string HTTP_OUT_HEADER_ACCEPT_ENCODING("Accept-Encoding");
const std::string HTTP_OUT_HEADER_ACCEPT_LANGUAGE("Accept-Language");
const std::string HTTP_OUT_HEADER_ACCEPT_RANGES("Accept-Ranges");
const std::string HTTP_OUT_HEADER_AGE("Age");
const std::string HTTP_OUT_HEADER_ALLOW("Allow");
const std::string HTTP_OUT_HEADER_AUTHORIZATION("Authorization");
const std::string HTTP_OUT_HEADER_CACHE_CONTROL("Cache-Control");
const std::string HTTP_OUT_HEADER_CONNECTION("Connection");
const std::string HTTP_OUT_HEADER_CONTENT_DESCRIPTION("Content-Description");
const std::string HTTP_OUT_HEADER_CONTENT_ENCODING("Content-Encoding");
const std::string HTTP_OUT_HEADER_CONTENT_ID("Content-ID");
const std::string HTTP_OUT_HEADER_CONTENT_LANGUAGE("Content-Language");
const std::string HTTP_OUT_HEADER_CONTENT_LENGTH("Content-Length");
const std::string HTTP_OUT_HEADER_CONTENT_LOCATION("Content-Location");
const std::string HTTP_OUT_HEADER_CONTENT_MD5("Content-MD5");
const std::string HTTP_OUT_HEADER_CONTENT_RANGE("Content-Range");
const std::string HTTP_OUT_HEADER_CONTENT_TRANSFER_ENCODING("Content-Transfer-Encoding");
const std::string HTTP_OUT_HEADER_CONTENT_TYPE("Content-Type");
const std::string HTTP_OUT_HEADER_COOKIE("Cookie");
const std::string HTTP_OUT_HEADER_DATE("Date");
const std::string HTTP_OUT_HEADER_DESTINATION("Destination");
const std::string HTTP_OUT_HEADER_ETAG("ETag");
const std::string HTTP_OUT_HEADER_EXPECT("Expect");
const std::string HTTP_OUT_HEADER_EXPIRES("Expires");
const std::string HTTP_OUT_HEADER_FROM("From");
const std::string HTTP_OUT_HEADER_HOST("Host");
const std::string HTTP_OUT_HEADER_IF_MATCH("If-Match");
const std::string HTTP_OUT_HEADER_IF_MODIFIED_SINCE("If-Modified-Since");
const std::string HTTP_OUT_HEADER_IF_NONE_MATCH("If-None-Match");
const std::string HTTP_OUT_HEADER_IF_RANGE("If-Range");
const std::string HTTP_OUT_HEADER_IF_UNMODIFIED_SINCE("If-Unmodified-Since");
const std::string HTTP_OUT_HEADER_KEEP_ALIVE("Keep-Alive");
const std::string HTTP_OUT_HEADER_LAST_MODIFIED("Last-Modified");
const std::string HTTP_OUT_HEADER_LOCATION("Location");
const std::string HTTP_OUT_HEADER_MAX_FORWARDS("Max-Forwards");
const std::string HTTP_OUT_HEADER_MIME_VERSION("MIME-Version");
const std::string HTTP_OUT_HEADER_PRAGMA("Pragma");
const std::string HTTP_OUT_HEADER_PROXY_AUTHENTICATE("Proxy-Authenticate");
const std::string HTTP_OUT_HEADER_PROXY_AUTHORIZATION("Proxy-Authorization");
const std::string HTTP_OUT_HEADER_RANGE("Range");
const std::string HTTP_OUT_HEADER_REFERER("Referer");
const std::string HTTP_OUT_HEADER_RETRY_AFTER("Retry-After");
const std::string HTTP_OUT_HEADER_SERVER("Server");
const std::string HTTP_OUT_HEADER_SET_COOKIE("Set-Cookie");
const std::string HTTP_OUT_HEADER_TE("TE");
const std::string HTTP_OUT_HEADER_TRAILER("Trailer");
const std::string HTTP_OUT_HEADER_TRANSFER_ENCODING("Transfer-Encoding");
const std::string HTTP_OUT_HEADER_UPGRADE("Upgrade");
const std::string HTTP_OUT_HEADER_USER_AGENT("User-Agent");
const std::string HTTP_OUT_HEADER_VARY("Vary");
const std::string HTTP_OUT_HEADER_VIA("Via");
const std::string HTTP_OUT_HEADER_WARNING("Warning");
const std::string HTTP_OUT_HEADER_WWW_AUTHENTICATE("WWW-Authenticate");
// Incoming headers are normalized to lower-case.
const std::string HTTP_IN_HEADER_ACCEPT_LANGUAGE("accept-language");
const std::string HTTP_IN_HEADER_CACHE_CONTROL("cache-control");
const std::string HTTP_IN_HEADER_CONTENT_LENGTH("content-length");
const std::string HTTP_IN_HEADER_CONTENT_LOCATION("content-location");
const std::string HTTP_IN_HEADER_CONTENT_TYPE("content-type");
const std::string HTTP_IN_HEADER_HOST("host");
const std::string HTTP_IN_HEADER_LOCATION("location");
const std::string HTTP_IN_HEADER_RETRY_AFTER("retry-after");
const std::string HTTP_IN_HEADER_SET_COOKIE("set-cookie");
const std::string HTTP_IN_HEADER_USER_AGENT("user-agent");
const std::string HTTP_IN_HEADER_X_FORWARDED_FOR("x-forwarded-for");
const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml");
const std::string HTTP_CONTENT_OCTET_STREAM("application/octet-stream");
const std::string HTTP_CONTENT_VND_LL_MESH("application/vnd.ll.mesh");
const std::string HTTP_CONTENT_XML("application/xml");
const std::string HTTP_CONTENT_JSON("application/json");
const std::string HTTP_CONTENT_TEXT_HTML("text/html");
const std::string HTTP_CONTENT_TEXT_HTML_UTF8("text/html; charset=utf-8");
const std::string HTTP_CONTENT_TEXT_PLAIN_UTF8("text/plain; charset=utf-8");
const std::string HTTP_CONTENT_TEXT_LLSD("text/llsd");
const std::string HTTP_CONTENT_TEXT_XML("text/xml");
const std::string HTTP_CONTENT_TEXT_LSL("text/lsl");
const std::string HTTP_CONTENT_TEXT_PLAIN("text/plain");
const std::string HTTP_CONTENT_IMAGE_X_J2C("image/x-j2c");
const std::string HTTP_CONTENT_IMAGE_J2C("image/j2c");
const std::string HTTP_CONTENT_IMAGE_JPEG("image/jpeg");
const std::string HTTP_CONTENT_IMAGE_PNG("image/png");
const std::string HTTP_CONTENT_IMAGE_BMP("image/bmp");
const std::string HTTP_NO_CACHE("no-cache");
const std::string HTTP_NO_CACHE_CONTROL("no-cache, max-age=0");
const std::string HTTP_VERB_INVALID("(invalid)");
const std::string HTTP_VERB_HEAD("HEAD");
const std::string HTTP_VERB_GET("GET");
const std::string HTTP_VERB_PUT("PUT");
const std::string HTTP_VERB_POST("POST");
const std::string HTTP_VERB_DELETE("DELETE");
const std::string HTTP_VERB_MOVE("MOVE");
const std::string HTTP_VERB_OPTIONS("OPTIONS");
const std::string HTTP_VERB_PATCH("PATCH");
const std::string HTTP_VERB_COPY("COPY");
+219
View File
@@ -0,0 +1,219 @@
/**
* @file llhttpconstants.h
* @brief Constants for HTTP requests and responses
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2001-2014, 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 LL_HTTP_CONSTANTS_H
#define LL_HTTP_CONSTANTS_H
#include "stdtypes.h"
/////// HTTP STATUS CODES ///////
// Standard errors from HTTP spec:
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
const S32 HTTP_CONTINUE = 100;
const S32 HTTP_SWITCHING_PROTOCOLS = 101;
// Success
const S32 HTTP_OK = 200;
const S32 HTTP_CREATED = 201;
const S32 HTTP_ACCEPTED = 202;
const S32 HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
const S32 HTTP_NO_CONTENT = 204;
const S32 HTTP_RESET_CONTENT = 205;
const S32 HTTP_PARTIAL_CONTENT = 206;
// Redirection
const S32 HTTP_MULTIPLE_CHOICES = 300;
const S32 HTTP_MOVED_PERMANENTLY = 301;
const S32 HTTP_FOUND = 302;
const S32 HTTP_SEE_OTHER = 303;
const S32 HTTP_NOT_MODIFIED = 304;
const S32 HTTP_USE_PROXY = 305;
const S32 HTTP_TEMPORARY_REDIRECT = 307;
// Client Error
const S32 HTTP_BAD_REQUEST = 400;
const S32 HTTP_UNAUTHORIZED = 401;
const S32 HTTP_PAYMENT_REQUIRED = 402;
const S32 HTTP_FORBIDDEN = 403;
const S32 HTTP_NOT_FOUND = 404;
const S32 HTTP_METHOD_NOT_ALLOWED = 405;
const S32 HTTP_NOT_ACCEPTABLE = 406;
const S32 HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
const S32 HTTP_REQUEST_TIME_OUT = 408;
const S32 HTTP_CONFLICT = 409;
const S32 HTTP_GONE = 410;
const S32 HTTP_LENGTH_REQUIRED = 411;
const S32 HTTP_PRECONDITION_FAILED = 412;
const S32 HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
const S32 HTTP_REQUEST_URI_TOO_LARGE = 414;
const S32 HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
const S32 HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
const S32 HTTP_EXPECTATION_FAILED = 417;
// Server Error
const S32 HTTP_INTERNAL_SERVER_ERROR = 500;
const S32 HTTP_NOT_IMPLEMENTED = 501;
const S32 HTTP_BAD_GATEWAY = 502;
const S32 HTTP_SERVICE_UNAVAILABLE = 503;
const S32 HTTP_GATEWAY_TIME_OUT = 504;
const S32 HTTP_VERSION_NOT_SUPPORTED = 505;
// We combine internal process errors with status codes
// These status codes should not be sent over the wire
// and indicate something went wrong internally.
// If you get these they are not normal.
const S32 HTTP_INTERNAL_CURL_ERROR = 498;
const S32 HTTP_INTERNAL_ERROR = 499;
////// HTTP Methods //////
extern const std::string HTTP_VERB_INVALID;
extern const std::string HTTP_VERB_HEAD;
extern const std::string HTTP_VERB_GET;
extern const std::string HTTP_VERB_PUT;
extern const std::string HTTP_VERB_POST;
extern const std::string HTTP_VERB_DELETE;
extern const std::string HTTP_VERB_MOVE;
extern const std::string HTTP_VERB_OPTIONS;
enum EHTTPMethod
{
HTTP_INVALID = 0,
HTTP_HEAD,
HTTP_GET,
HTTP_PUT,
HTTP_POST,
HTTP_DELETE,
HTTP_MOVE, // Caller will need to set 'Destination' header
HTTP_OPTIONS,
HTTP_PATCH,
HTTP_COPY,
HTTP_METHOD_COUNT
};
// Parses 'Retry-After' header contents and returns seconds until retry should occur.
bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait);
//// HTTP Headers /////
// Outgoing headers. Do *not* use these to check incoming headers.
// For incoming headers, use the lower-case headers, below.
extern const std::string HTTP_OUT_HEADER_ACCEPT;
extern const std::string HTTP_OUT_HEADER_ACCEPT_CHARSET;
extern const std::string HTTP_OUT_HEADER_ACCEPT_ENCODING;
extern const std::string HTTP_OUT_HEADER_ACCEPT_LANGUAGE;
extern const std::string HTTP_OUT_HEADER_ACCEPT_RANGES;
extern const std::string HTTP_OUT_HEADER_AGE;
extern const std::string HTTP_OUT_HEADER_ALLOW;
extern const std::string HTTP_OUT_HEADER_AUTHORIZATION;
extern const std::string HTTP_OUT_HEADER_CACHE_CONTROL;
extern const std::string HTTP_OUT_HEADER_CONNECTION;
extern const std::string HTTP_OUT_HEADER_CONTENT_DESCRIPTION;
extern const std::string HTTP_OUT_HEADER_CONTENT_ENCODING;
extern const std::string HTTP_OUT_HEADER_CONTENT_ID;
extern const std::string HTTP_OUT_HEADER_CONTENT_LANGUAGE;
extern const std::string HTTP_OUT_HEADER_CONTENT_LENGTH;
extern const std::string HTTP_OUT_HEADER_CONTENT_LOCATION;
extern const std::string HTTP_OUT_HEADER_CONTENT_MD5;
extern const std::string HTTP_OUT_HEADER_CONTENT_RANGE;
extern const std::string HTTP_OUT_HEADER_CONTENT_TRANSFER_ENCODING;
extern const std::string HTTP_OUT_HEADER_CONTENT_TYPE;
extern const std::string HTTP_OUT_HEADER_COOKIE;
extern const std::string HTTP_OUT_HEADER_DATE;
extern const std::string HTTP_OUT_HEADER_DESTINATION;
extern const std::string HTTP_OUT_HEADER_ETAG;
extern const std::string HTTP_OUT_HEADER_EXPECT;
extern const std::string HTTP_OUT_HEADER_EXPIRES;
extern const std::string HTTP_OUT_HEADER_FROM;
extern const std::string HTTP_OUT_HEADER_HOST;
extern const std::string HTTP_OUT_HEADER_IF_MATCH;
extern const std::string HTTP_OUT_HEADER_IF_MODIFIED_SINCE;
extern const std::string HTTP_OUT_HEADER_IF_NONE_MATCH;
extern const std::string HTTP_OUT_HEADER_IF_RANGE;
extern const std::string HTTP_OUT_HEADER_IF_UNMODIFIED_SINCE;
extern const std::string HTTP_OUT_HEADER_KEEP_ALIVE;
extern const std::string HTTP_OUT_HEADER_LAST_MODIFIED;
extern const std::string HTTP_OUT_HEADER_LOCATION;
extern const std::string HTTP_OUT_HEADER_MAX_FORWARDS;
extern const std::string HTTP_OUT_HEADER_MIME_VERSION;
extern const std::string HTTP_OUT_HEADER_PRAGMA;
extern const std::string HTTP_OUT_HEADER_PROXY_AUTHENTICATE;
extern const std::string HTTP_OUT_HEADER_PROXY_AUTHORIZATION;
extern const std::string HTTP_OUT_HEADER_RANGE;
extern const std::string HTTP_OUT_HEADER_REFERER;
extern const std::string HTTP_OUT_HEADER_RETRY_AFTER;
extern const std::string HTTP_OUT_HEADER_SERVER;
extern const std::string HTTP_OUT_HEADER_SET_COOKIE;
extern const std::string HTTP_OUT_HEADER_TE;
extern const std::string HTTP_OUT_HEADER_TRAILER;
extern const std::string HTTP_OUT_HEADER_TRANSFER_ENCODING;
extern const std::string HTTP_OUT_HEADER_UPGRADE;
extern const std::string HTTP_OUT_HEADER_USER_AGENT;
extern const std::string HTTP_OUT_HEADER_VARY;
extern const std::string HTTP_OUT_HEADER_VIA;
extern const std::string HTTP_OUT_HEADER_WARNING;
extern const std::string HTTP_OUT_HEADER_WWW_AUTHENTICATE;
// Incoming headers are normalized to lower-case.
extern const std::string HTTP_IN_HEADER_ACCEPT_LANGUAGE;
extern const std::string HTTP_IN_HEADER_CACHE_CONTROL;
extern const std::string HTTP_IN_HEADER_CONTENT_LENGTH;
extern const std::string HTTP_IN_HEADER_CONTENT_LOCATION;
extern const std::string HTTP_IN_HEADER_CONTENT_TYPE;
extern const std::string HTTP_IN_HEADER_HOST;
extern const std::string HTTP_IN_HEADER_LOCATION;
extern const std::string HTTP_IN_HEADER_RETRY_AFTER;
extern const std::string HTTP_IN_HEADER_SET_COOKIE;
extern const std::string HTTP_IN_HEADER_USER_AGENT;
extern const std::string HTTP_IN_HEADER_X_FORWARDED_FOR;
//// HTTP Content Types ////
extern const std::string HTTP_CONTENT_LLSD_XML;
extern const std::string HTTP_CONTENT_OCTET_STREAM;
extern const std::string HTTP_CONTENT_VND_LL_MESH;
extern const std::string HTTP_CONTENT_XML;
extern const std::string HTTP_CONTENT_JSON;
extern const std::string HTTP_CONTENT_TEXT_HTML;
extern const std::string HTTP_CONTENT_TEXT_HTML_UTF8;
extern const std::string HTTP_CONTENT_TEXT_PLAIN_UTF8;
extern const std::string HTTP_CONTENT_TEXT_LLSD;
extern const std::string HTTP_CONTENT_TEXT_XML;
extern const std::string HTTP_CONTENT_TEXT_LSL;
extern const std::string HTTP_CONTENT_TEXT_PLAIN;
extern const std::string HTTP_CONTENT_IMAGE_X_J2C;
extern const std::string HTTP_CONTENT_IMAGE_J2C;
extern const std::string HTTP_CONTENT_IMAGE_JPEG;
extern const std::string HTTP_CONTENT_IMAGE_PNG;
extern const std::string HTTP_CONTENT_IMAGE_BMP;
//// HTTP Cache Settings ////
extern const std::string HTTP_NO_CACHE;
extern const std::string HTTP_NO_CACHE_CONTROL;
#endif
+179
View File
@@ -0,0 +1,179 @@
/**
* @file llcorehttp_test
* @brief Main test runner
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llcorehttp_test.h"
#include <iostream>
#include <sstream>
// These are not the right way in viewer for some reason:
// #include <tut/tut.hpp>
// #include <tut/tut_reporter.hpp>
// This works:
#include "../test/lltut.h"
// Pull in each of the test sets
#include "test_bufferarray.hpp"
#include "test_bufferstream.hpp"
#include "test_httpstatus.hpp"
#include "test_refcounted.hpp"
#include "test_httpoperation.hpp"
// As of 2019-06-28, test_httprequest.hpp consistently crashes on Mac Release
// builds for reasons not yet diagnosed.
#if ! (LL_DARWIN && LL_RELEASE)
#include "test_httprequest.hpp"
#endif
#include "test_httpheaders.hpp"
#include "test_httprequestqueue.hpp"
#include "_httpservice.h"
#include "llproxy.h"
#include "llcleanup.h"
void ssl_thread_id_callback(CRYPTO_THREADID*);
void ssl_locking_callback(int mode, int type, const char * file, int line);
#if 0 // lltut provides main and runner
namespace tut
{
test_runner_singleton runner;
}
int main()
{
curl_global_init(CURL_GLOBAL_ALL);
// *FIXME: Need threaded/SSL curl setup here.
tut::reporter reporter;
tut::runner.get().set_callback(&reporter);
tut::runner.get().run_tests();
return !reporter.all_ok();
curl_global_cleanup();
}
#endif // 0
int ssl_mutex_count(0);
LLCoreInt::HttpMutex ** ssl_mutex_list = NULL;
void init_curl()
{
curl_global_init(CURL_GLOBAL_ALL);
ssl_mutex_count = CRYPTO_num_locks();
if (ssl_mutex_count > 0)
{
ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count];
for (int i(0); i < ssl_mutex_count; ++i)
{
ssl_mutex_list[i] = new LLCoreInt::HttpMutex;
}
CRYPTO_set_locking_callback(ssl_locking_callback);
CRYPTO_THREADID_set_callback(ssl_thread_id_callback);
}
LLProxy::getInstance();
}
void term_curl()
{
SUBSYSTEM_CLEANUP(LLProxy);
CRYPTO_set_locking_callback(NULL);
for (int i(0); i < ssl_mutex_count; ++i)
{
delete ssl_mutex_list[i];
}
delete [] ssl_mutex_list;
}
void ssl_thread_id_callback(CRYPTO_THREADID* pthreadid)
{
#if defined(WIN32)
CRYPTO_THREADID_set_pointer(pthreadid, GetCurrentThread());
#else
CRYPTO_THREADID_set_pointer(pthreadid, pthread_self());
#endif
}
void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */)
{
if (type >= 0 && type < ssl_mutex_count)
{
if (mode & CRYPTO_LOCK)
{
ssl_mutex_list[type]->lock();
}
else
{
ssl_mutex_list[type]->unlock();
}
}
}
std::string get_base_url()
{
const char * env(getenv("LL_TEST_PORT"));
if (! env)
{
std::cerr << "LL_TEST_PORT environment variable missing." << std::endl;
std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl;
tut::ensure("LL_TEST_PORT set in environment", NULL != env);
}
int port(atoi(env));
std::ostringstream out;
out << "http://localhost:" << port << "/";
return out.str();
}
void stop_thread(LLCore::HttpRequest * req)
{
if (req)
{
req->requestStopThread(LLCore::HttpHandler::ptr_t());
int count = 0;
int limit = 10;
while (count++ < limit && ! HttpService::isStopped())
{
req->update(1000);
usleep(100000);
}
}
}
+64
View File
@@ -0,0 +1,64 @@
/**
* @file llcorehttp_test.h
* @brief Main test runner
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef _LLCOREHTTP_TEST_H_
#define _LLCOREHTTP_TEST_H_
#include "linden_common.h" // Modifies curl interfaces
#include <curl/curl.h>
#include <openssl/crypto.h>
#include <string>
#include "httprequest.h"
// Initialization and cleanup for libcurl. Mainly provides
// a mutex callback for SSL and a thread ID hash for libcurl.
// If you don't use these (or equivalent) and do use libcurl,
// you'll see stalls and other anomalies when performing curl
// operations.
extern void init_curl();
extern void term_curl();
extern std::string get_base_url();
extern void stop_thread(LLCore::HttpRequest * req);
class ScopedCurlInit
{
public:
ScopedCurlInit()
{
init_curl();
}
~ScopedCurlInit()
{
term_curl();
}
};
#endif // _LLCOREHTTP_TEST_H_
+174
View File
@@ -0,0 +1,174 @@
/**
* @file test_allocator.cpp
* @brief quick and dirty allocator for tracking memory allocations
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "test_allocator.h"
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
#include <libkern/OSAtomic.h>
#elif defined(_MSC_VER)
#include <Windows.h>
#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100
// atomic extensions are built into GCC on posix platforms
#endif
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iostream>
#include <new>
#include <boost/thread.hpp>
struct BlockHeader
{
struct Block * next;
std::size_t size;
bool in_use;
};
struct Block
{
BlockHeader hdr;
unsigned char data[1];
};
#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl;
static unsigned char MemBuf[ 4096 * 1024 ];
Block * pNext = static_cast<Block *>(static_cast<void *>(MemBuf));
volatile std::size_t MemTotal = 0;
// cross-platform compare and swap operation
static bool CAS(void * volatile * ptr, void * expected, void * new_value)
{
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
return OSAtomicCompareAndSwapPtr( expected, new_value, ptr );
#elif defined(_MSC_VER)
return expected == InterlockedCompareExchangePointer( ptr, new_value, expected );
#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100
return __sync_bool_compare_and_swap( ptr, expected, new_value );
#endif
}
static void * GetMem(std::size_t size)
{
// TRACE_MSG(size);
volatile Block * pBlock = NULL;
volatile Block * pNewNext = NULL;
// do a lock-free update of the global next pointer
do
{
pBlock = pNext;
pNewNext = (volatile Block *)(pBlock->data + size);
} while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext));
// if we get here, we safely carved out a block of memory in the
// memory pool...
// initialize our block
pBlock->hdr.next = (Block *)(pBlock->data + size);
pBlock->hdr.size = size;
pBlock->hdr.in_use = true;
memset((void *) pBlock->data, 0, pBlock->hdr.size);
// do a lock-free update of the global memory total
volatile size_t total = 0;
volatile size_t new_total = 0;
do
{
total = MemTotal;
new_total = total + size;
} while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total));
return (void *) pBlock->data;
}
static void FreeMem(void * p)
{
// get the pointer to the block record
Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader));
// TRACE_MSG(pBlock->hdr.size);
bool * cur_in_use = &(pBlock->hdr.in_use);
volatile bool in_use = false;
bool new_in_use = false;
do
{
in_use = pBlock->hdr.in_use;
} while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use));
// do a lock-free update of the global memory total
volatile size_t total = 0;
volatile size_t new_total = 0;
do
{
total = MemTotal;
new_total = total - pBlock->hdr.size;
} while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total));
}
std::size_t GetMemTotal()
{
return MemTotal;
}
void * operator new(std::size_t size) //throw(std::bad_alloc)
{
return GetMem( size );
}
void * operator new[](std::size_t size) //throw(std::bad_alloc)
{
return GetMem( size );
}
void operator delete(void * p) throw()
{
if (p)
{
FreeMem( p );
}
}
void operator delete[](void * p) throw()
{
if (p)
{
FreeMem( p );
}
}
+42
View File
@@ -0,0 +1,42 @@
/**
* @file test_allocator.h
* @brief quick and dirty allocator for tracking memory allocations
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef TEST_ALLOCATOR_H
#define TEST_ALLOCATOR_H
#include <cstdlib>
#include <new>
#error 2019-06-27 Do not use test_allocator.h -- does not respect alignment.
size_t GetMemTotal();
void * operator new(std::size_t size); //throw (std::bad_alloc);
void * operator new[](std::size_t size); //throw (std::bad_alloc);
void operator delete(void * p) throw ();
void operator delete[](void * p) throw ();
#endif // TEST_ALLOCATOR_H
+380
View File
@@ -0,0 +1,380 @@
/**
* @file test_bufferarray.hpp
* @brief unit tests for the LLCore::BufferArray class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef TEST_LLCORE_BUFFER_ARRAY_H_
#define TEST_LLCORE_BUFFER_ARRAY_H_
#include "bufferarray.h"
#include <iostream>
using namespace LLCore;
namespace tut
{
struct BufferArrayTestData
{
// the test objects inherit from this so the member functions and variables
// can be referenced directly inside of the test functions.
};
typedef test_group<BufferArrayTestData> BufferArrayTestGroupType;
typedef BufferArrayTestGroupType::object BufferArrayTestObjectType;
BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests");
template <> template <>
void BufferArrayTestObjectType::test<1>()
{
set_test_name("BufferArray construction");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
ensure("One ref on construction of BufferArray", ba->getRefCount() == 1);
ensure("Nothing in BA", 0 == ba->size());
// Try to read
char buffer[20];
size_t read_len(ba->read(0, buffer, sizeof(buffer)));
ensure("Read returns empty", 0 == read_len);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<2>()
{
set_test_name("BufferArray single write");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
char buffer[256];
size_t len = ba->write(0, str1, strlen(str1));
ensure("Wrote length correct", strlen(str1) == len);
ensure("Recorded size correct", strlen(str1) == ba->size());
// read some data back
memset(buffer, 'X', sizeof(buffer));
len = ba->read(2, buffer, 2);
ensure("Read length correct", 2 == len);
ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]);
ensure("Read didn't overwrite", 'X' == buffer[2]);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<3>()
{
set_test_name("BufferArray multiple writes");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
size_t str1_len(strlen(str1));
char buffer[256];
size_t len = ba->write(0, str1, str1_len);
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", str1_len == ba->size());
// again...
len = ba->write(str1_len, str1, strlen(str1));
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", (2 * str1_len) == ba->size());
// read some data back
memset(buffer, 'X', sizeof(buffer));
len = ba->read(8, buffer, 4);
ensure("Read length correct", 4 == len);
ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]);
ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]);
ensure("Read didn't overwrite", 'X' == buffer[4]);
// Read whole thing
memset(buffer, 'X', sizeof(buffer));
len = ba->read(0, buffer, sizeof(buffer));
ensure("Read length correct", (2 * str1_len) == len);
ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len));
ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len));
ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<4>()
{
set_test_name("BufferArray overwriting");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
size_t str1_len(strlen(str1));
char str2[] = "ABCDEFGHIJ";
char buffer[256];
size_t len = ba->write(0, str1, str1_len);
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", str1_len == ba->size());
// again...
len = ba->write(str1_len, str1, strlen(str1));
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", (2 * str1_len) == ba->size());
// reposition and overwrite
len = ba->write(8, str2, 4);
ensure("Overwrite length correct", 4 == len);
// Leave position and read verifying content (stale really from seek() days)
memset(buffer, 'X', sizeof(buffer));
len = ba->read(12, buffer, 4);
ensure("Read length correct", 4 == len);
ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]);
ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]);
ensure("Read didn't overwrite", 'X' == buffer[4]);
// reposition and check
len = ba->read(6, buffer, 8);
ensure("Read length correct.2", 8 == len);
ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]);
ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]);
ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]);
ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]);
ensure("Read didn't overwrite.7", 'X' == buffer[8]);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<5>()
{
set_test_name("BufferArray multiple writes - sequential reads");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
size_t str1_len(strlen(str1));
char buffer[256];
size_t len = ba->write(0, str1, str1_len);
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", str1_len == ba->size());
// again...
len = ba->write(str1_len, str1, str1_len);
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", (2 * str1_len) == ba->size());
// read some data back
memset(buffer, 'X', sizeof(buffer));
len = ba->read(8, buffer, 4);
ensure("Read length correct", 4 == len);
ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]);
ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]);
ensure("Read didn't overwrite", 'X' == buffer[4]);
// Read some more without repositioning
memset(buffer, 'X', sizeof(buffer));
len = ba->read(12, buffer, sizeof(buffer));
ensure("Read length correct", (str1_len - 2) == len);
ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2));
ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<6>()
{
set_test_name("BufferArray overwrite spanning blocks and appending");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
size_t str1_len(strlen(str1));
char str2[] = "ABCDEFGHIJKLMNOPQRST";
size_t str2_len(strlen(str2));
char buffer[256];
size_t len = ba->write(0, str1, str1_len);
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", str1_len == ba->size());
// again...
len = ba->write(str1_len, str1, strlen(str1));
ensure("Wrote length correct", str1_len == len);
ensure("Recorded size correct", (2 * str1_len) == ba->size());
// reposition and overwrite
len = ba->write(8, str2, str2_len);
ensure("Overwrite length correct", str2_len == len);
// Leave position and read verifying content
memset(buffer, 'X', sizeof(buffer));
len = ba->read(8 + str2_len, buffer, 0);
ensure("Read length correct", 0 == len);
ensure("Read didn't overwrite", 'X' == buffer[0]);
// reposition and check
len = ba->read(0, buffer, sizeof(buffer));
ensure("Read length correct.2", (str1_len + str2_len - 2) == len);
ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2));
ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len));
ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<7>()
{
set_test_name("BufferArray overwrite spanning blocks and sequential writes");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
size_t str1_len(strlen(str1));
char str2[] = "ABCDEFGHIJKLMNOPQRST";
size_t str2_len(strlen(str2));
char buffer[256];
// 2x str1
size_t len = ba->write(0, str1, str1_len);
len = ba->write(str1_len, str1, str1_len);
// reposition and overwrite
len = ba->write(6, str2, 2);
ensure("Overwrite length correct", 2 == len);
len = ba->write(8, str2, 2);
ensure("Overwrite length correct.2", 2 == len);
len = ba->write(10, str2, 2);
ensure("Overwrite length correct.3", 2 == len);
// append some data
len = ba->append(str2, str2_len);
ensure("Append length correct", str2_len == len);
// append some more
void * out_buf(ba->appendBufferAlloc(str1_len));
memcpy(out_buf, str1, str1_len);
// And some final writes
len = ba->write(3 * str1_len + str2_len, str2, 2);
ensure("Write length correct.2", 2 == len);
// Check contents
memset(buffer, 'X', sizeof(buffer));
len = ba->read(0, buffer, sizeof(buffer));
ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len);
ensure("Read content correct", 0 == strncmp(buffer, str1, 6));
ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2));
ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2));
ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2));
ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2));
ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len));
ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len));
ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2));
ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]);
// release the implicit reference, causing the object to be released
ba->release();
}
template <> template <>
void BufferArrayTestObjectType::test<8>()
{
set_test_name("BufferArray zero-length appendBufferAlloc");
// create a new ref counted object with an implicit reference
BufferArray * ba = new BufferArray();
// write some data to the buffer
char str1[] = "abcdefghij";
size_t str1_len(strlen(str1));
char str2[] = "ABCDEFGHIJKLMNOPQRST";
size_t str2_len(strlen(str2));
char buffer[256];
// 2x str1
size_t len = ba->write(0, str1, str1_len);
len = ba->write(str1_len, str1, str1_len);
// zero-length allocate (we allow this with a valid pointer returned)
void * out_buf(ba->appendBufferAlloc(0));
ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf);
// Do it again
void * out_buf2(ba->appendBufferAlloc(0));
ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2);
ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2);
// And some final writes
len = ba->write(2 * str1_len, str2, str2_len);
// Check contents
memset(buffer, 'X', sizeof(buffer));
len = ba->read(0, buffer, sizeof(buffer));
ensure("Final buffer length correct", (2 * str1_len + str2_len) == len);
ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len));
ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len));
ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len));
ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]);
// release the implicit reference, causing the object to be released
ba->release();
}
} // end namespace tut
#endif // TEST_LLCORE_BUFFER_ARRAY_H_
@@ -0,0 +1,252 @@
/**
* @file test_bufferstream.hpp
* @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef TEST_LLCORE_BUFFER_STREAM_H_
#define TEST_LLCORE_BUFFER_STREAM_H_
#include "bufferstream.h"
#include <iostream>
#include "llsd.h"
#include "llsdserialize.h"
using namespace LLCore;
namespace tut
{
struct BufferStreamTestData
{
// the test objects inherit from this so the member functions and variables
// can be referenced directly inside of the test functions.
};
typedef test_group<BufferStreamTestData> BufferStreamTestGroupType;
typedef BufferStreamTestGroupType::object BufferStreamTestObjectType;
BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests");
typedef BufferArrayStreamBuf::traits_type tst_traits_t;
template <> template <>
void BufferStreamTestObjectType::test<1>()
{
set_test_name("BufferArrayStreamBuf construction with NULL BufferArray");
// create a new ref counted object with an implicit reference
BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL);
// Not much will work with a NULL
ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow());
ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow());
ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c'));
ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1);
ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c'));
ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0);
ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1));
// release the implicit reference, causing the object to be released
delete bsb;
bsb = NULL;
}
template <> template <>
void BufferStreamTestObjectType::test<2>()
{
set_test_name("BufferArrayStream construction with NULL BufferArray");
// create a new ref counted object with an implicit reference
BufferArrayStream * bas = new BufferArrayStream(NULL);
// Not much will work with a NULL here
ensure("eof() is false on NULL", ! bas->eof());
ensure("fail() is false on NULL", ! bas->fail());
ensure("good() on NULL", bas->good());
// release the implicit reference, causing the object to be released
delete bas;
bas = NULL;
}
template <> template <>
void BufferStreamTestObjectType::test<3>()
{
set_test_name("BufferArrayStreamBuf construction with empty BufferArray");
// create a new ref counted BufferArray with implicit reference
BufferArray * ba = new BufferArray;
BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba);
// I can release my ref on the BA
ba->release();
ba = NULL;
// release the implicit reference, causing the object to be released
delete bsb;
bsb = NULL;
}
template <> template <>
void BufferStreamTestObjectType::test<4>()
{
set_test_name("BufferArrayStream construction with empty BufferArray");
// create a new ref counted BufferArray with implicit reference
BufferArray * ba = new BufferArray;
{
// create a new ref counted object with an implicit reference
BufferArrayStream bas(ba);
}
// release the implicit reference, causing the object to be released
ba->release();
ba = NULL;
}
template <> template <>
void BufferStreamTestObjectType::test<5>()
{
set_test_name("BufferArrayStreamBuf construction with real BufferArray");
// create a new ref counted BufferArray with implicit reference
BufferArray * ba = new BufferArray;
const char * content("This is a string. A fragment.");
const size_t c_len(strlen(content));
ba->append(content, c_len);
// Creat an adapter for the BufferArray
BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba);
// I can release my ref on the BA
ba->release();
ba = NULL;
// Various static state
ensure("underflow() returns 'T'", bsb->underflow() == 'T');
ensure("underflow() returns 'T' again", bsb->underflow() == 'T');
ensure("uflow() returns 'T'", bsb->uflow() == 'T');
ensure("uflow() returns 'h'", bsb->uflow() == 'h');
ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i'));
ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T'));
ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h');
ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1));
ensure("overflow() appends", bsb->overflow('c') == 'c');
ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1));
ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4);
ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5));
ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0));
ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4));
ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4));
ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4));
ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!'));
// release the implicit reference, causing the object to be released
delete bsb;
bsb = NULL;
}
template <> template <>
void BufferStreamTestObjectType::test<6>()
{
set_test_name("BufferArrayStream construction with real BufferArray");
// create a new ref counted BufferArray with implicit reference
BufferArray * ba = new BufferArray;
//const char * content("This is a string. A fragment.");
//const size_t c_len(strlen(content));
//ba->append(content, strlen(content));
{
// Creat an adapter for the BufferArray
BufferArrayStream bas(ba);
// Basic operations
bas << "Hello" << 27 << ".";
ensure("BA length 8", ba->size() == 8);
std::string str;
bas >> str;
ensure("reads correctly", str == "Hello27.");
}
// release the implicit reference, causing the object to be released
ba->release();
ba = NULL;
}
template <> template <>
void BufferStreamTestObjectType::test<7>()
{
set_test_name("BufferArrayStream with LLSD serialization");
// create a new ref counted BufferArray with implicit reference
BufferArray * ba = new BufferArray;
{
// Creat an adapter for the BufferArray
BufferArrayStream bas(ba);
// LLSD
LLSD llsd = LLSD::emptyMap();
llsd["int"] = LLSD::Integer(3);
llsd["float"] = LLSD::Real(923289.28992);
llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj");
LLSD llsd_map = LLSD::emptyMap();
llsd_map["int"] = LLSD::Integer(-2889);
llsd_map["float"] = LLSD::Real(2.37829e32);
llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP");
llsd["map"] = llsd_map;
// Serialize it
LLSDSerialize::toXML(llsd, bas);
std::string str;
bas >> str;
// std::cout << "SERIALIZED LLSD: " << str << std::endl;
ensure("Extracted string has reasonable length", str.size() > 60);
}
// release the implicit reference, causing the object to be released
ba->release();
ba = NULL;
}
} // end namespace tut
#endif // TEST_LLCORE_BUFFER_STREAM_H_
+392
View File
@@ -0,0 +1,392 @@
/**
* @file test_httpheaders.hpp
* @brief unit tests for the LLCore::HttpHeaders 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_HEADERS_H_
#define TEST_LLCORE_HTTP_HEADERS_H_
#include "httpheaders.h"
#include <iostream>
using namespace LLCoreInt;
namespace tut
{
struct HttpHeadersTestData
{
// the test objects inherit from this so the member functions and variables
// can be referenced directly inside of the test functions.
};
typedef test_group<HttpHeadersTestData> HttpHeadersTestGroupType;
typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType;
HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests");
template <> template <>
void HttpHeadersTestObjectType::test<1>()
{
set_test_name("HttpHeaders construction");
// create a new ref counted object with an implicit reference
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
ensure("Nothing in headers", 0 == headers->size());
// release the implicit reference, causing the object to be released
headers.reset();
}
template <> template <>
void HttpHeadersTestObjectType::test<2>()
{
set_test_name("HttpHeaders construction");
// create a new ref counted object with an implicit reference
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
// Append a few strings
std::string str1n("Pragma");
std::string str1v("");
headers->append(str1n, str1v);
std::string str2n("Accept");
std::string str2v("application/json");
headers->append(str2n, str2v);
ensure("Headers retained", 2 == headers->size());
HttpHeaders::container_t & c(headers->getContainerTESTONLY());
ensure("First name is first name", c[0].first == str1n);
ensure("First value is first value", c[0].second == str1v);
ensure("Second name is second name", c[1].first == str2n);
ensure("Second value is second value", c[1].second == str2v);
}
// release the implicit reference, causing the object to be released
headers.reset();
}
template <> template <>
void HttpHeadersTestObjectType::test<3>()
{
set_test_name("HttpHeaders basic find");
// create a new ref counted object with an implicit reference
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
// Append a few strings
std::string str1n("Uno");
std::string str1v("1");
headers->append(str1n, str1v);
std::string str2n("doS");
std::string str2v("2-2-2-2");
headers->append(str2n, str2v);
std::string str3n("TRES");
std::string str3v("trois gymnopedie");
headers->append(str3n, str3v);
ensure("Headers retained", 3 == headers->size());
const std::string * result(NULL);
// Find a header
result = headers->find("TRES");
ensure("Found the last item", result != NULL);
ensure("Last item is a nice", result != NULL && str3v == *result);
// appends above are raw and find is case sensitive
result = headers->find("TReS");
ensure("Last item not found due to case", result == NULL);
result = headers->find("TRE");
ensure("Last item not found due to prefixing (1)", result == NULL);
result = headers->find("TRESS");
ensure("Last item not found due to prefixing (2)", result == NULL);
}
// release the implicit reference, causing the object to be released
headers.reset();
}
template <> template <>
void HttpHeadersTestObjectType::test<4>()
{
set_test_name("HttpHeaders normalized header entry");
// create a new ref counted object with an implicit reference
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
headers->appendNormal(line1, sizeof(line1) - 1);
ensure("First append worked in some fashion", 1 == headers->size());
const std::string * result(NULL);
// Find a header
result = headers->find("accept");
ensure("Found 'accept'", result != NULL);
ensure("accept value has face", result != NULL && *result == line1v);
// Left-clean on value
static char line2[] = " next : \t\tlinejunk \t";
headers->appendNormal(line2, sizeof(line2) - 1);
ensure("Second append worked", 2 == headers->size());
result = headers->find("next");
ensure("Found 'next'", result != NULL);
ensure("next value is left-clean", result != NULL &&
*result == "linejunk \t");
// First value unmolested
result = headers->find("accept");
ensure("Found 'accept' again", result != NULL);
ensure("accept value has face", result != NULL && *result == line1v);
// Colons in value are okay
static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
static char line3v[] = ":plop:-neuf-=vleem=";
headers->appendNormal(line3, sizeof(line3) - 1);
ensure("Third append worked", 3 == headers->size());
result = headers->find("fancy-pants");
ensure("Found 'fancy-pants'", result != NULL);
ensure("fancy-pants value has colons", result != NULL && *result == line3v);
// Zero-length value
static char line4[] = "all-talk-no-walk:";
headers->appendNormal(line4, sizeof(line4) - 1);
ensure("Fourth append worked", 4 == headers->size());
result = headers->find("all-talk-no-walk");
ensure("Found 'all-talk'", result != NULL);
ensure("al-talk value is zero-length", result != NULL && result->size() == 0);
// Zero-length name
static char line5[] = ":all-talk-no-walk";
static char line5v[] = "all-talk-no-walk";
headers->appendNormal(line5, sizeof(line5) - 1);
ensure("Fifth append worked", 5 == headers->size());
result = headers->find("");
ensure("Found no-name", result != NULL);
ensure("no-name value is something", result != NULL && *result == line5v);
// Lone colon is still something
headers->clear();
static char line6[] = " :";
headers->appendNormal(line6, sizeof(line6) - 1);
ensure("Sixth append worked", 1 == headers->size());
result = headers->find("");
ensure("Found 2nd no-name", result != NULL);
ensure("2nd no-name value is nothing", result != NULL && result->size() == 0);
// Line without colons is taken as-is and unstripped in name
static char line7[] = " \toskdgioasdghaosdghoowg28342908tg8902hg0hwedfhqew890v7qh0wdebv78q0wdevbhq>?M>BNM<ZV>?NZ? \t";
headers->appendNormal(line7, sizeof(line7) - 1);
ensure("Seventh append worked", 2 == headers->size());
result = headers->find(line7);
ensure("Found whatsit line", result != NULL);
ensure("Whatsit line has no value", result != NULL && result->size() == 0);
// Normaling interface heeds the byte count, doesn't look for NUL-terminator
static char line8[] = "binary:ignorestuffontheendofthis";
headers->appendNormal(line8, 13);
ensure("Eighth append worked", 3 == headers->size());
result = headers->find("binary");
ensure("Found 'binary'", result != NULL);
ensure("binary value was limited to 'ignore'", result != NULL &&
*result == "ignore");
}
// release the implicit reference, causing the object to be released
headers.reset();
}
// Verify forward iterator finds everything as expected
template <> template <>
void HttpHeadersTestObjectType::test<5>()
{
set_test_name("HttpHeaders iterator tests");
// create a new ref counted object with an implicit reference
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
HttpHeaders::iterator end(headers->end()), begin(headers->begin());
ensure("Empty container has equal begin/end const iterators", end == begin);
HttpHeaders::const_iterator cend(headers->end()), cbegin(headers->begin());
ensure("Empty container has equal rbegin/rend const iterators", cend == cbegin);
ensure("Empty container has equal begin/end iterators", headers->end() == headers->begin());
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
headers->appendNormal(line1, sizeof(line1) - 1);
static char line2[] = " next : \t\tlinejunk \t";
static char line2v[] = "linejunk \t";
headers->appendNormal(line2, sizeof(line2) - 1);
static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
static char line3v[] = ":plop:-neuf-=vleem=";
headers->appendNormal(line3, sizeof(line3) - 1);
static char line4[] = "all-talk-no-walk:";
static char line4v[] = "";
headers->appendNormal(line4, sizeof(line4) - 1);
static char line5[] = ":all-talk-no-walk";
static char line5v[] = "all-talk-no-walk";
headers->appendNormal(line5, sizeof(line5) - 1);
static char line6[] = " :";
static char line6v[] = "";
headers->appendNormal(line6, sizeof(line6) - 1);
ensure("All entries accounted for", 6 == headers->size());
static char * values[] = {
line1v,
line2v,
line3v,
line4v,
line5v,
line6v
};
int i(0);
HttpHeaders::const_iterator cend(headers->end());
for (HttpHeaders::const_iterator it(headers->begin());
cend != it;
++it, ++i)
{
std::ostringstream str;
str << "Const Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
// Rewind, do non-consts
i = 0;
HttpHeaders::iterator end(headers->end());
for (HttpHeaders::iterator it(headers->begin());
end != it;
++it, ++i)
{
std::ostringstream str;
str << "Const Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
}
// release the implicit reference, causing the object to be released
headers.reset();
}
// Reverse iterators find everything as expected
template <> template <>
void HttpHeadersTestObjectType::test<6>()
{
set_test_name("HttpHeaders reverse iterator tests");
// create a new ref counted object with an implicit reference
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
HttpHeaders::const_reverse_iterator crend(headers->rend()), crbegin(headers->rbegin());
ensure("Empty container has equal rbegin/rend const iterators", crend == crbegin);
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
headers->appendNormal(line1, sizeof(line1) - 1);
static char line2[] = " next : \t\tlinejunk \t";
static char line2v[] = "linejunk \t";
headers->appendNormal(line2, sizeof(line2) - 1);
static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
static char line3v[] = ":plop:-neuf-=vleem=";
headers->appendNormal(line3, sizeof(line3) - 1);
static char line4[] = "all-talk-no-walk:";
static char line4v[] = "";
headers->appendNormal(line4, sizeof(line4) - 1);
static char line5[] = ":all-talk-no-walk";
static char line5v[] = "all-talk-no-walk";
headers->appendNormal(line5, sizeof(line5) - 1);
static char line6[] = " :";
static char line6v[] = "";
headers->appendNormal(line6, sizeof(line6) - 1);
ensure("All entries accounted for", 6 == headers->size());
static char * values[] = {
line6v,
line5v,
line4v,
line3v,
line2v,
line1v
};
int i(0);
HttpHeaders::const_reverse_iterator cend(headers->rend());
for (HttpHeaders::const_reverse_iterator it(headers->rbegin());
cend != it;
++it, ++i)
{
std::ostringstream str;
str << "Const Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
// Rewind, do non-consts
i = 0;
HttpHeaders::reverse_iterator end(headers->rend());
for (HttpHeaders::reverse_iterator it(headers->rbegin());
end != it;
++it, ++i)
{
std::ostringstream str;
str << "Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
}
// release the implicit reference, causing the object to be released
headers.reset();
}
} // end namespace tut
#endif // TEST_LLCORE_HTTP_HEADERS_H_
@@ -0,0 +1,107 @@
/**
* @file test_httpoperation.hpp
* @brief unit tests for the LLCore::HttpOperation-derived classes
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef TEST_LLCORE_HTTP_OPERATION_H_
#define TEST_LLCORE_HTTP_OPERATION_H_
#include "_httpoperation.h"
#include "httphandler.h"
#include <iostream>
using namespace LLCoreInt;
namespace
{
class TestHandler : public LLCore::HttpHandler
{
public:
virtual void onCompleted(HttpHandle, HttpResponse *)
{
std::cout << "TestHandler::onCompleted() invoked" << std::endl;
}
};
} // end namespace anonymous
namespace tut
{
struct HttpOperationTestData
{
// the test objects inherit from this so the member functions and variables
// can be referenced directly inside of the test functions.
};
typedef test_group<HttpOperationTestData> HttpOperationTestGroupType;
typedef HttpOperationTestGroupType::object HttpOperationTestObjectType;
HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests");
template <> template <>
void HttpOperationTestObjectType::test<1>()
{
set_test_name("HttpOpNull construction");
// create a new ref counted object with an implicit reference
HttpOperation::ptr_t op (new HttpOpNull());
ensure(op.use_count() == 1);
// release the implicit reference, causing the object to be released
op.reset();
}
template <> template <>
void HttpOperationTestObjectType::test<2>()
{
set_test_name("HttpOpNull construction with handlers");
// Get some handlers
LLCore::HttpHandler::ptr_t h1 (new TestHandler());
// create a new ref counted object with an implicit reference
HttpOperation::ptr_t op (new HttpOpNull());
// Add the handlers
op->setReplyPath(LLCore::HttpOperation::HttpReplyQueuePtr_t(), h1);
// Check ref count
ensure(op.use_count() == 1);
// release the reference, releasing the operation but
// not the handlers.
op.reset();
// release the handlers
h1.reset();
}
}
#endif // TEST_LLCORE_HTTP_OPERATION_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
/**
* @file test_httprequestqueue.hpp
* @brief unit tests for the LLCore::HttpRequestQueue class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef TEST_LLCORE_HTTP_REQUESTQUEUE_H_
#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_
#include "_httprequestqueue.h"
#include <iostream>
#include "_httpoperation.h"
using namespace LLCoreInt;
namespace tut
{
struct HttpRequestqueueTestData
{
// the test objects inherit from this so the member functions and variables
// can be referenced directly inside of the test functions.
};
typedef test_group<HttpRequestqueueTestData> HttpRequestqueueTestGroupType;
typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType;
HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests");
template <> template <>
void HttpRequestqueueTestObjectType::test<1>()
{
set_test_name("HttpRequestQueue construction");
// create a new ref counted object with an implicit reference
HttpRequestQueue::init();
ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1);
// release the implicit reference, causing the object to be released
HttpRequestQueue::term();
}
template <> template <>
void HttpRequestqueueTestObjectType::test<2>()
{
set_test_name("HttpRequestQueue refcount works");
// create a new ref counted object with an implicit reference
HttpRequestQueue::init();
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
rq->addRef();
// release the singleton, hold on to the object
HttpRequestQueue::term();
ensure("One ref after term() called", rq->getRefCount() == 1);
// Drop ref
rq->release();
}
template <> template <>
void HttpRequestqueueTestObjectType::test<3>()
{
set_test_name("HttpRequestQueue addOp/fetchOp work");
// create a new ref counted object with an implicit reference
HttpRequestQueue::init();
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
HttpOperation::ptr_t op(new HttpOpNull());
rq->addOp(op); // transfer my refcount
op = rq->fetchOp(true); // Potentially hangs the test on failure
ensure("One goes in, one comes out", static_cast<bool>(op));
op.reset();
op = rq->fetchOp(false);
ensure("Better not be two of them", !op);
// release the singleton, hold on to the object
HttpRequestQueue::term();
}
template <> template <>
void HttpRequestqueueTestObjectType::test<4>()
{
set_test_name("HttpRequestQueue addOp/fetchAll work");
// create a new ref counted object with an implicit reference
HttpRequestQueue::init();
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
HttpOperation::ptr_t op (new HttpOpNull());
rq->addOp(op); // transfer my refcount
op.reset(new HttpOpNull());
rq->addOp(op); // transfer my refcount
op.reset(new HttpOpNull());
rq->addOp(op); // transfer my refcount
{
HttpRequestQueue::OpContainer ops;
rq->fetchAll(true, ops); // Potentially hangs the test on failure
ensure("Three go in, three come out", 3 == ops.size());
op = rq->fetchOp(false);
ensure("Better not be any more of them", !op);
op.reset();
// release the singleton, hold on to the object
HttpRequestQueue::term();
// Release them
ops.clear();
// while (! ops.empty())
// {
// HttpOperation * op = ops.front();
// ops.erase(ops.begin());
// op->release();
// }
}
}
} // end namespace tut
#endif // TEST_LLCORE_HTTP_REQUESTQUEUE_H_
+300
View File
@@ -0,0 +1,300 @@
/**
* @file test_llrefcounted
* @brief unit tests for HttpStatus struct
*
* $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_HTTP_STATUS_H_
#define TEST_HTTP_STATUS_H_
#include "httpcommon.h"
#include <curl/curl.h>
#include <curl/multi.h>
using namespace LLCore;
namespace tut
{
struct HttpStatusTestData
{
HttpStatusTestData()
{}
};
typedef test_group<HttpStatusTestData> HttpStatusTestGroupType;
typedef HttpStatusTestGroupType::object HttpStatusTestObjectType;
HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests");
template <> template <>
void HttpStatusTestObjectType::test<1>()
{
set_test_name("HttpStatus construction");
// auto allocation fine for this
HttpStatus status;
status = HttpStatus(HttpStatus::EXT_CURL_EASY, 0);
ensure(bool(status));
ensure(false == !(status));
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, 0);
ensure(bool(status));
ensure(false == !(status));
status = HttpStatus(HttpStatus::LLCORE, HE_SUCCESS);
ensure(bool(status));
ensure(false == !(status));
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, -1);
ensure(false == bool(status));
ensure(!(status));
status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_BAD_DOWNLOAD_RESUME);
ensure(false == bool(status));
ensure(!(status));
}
// template <> template <>
// void HttpStatusTestObjectType::test<2>()
// {
// set_test_name("HttpStatus memory structure");
//
// // Require that an HttpStatus object can be trivially
// // returned as a function return value in registers.
// // One should fit in an int on all platforms.
//
// //ensure(sizeof(HttpStatus) <= sizeof(int));
// }
template <> template <>
void HttpStatusTestObjectType::test<2>()
{
set_test_name("HttpStatus valid status string conversion");
HttpStatus status = HttpStatus(HttpStatus::EXT_CURL_EASY, 0);
std::string msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(msg.empty());
status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_BAD_FUNCTION_ARGUMENT);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status = HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
}
template <> template <>
void HttpStatusTestObjectType::test<3>()
{
set_test_name("HttpStatus invalid status string conversion");
HttpStatus status = HttpStatus(HttpStatus::EXT_CURL_EASY, 32726);
std::string msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, -470);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status = HttpStatus(HttpStatus::LLCORE, 923);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
}
template <> template <>
void HttpStatusTestObjectType::test<4>()
{
set_test_name("HttpStatus equality/inequality testing");
// Make certain equality/inequality tests do not pass
// through the bool conversion. Distinct successful
// and error statuses should compare unequal.
HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS);
HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS);
ensure(status1 != status2);
status1 = HttpStatus(HttpStatus::LLCORE, HE_REPLY_ERROR);
status1 = HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN);
ensure(status1 != status2);
}
template <> template <>
void HttpStatusTestObjectType::test<5>()
{
set_test_name("HttpStatus basic HTTP status encoding");
HttpStatus status;
status = HttpStatus(200, HE_SUCCESS);
std::string msg = status.toString();
ensure(msg.empty());
ensure(bool(status));
// Normally a success but application says error
status = HttpStatus(200, HE_REPLY_ERROR);
msg = status.toString();
ensure(! msg.empty());
ensure(! bool(status));
ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong
// Same statuses with distinct success/fail are distinct
status = HttpStatus(200, HE_SUCCESS);
HttpStatus status2(200, HE_REPLY_ERROR);
ensure(status != status2);
// Normally an error but application says okay
status = HttpStatus(406, HE_SUCCESS);
msg = status.toString();
ensure(msg.empty());
ensure(bool(status));
// Different statuses but both successful are distinct
status = HttpStatus(200, HE_SUCCESS);
status2 = HttpStatus(201, HE_SUCCESS);
ensure(status != status2);
// Different statuses but both failed are distinct
status = HttpStatus(200, HE_REPLY_ERROR);
status2 = HttpStatus(201, HE_REPLY_ERROR);
ensure(status != status2);
}
template <> template <>
void HttpStatusTestObjectType::test<6>()
{
set_test_name("HttpStatus HTTP status text strings");
HttpStatus status(100, HE_REPLY_ERROR);
std::string msg(status.toString());
ensure(! msg.empty()); // Should be something
ensure(msg == "Continue");
status = HttpStatus(200, HE_SUCCESS);
msg = status.toString();
ensure(msg.empty()); // Success is empty
status = HttpStatus(199, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "Unknown error");
status = HttpStatus(505, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "HTTP Version not supported");
status = HttpStatus(506, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "Unknown error");
status = HttpStatus(999, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "Unknown error");
}
template <> template <>
void HttpStatusTestObjectType::test<7>()
{
set_test_name("HttpStatus toHex() nominal function");
HttpStatus status(404);
std::string msg = status.toHex();
// std::cout << "Result: " << msg << std::endl;
ensure_equals(msg, "01940001");
}
template <> template <>
void HttpStatusTestObjectType::test<8>()
{
set_test_name("HttpStatus toTerseString() nominal function");
HttpStatus status(404);
std::string msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Normal HTTP 404", msg == "Http_404");
status = HttpStatus(200);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Normal HTTP 200", msg == "Http_200");
status = HttpStatus(200, HE_REPLY_ERROR);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Unsuccessful HTTP 200", msg == "Http_200"); // No distinction for error
status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Easy couldn't connect error", msg == "Easy_7");
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Multi out-of-memory error", msg == "Multi_3");
status = HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_SET);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Core option not set error", msg == "Core_7");
status = HttpStatus(22000, 1);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Undecodable error", msg == "Unknown_1");
status = HttpStatus(22000, -1);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Undecodable error 65535", msg == "Unknown_65535");
}
} // end namespace tut
#endif // TEST_HTTP_STATUS_H
+318
View File
@@ -0,0 +1,318 @@
#!/usr/bin/env python3
"""\
@file test_llsdmessage_peer.py
@author Nat Goodspeed
@date 2008-10-09
@brief This script asynchronously runs the executable (with args) specified on
the command line, returning its result code. While that executable is
running, we provide dummy local services for use by C++ tests.
$LicenseInfo:firstyear=2008&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$
"""
import os
import sys
import time
import select
import getopt
from io import StringIO
from http.server import HTTPServer, BaseHTTPRequestHandler
import llsd
# we're in llcorehttp/tests ; testrunner.py is found in llmessage/tests
sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
"llmessage", "tests"))
from testrunner import freeport, run, debug, VERBOSE
class TestHTTPRequestHandler(BaseHTTPRequestHandler):
"""This subclass of BaseHTTPRequestHandler is to receive and echo
LLSD-flavored messages sent by the C++ LLHTTPClient.
Target URLs are fairly free-form and are assembled by
concatinating fragments. Currently defined fragments
are:
- '/reflect/' Request headers are bounced back to caller
after prefixing with 'X-Reflect-'
- '/fail/' Body of request can contain LLSD with
'reason' string and 'status' integer
which will become response header.
- '/bug2295/' 206 response, no data in body:
-- '/bug2295/0/' "Content-Range: bytes 0-75/2983"
-- '/bug2295/1/' "Content-Range: bytes 0-75/*"
-- '/bug2295/2/' "Content-Range: bytes 0-75/2983",
"Content-Length: 0"
-- '/bug2295/00000018/0/' Generates PARTIAL_FILE (18) error in libcurl.
"Content-Range: bytes 0-75/2983",
"Content-Length: 76"
-- '/bug2295/inv_cont_range/0/' Generates HE_INVALID_CONTENT_RANGE error in llcorehttp.
- '/503/' Generate 503 responses with various kinds
of 'retry-after' headers
-- '/503/0/' "Retry-After: 2"
-- '/503/1/' "Retry-After: Thu, 31 Dec 2043 23:59:59 GMT"
-- '/503/2/' "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT"
-- '/503/3/' "Retry-After: "
-- '/503/4/' "Retry-After: (*#*(@*(@(")"
-- '/503/5/' "Retry-After: aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo"
-- '/503/6/' "Retry-After: 1 2 3 4 5 6 7 8 9 10"
Some combinations make no sense, there's no effort to protect
you from that.
"""
ignore_exceptions = (Exception,)
def read(self):
# The following logic is adapted from the library module
# SimpleXMLRPCServer.py.
# Get arguments by reading body of request.
# We read this in chunks to avoid straining
# socket.read(); around the 10 or 15Mb mark, some platforms
# begin to have problems (bug #792570).
try:
size_remaining = int(self.headers["content-length"])
except (KeyError, ValueError):
return ""
max_chunk_size = 10*1024*1024
L = bytes()
while size_remaining:
chunk_size = min(size_remaining, max_chunk_size)
chunk = self.rfile.read(chunk_size)
L += chunk
size_remaining -= len(chunk)
return L.decode("utf-8")
# end of swiped read() logic
def read_xml(self):
# This approach reads the entire POST data into memory first
return llsd.parse(self.read())
## # This approach attempts to stream in the LLSD XML from self.rfile,
## # assuming that the underlying XML parser reads its input file
## # incrementally. Unfortunately I haven't been able to make it work.
## tree = xml_parse(self.rfile)
## debug("Finished raw parse")
## debug("parsed XML tree %s", tree)
## debug("parsed root node %s", tree.getroot())
## debug("root node tag %s", tree.getroot().tag)
## return llsd.to_python(tree.getroot())
def do_HEAD(self):
self.do_GET(withdata=False)
def do_GET(self, withdata=True):
# Of course, don't attempt to read data.
try:
self.answer(dict(reply="success", status=200,
reason="Your GET operation worked"))
except self.ignore_exceptions as e:
print("Exception during GET (ignoring): %s" % str(e), file=sys.stderr)
def do_POST(self):
# Read the provided POST data.
# self.answer(self.read())
try:
self.answer(dict(reply="success", status=200,
reason=self.read()))
except self.ignore_exceptions as e:
print("Exception during POST (ignoring): %s" % str(e), file=sys.stderr)
def do_PUT(self):
# Read the provided PUT data.
# self.answer(self.read())
try:
self.answer(dict(reply="success", status=200,
reason=self.read()))
except self.ignore_exceptions as e:
print("Exception during PUT (ignoring): %s" % str(e), file=sys.stderr)
def answer(self, data, withdata=True):
debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path)
if "/sleep/" in self.path:
time.sleep(30)
if "/503/" in self.path:
# Tests for various kinds of 'Retry-After' header parsing
body = None
if "/503/0/" in self.path:
self.send_response(503)
self.send_header("retry-after", "2")
elif "/503/1/" in self.path:
self.send_response(503)
self.send_header("retry-after", "Thu, 31 Dec 2043 23:59:59 GMT")
elif "/503/2/" in self.path:
self.send_response(503)
self.send_header("retry-after", "Fri, 31 Dec 1999 23:59:59 GMT")
elif "/503/3/" in self.path:
self.send_response(503)
self.send_header("retry-after", "")
elif "/503/4/" in self.path:
self.send_response(503)
self.send_header("retry-after", "(*#*(@*(@(")
elif "/503/5/" in self.path:
self.send_response(503)
self.send_header("retry-after", "aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo")
elif "/503/6/" in self.path:
self.send_response(503)
self.send_header("retry-after", "1 2 3 4 5 6 7 8 9 10")
else:
# Unknown request
self.send_response(400)
body = "Unknown /503/ path in server"
if "/reflect/" in self.path:
self.reflect_headers()
self.send_header("Content-type", "text/plain")
self.end_headers()
if body:
self.wfile.write(body)
elif "/bug2295/" in self.path:
# Test for https://jira.secondlife.com/browse/BUG-2295
#
# Client can receive a header indicating data should
# appear in the body without actually getting the body.
# Library needs to defend against this case.
#
body = None
if "/bug2295/0/" in self.path:
self.send_response(206)
self.send_header("Content-Range", "bytes 0-75/2983")
elif "/bug2295/1/" in self.path:
self.send_response(206)
self.send_header("Content-Range", "bytes 0-75/*")
elif "/bug2295/2/" in self.path:
self.send_response(206)
self.send_header("Content-Range", "bytes 0-75/2983")
self.send_header("Content-Length", "0")
elif "/bug2295/00000012/0/" in self.path:
self.send_response(206)
self.send_header("Content-Range", "bytes 0-75/2983")
self.send_header("Content-Length", "76")
elif "/bug2295/inv_cont_range/0/" in self.path:
self.send_response(206)
self.send_header("Content-Range", "bytes 0-75/2983")
body = "Some text, but not enough."
else:
# Unknown request
self.send_response(400)
if "/reflect/" in self.path:
self.reflect_headers()
self.send_header("Content-type", "text/plain")
self.end_headers()
if body:
self.wfile.write(body.encode("utf-8"))
elif "fail" not in self.path:
data = data.copy() # we're going to modify
# Ensure there's a "reply" key in data, even if there wasn't before
data["reply"] = data.get("reply", llsd.LLSD("success"))
response = llsd.format_xml(data)
debug("success: %s", response)
self.send_response(200)
if "/reflect/" in self.path:
self.reflect_headers()
self.send_header("Content-type", "application/llsd+xml")
self.send_header("Content-Length", str(len(response)))
self.send_header("X-LL-Special", "Mememememe");
self.end_headers()
if withdata:
self.wfile.write(response)
else: # fail requested
status = data.get("status", 500)
# self.responses maps an int status to a (short, long) pair of
# strings. We want the longer string. That's why we pass a string
# pair to get(): the [1] will select the second string, whether it
# came from self.responses or from our default pair.
reason = data.get("reason",
self.responses.get(status,
("fail requested",
"Your request specified failure status %s "
"without providing a reason" % status))[1])
debug("fail requested: %s: %r", status, reason)
self.send_error(status, reason)
if "/reflect/" in self.path:
self.reflect_headers()
self.end_headers()
def reflect_headers(self):
for (name, val) in self.headers.items():
# print("Header: %s %s" % (name, val), file=sys.stderr)
self.send_header("X-Reflect-" + name, val)
if not VERBOSE:
# When VERBOSE is set, skip both these overrides because they exist to
# suppress output.
def log_request(self, code, size=None):
# For present purposes, we don't want the request splattered onto
# stderr, as it would upset devs watching the test run
pass
def log_error(self, format, *args):
# Suppress error output as well
pass
class Server(HTTPServer):
# This pernicious flag is on by default in HTTPServer. But proper
# operation of freeport() absolutely depends on it being off.
allow_reuse_address = False
# Override of BaseServer.handle_error(). Not too interested
# in errors and the default handler emits a scary traceback
# to stderr which annoys some. Disable this override to get
# default behavior which *shouldn't* cause the program to return
# a failure status.
def handle_error(self, request, client_address):
print('-'*40)
print('Ignoring exception during processing of request from %' % (client_address))
print('-'*40)
if __name__ == "__main__":
do_valgrind = False
path_search = False
options, args = getopt.getopt(sys.argv[1:], "V", ["valgrind"])
for option, value in options:
if option == "-V" or option == "--valgrind":
do_valgrind = True
# function to make a server with specified port
make_server = lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)
if not sys.platform.startswith("win"):
# Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the
# runtime.
httpd = make_server(0)
else:
# "Then there's Windows"
# Instantiate a Server(TestHTTPRequestHandler) on the first free port
# in the specified port range.
httpd, port = freeport(range(8000, 8020), make_server)
# Pass the selected port number to the subject test program via the
# environment. We don't want to impose requirements on the test program's
# command-line parsing -- and anyway, for C++ integration tests, that's
# performed in TUT code rather than our own.
os.environ["LL_TEST_PORT"] = str(httpd.server_port)
debug("$LL_TEST_PORT = %s", httpd.server_port)
if do_valgrind:
args = ["valgrind", "--log-file=./valgrind.log"] + args
path_search = True
sys.exit(run(server_inst=httpd, use_path=path_search, *args))
+126
View File
@@ -0,0 +1,126 @@
/**
* @file test_refcounted.hpp
* @brief unit tests for the LLCoreInt::RefCounted class
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef TEST_LLCOREINT_REF_COUNTED_H_
#define TEST_LLCOREINT_REF_COUNTED_H_
#include "_refcounted.h"
// disable all of this because it's hanging win64 builds?
#if ! (LL_WINDOWS && ADDRESS_SIZE == 64)
using namespace LLCoreInt;
namespace tut
{
struct RefCountedTestData
{
// the test objects inherit from this so the member functions and variables
// can be referenced directly inside of the test functions.
};
typedef test_group<RefCountedTestData> RefCountedTestGroupType;
typedef RefCountedTestGroupType::object RefCountedTestObjectType;
RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests");
template <> template <>
void RefCountedTestObjectType::test<1>()
{
set_test_name("RefCounted construction with implicit count");
// create a new ref counted object with an implicit reference
RefCounted * rc = new RefCounted(true);
ensure(rc->getRefCount() == 1);
// release the implicit reference, causing the object to be released
rc->release();
}
template <> template <>
void RefCountedTestObjectType::test<2>()
{
set_test_name("RefCounted construction without implicit count");
// create a new ref counted object with an implicit reference
RefCounted * rc = new RefCounted(false);
ensure(rc->getRefCount() == 0);
// add a reference
rc->addRef();
ensure(rc->getRefCount() == 1);
// release the implicit reference, causing the object to be released
rc->release();
}
template <> template <>
void RefCountedTestObjectType::test<3>()
{
set_test_name("RefCounted addRef and release");
RefCounted * rc = new RefCounted(false);
for (int i = 0; i < 1024; ++i)
{
rc->addRef();
}
ensure(rc->getRefCount() == 1024);
for (int i = 0; i < 1024; ++i)
{
rc->release();
}
}
template <> template <>
void RefCountedTestObjectType::test<4>()
{
set_test_name("RefCounted isLastRef check");
RefCounted * rc = new RefCounted(true);
// with only one reference, isLastRef should be true
ensure(rc->isLastRef());
// release it to clean up memory
rc->release();
}
template <> template <>
void RefCountedTestObjectType::test<5>()
{
set_test_name("RefCounted noRef check");
RefCounted * rc = new RefCounted(false);
// set the noRef
rc->noRef();
// with only one reference, isLastRef should be true
ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED);
}
}
#endif // disabling on Win64
#endif // TEST_LLCOREINT_REF_COUNTED_H_