SH-4492 Create a useful README for llcorehttp

Wed, 18 Sep 2013 18:44:41 -0400

author
Monty Brandenberg <monty@lindenlab.com>
date
Wed, 18 Sep 2013 18:44:41 -0400
changeset 40725
3053bf46de4b
parent 40724
d3cca5ca0fbe
child 40726
ea87b029d00c

SH-4492 Create a useful README for llcorehttp
Last bit for this release. Describe stream adapters and how
to select a policy class. Slight changes to setup code to
make reality reflect documentation.

indra/llcorehttp/README.Linden file | annotate | diff | revisions
indra/newview/llappcorehttp.cpp file | annotate | diff | revisions
indra/newview/llappcorehttp.h file | annotate | diff | revisions
     1.1 --- a/indra/llcorehttp/README.Linden	Tue Sep 17 21:55:44 2013 +0000
     1.2 +++ b/indra/llcorehttp/README.Linden	Wed Sep 18 18:44:41 2013 -0400
     1.3 @@ -1,13 +1,13 @@
     1.4  
     1.5  
     1.6  
     1.7 -1.  HTTP fetching in 15 Minutes
     1.8 +1.  HTTP Fetching in 15 Minutes
     1.9  
    1.10      Let's start with a trivial working example.  You'll need a throwaway
    1.11      build of the viewer.  And we'll use indra/newview/llappviewer.cpp as
    1.12 -    our host.
    1.13 +    the host module for these hacks.
    1.14  
    1.15 -    Add some needed headers:
    1.16 +    First, add some headers:
    1.17  
    1.18  
    1.19          #include "httpcommon.h"
    1.20 @@ -182,7 +182,7 @@
    1.21                  {
    1.22                      // There's some data.  A BufferArray is a linked list
    1.23                      // of buckets.  We'll create a linear buffer and copy
    1.24 -                    // it into it.
    1.25 +                    // the data into it.
    1.26                      size_t data_len = data->size();
    1.27                      char * data_blob = new char [data_len + 1];
    1.28                      data->read(0, data_blob, data_len);
    1.29 @@ -419,7 +419,7 @@
    1.30  
    1.31      BufferArray.  The core data representation for request and response
    1.32      bodies.  In HTTP responses, it's fetched with the getBody() method
    1.33 -    and may be NULL or non-NULL but zero length.  All successful data
    1.34 +    and may be NULL or non-NULL with zero length.  All successful data
    1.35      handling should check both conditions before attempting to fetch
    1.36      data from the object.  Data access model uses simple read/write
    1.37      semantics:
    1.38 @@ -429,8 +429,8 @@
    1.39          * read()
    1.40          * write()
    1.41  
    1.42 -    There is a more sophisticated stream adapter that extends these
    1.43 -    methods and will be covered below.  So, one way to retrieve data
    1.44 +    (There is a more sophisticated stream adapter that extends these
    1.45 +    methods and will be covered below.)  So, one way to retrieve data
    1.46      from a request is as follows:
    1.47  
    1.48  
    1.49 @@ -449,6 +449,7 @@
    1.50      by just writing new values to the shared object.  And in tests
    1.51      everything will appear to work.  Then you ship and people in the
    1.52      real world start hitting read/write races in strings and then crash.
    1.53 +    Don't be lazy.
    1.54  
    1.55      HttpHandle.  Uniquely identifies a request and can be used to
    1.56      identify it in an onCompleted() method or cancel it if it's still
    1.57 @@ -459,9 +460,197 @@
    1.58  
    1.59  5.  And Still More Refinements
    1.60  
    1.61 +    (Note: The following refinements are just code fragments.  They
    1.62 +    don't directly fit into the working example above.  But they
    1.63 +    demonstrate several idioms you'll want to copy.)
    1.64 +
    1.65 +    LLSD, std::streambuf, std::iostream.  The read(), write() and
    1.66 +    append() methods may be adequate for your purposes.  But we use a
    1.67 +    lot of LLSD.  Its interfaces aren't particularly compatible with
    1.68 +    BufferArray.  And so two adapters are available to give
    1.69 +    stream-like behaviors:  BufferArrayStreamBuf and BufferArrayStream,
    1.70 +    which implement the std::streambuf and std::iostream interfaces,
    1.71 +    respectively.
    1.72 +
    1.73 +    A std::streambuf interface isn't something you'll want to use
    1.74 +    directly.  Instead, you'll use the much friendlier std::iostream
    1.75 +    interface found in BufferArrayStream.  This adapter gives you all
    1.76 +    the '>>' and '<<' operators you'll want as well as working
    1.77 +    directly with the LLSD conversion operators.
    1.78 +
    1.79 +    Some new headers:
    1.80 +
    1.81 +
    1.82 +        #include "bufferstream.h"
    1.83 +        #include "llsdserialize.h"
    1.84 +
    1.85 +
    1.86 +    And an updated fragment based on onCompleted() above:
    1.87 +
    1.88 +
    1.89 +                // Successful request.  Try to fetch the data
    1.90 +                LLCore::BufferArray * data = response->getBody();
    1.91 +                LLSD resp_llsd;
    1.92 +
    1.93 +                if (data && data->size())
    1.94 +                {
    1.95 +                    // There's some data and we expect this to be
    1.96 +                    // LLSD.  Checking of content type and validation
    1.97 +                    // during parsing would be admirable additions.
    1.98 +                    // But we'll forgo that now.
    1.99 +                    LLCore::BufferArrayStream data_stream(data);
   1.100 +                    LLSDSerialize::fromXML(resp_llsd, data_stream);
   1.101 +                }
   1.102 +                LL_INFOS("Hack") << "LLSD Received:  " << resp_llsd << LL_ENDL;
   1.103 +            }
   1.104 +            else
   1.105 +            {
   1.106 +
   1.107 +
   1.108 +    Converting an LLSD object into an XML stream stored in a
   1.109 +    BufferArray is just the reverse of the above:
   1.110 +
   1.111 +
   1.112 +        BufferArray * data = new BufferArray();
   1.113 +        LLCore::BufferArrayStream data_stream(data);
   1.114 +
   1.115 +        LLSD src_llsd;
   1.116 +        src_llsd["foo"] = "bar";
   1.117 +
   1.118 +        LLSDSerialize::toXML(src_llsd, data_stream);
   1.119 +
   1.120 +        // 'data' now contains an XML payload and can be sent
   1.121 +        // to a web service using the requestPut() or requestPost()
   1.122 +        //  methods.
   1.123 +        ...  requestPost(...);
   1.124 +
   1.125 +        // And don't forget to release the BufferArray.
   1.126 +        data->release();
   1.127 +        data = NULL;
   1.128 +
   1.129 +
   1.130 +    LLSD will often go hand-in-hand with BufferArray and data
   1.131 +    transport.  But you can also do all the streaming I/O you'd expect
   1.132 +    of a std::iostream object:
   1.133 +
   1.134 +
   1.135 +        BufferArray * data = new BufferArray();
   1.136 +        LLCore::BufferArrayStream data_stream(data);
   1.137 +
   1.138 +        data_stream << "Hello, World!" << 29.4 << '\n';
   1.139 +        std::string str;
   1.140 +        data_stream >> str;
   1.141 +        std::cout << str << std::endl;
   1.142 +
   1.143 +        data->release();
   1.144 +        // Actual delete will occur when 'data_stream'
   1.145 +        // falls out of scope and is destructed.
   1.146 +
   1.147 +
   1.148 +    Scoping objects and cleaning up.  The examples haven't bothered
   1.149 +    with cleanup of objects that are no longer needed.  Instead, most
   1.150 +    objects have been allocated as if they were global and eternal.
   1.151 +    You'll put the objects in more appropriate feature objects and
   1.152 +    clean them up as a group.  Here's a checklist for actions you may
   1.153 +    need to take on cleanup:
   1.154 +
   1.155 +    * Call delete on:
   1.156 +      o HttpHandlers created on the heap
   1.157 +      o HttpRequest objects
   1.158 +    * Call release() on:
   1.159 +      o BufferArray objects
   1.160 +      o HttpHeaders objects
   1.161 +      o HttpOptions objects
   1.162 +      o HttpResponse objects
   1.163 +
   1.164 +    On program exit, as threads wind down, the library continues to
   1.165 +    operate safely.  Threads don't interact via the library and even
   1.166 +    dangling references to HttpHandler objects are safe.  If you don't
   1.167 +    call HttpRequest::update(), handler references are never
   1.168 +    dereferenced.
   1.169 +
   1.170 +    You can take a more thorough approach to wind-down.  Keep a list
   1.171 +    of HttpHandles (not HttpHandlers) of outstanding requests.  For
   1.172 +    each of these, call HttpRequest::requestCancel() to cancel the
   1.173 +    operation.  (Don't add the cancel requests' handled to the list.)
   1.174 +    This will cancel the outstanding requests that haven't completed.
   1.175 +    Canceled or completed, all requests will queue notifications.  You
   1.176 +    can now cycle calling update() discarding responses.  Continue
   1.177 +    until all requests notify or a few seconds have passed.
   1.178 +
   1.179 +    Global startup and shutdown is handled in the viewer.  But you can
   1.180 +    learn about it in the code or in the documentation in the headers.
   1.181 +
   1.182  
   1.183  6.  Choosing a Policy Class
   1.184  
   1.185 +    Now it's time to get rid of the default policy class.  Take a look
   1.186 +    at the policy class definitions in newview/llappcorehttp.h.
   1.187 +    Ideally, you'll find one that's compatible with what you're doing.
   1.188 +    Some of the compatibility guidelines are:
   1.189  
   1.190 +    * Destination: Pair of host and port.  Mixing requests with
   1.191 +      different destinations may cause more connection setup and tear
   1.192 +      down.
   1.193 +
   1.194 +    * Method: http or https.  Usually moot given destination.  But
   1.195 +      mixing these may also cause connection churn.
   1.196 +
   1.197 +    * Transfer size: If you're moving 100MB at a time and you make your
   1.198 +      requests to the same policy class as a lot of small, fast event
   1.199 +      information that fast traffic is going to get stuck behind you
   1.200 +      and someone's experience is going to be miserable.
   1.201 +
   1.202 +    * Long poll requests: These are long-lived, must- do operations.
   1.203 +      They have a special home called AP_LONG_POLL.
   1.204 +
   1.205 +    * Concurrency: High concurrency (5 or more) and large transfer
   1.206 +      sizes are incompatible.  Another head-of-the-line problem.  High
   1.207 +      concurrency is tolerated when it's desired to get maximal
   1.208 +      throughput.  Mesh and texture downloads, for example.
   1.209 +
   1.210 +    * Pipelined: If your requests are not idempotent, stay away from
   1.211 +      anything marked 'soon' or 'yes'.  Hidden retries may be a
   1.212 +      problem for you.  For now, would also recommend keeping PUT and
   1.213 +      POST requests out of classes that may be pipelined.  Support for
   1.214 +      that is still a bit new.
   1.215 +
   1.216 +    If you haven't found a compatible match, you can either create a
   1.217 +    new class (llappcorehttp.*) or just use AP_DEFAULT, the catchall
   1.218 +    class when all else fails.  Inventory query operations might be a
   1.219 +    candidate for a new class that supported pipelining on https:.
   1.220 +    Same with display name lookups and other bursty-at-login
   1.221 +    operations.  For other things, AP_DEFAULT will do what it can and
   1.222 +    will, in some way or another, tolerate any usage.  Whether the
   1.223 +    users' experiences are good are for you to determine.
   1.224 +
   1.225 +    
   1.226  7.  FAQ
   1.227  
   1.228 +    Q1.  What do these policy classes achieve?
   1.229 +
   1.230 +    A1.  Previously, HTTP-using code in the viewer was written as if
   1.231 +    it were some isolated, local operation that didn't have to
   1.232 +    consider resources, contention or impact on services and the
   1.233 +    larger environment.  The result was an application with on the
   1.234 +    order of 100 HTTP launch points in its codebase that could create
   1.235 +    dozens or even 100's of TCP connections zeroing in on grid
   1.236 +    services and disrupting networking equipment, web services and
   1.237 +    innocent users.  The use of policy classes (modeled on
   1.238 +    http://en.wikipedia.org/wiki/Class-based_queueing) is a means to
   1.239 +    restrict connection concurrency, good and necessary in itself.  In
   1.240 +    turn, that reduces demands on an expensive resource (connection
   1.241 +    setup and concurrency) which relieves strain on network points.
   1.242 +    That enables connection keepalive and opportunites for true
   1.243 +    improvements in throughput and user experience.
   1.244 +
   1.245 +    Another aspect of the classes is that they give some control over
   1.246 +    how competing demands for the network will be apportioned.  If
   1.247 +    mesh fetches, texture fetches and inventory queries are all being
   1.248 +    made at once, the relative weights of their classes' concurrency
   1.249 +    limits established that apportioning.  We now have an opportunity
   1.250 +    to balance the entire viewer system.
   1.251 +
   1.252 +    Q2.  How's that data sharing with refcounts working for you?
   1.253 +
   1.254 +    A2.  Meh.
     2.1 --- a/indra/newview/llappcorehttp.cpp	Tue Sep 17 21:55:44 2013 +0000
     2.2 +++ b/indra/newview/llappcorehttp.cpp	Wed Sep 18 18:44:41 2013 -0400
     2.3 @@ -52,6 +52,11 @@
     2.4  } init_data[] =					//  Default and dynamic values for classes
     2.5  {
     2.6  	{
     2.7 +		LLAppCoreHttp::AP_DEFAULT,			8,		8,		8,		0,
     2.8 +		"",
     2.9 +		"other"
    2.10 +	},
    2.11 +	{
    2.12  		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		0,
    2.13  		"TextureFetchConcurrency",
    2.14  		"texture fetch"
    2.15 @@ -75,6 +80,11 @@
    2.16  		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		0,
    2.17  		"",
    2.18  		"asset upload"
    2.19 +	},
    2.20 +	{
    2.21 +		LLAppCoreHttp::AP_LONG_POLL,		32,		32,		32,		0,
    2.22 +		"",
    2.23 +		"long poll"
    2.24  	}
    2.25  };
    2.26  
    2.27 @@ -154,25 +164,21 @@
    2.28  	{
    2.29  		const EAppPolicy policy(init_data[i].mPolicy);
    2.30  
    2.31 -		// Create a policy class but use default for texture for now.
    2.32 -		// This also has the side-effect of initializing the default
    2.33 -		// class to desired values.
    2.34 -		if (AP_TEXTURE == policy)
    2.35 +		if (AP_DEFAULT == policy)
    2.36  		{
    2.37 +			// Pre-created
    2.38 +			continue;
    2.39 +		}
    2.40 +
    2.41 +		mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
    2.42 +		if (! mPolicies[policy])
    2.43 +		{
    2.44 +			// Use default policy (but don't accidentally modify default)
    2.45 +			LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
    2.46 +							 << ".  Using default policy."
    2.47 +							 << LL_ENDL;
    2.48  			mPolicies[policy] = mPolicies[AP_DEFAULT];
    2.49 -		}
    2.50 -		else
    2.51 -		{
    2.52 -			mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
    2.53 -			if (! mPolicies[policy])
    2.54 -			{
    2.55 -				// Use default policy (but don't accidentally modify default)
    2.56 -				LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
    2.57 -								 << ".  Using default policy."
    2.58 -								 << LL_ENDL;
    2.59 -				mPolicies[policy] = mPolicies[AP_DEFAULT];
    2.60 -				continue;
    2.61 -			}
    2.62 +			continue;
    2.63  		}
    2.64  	}
    2.65  
     3.1 --- a/indra/newview/llappcorehttp.h	Tue Sep 17 21:55:44 2013 +0000
     3.2 +++ b/indra/newview/llappcorehttp.h	Wed Sep 18 18:44:41 2013 -0400
     3.3 @@ -45,12 +45,109 @@
     3.4  
     3.5  	enum EAppPolicy
     3.6  	{
     3.7 +		/// Catchall policy class.  Not used yet
     3.8 +		/// but will have a generous concurrency
     3.9 +		/// limit.  Deep queueing possible by having
    3.10 +		/// a chatty HTTP user.
    3.11 +		///
    3.12 +		/// Destination:     anywhere
    3.13 +		/// Protocol:        http: or https:
    3.14 +		/// Transfer size:   KB-MB
    3.15 +		/// Long poll:       no
    3.16 +		/// Concurrency:     high 
    3.17 +		/// Request rate:    unknown
    3.18 +		/// Pipelined:       no
    3.19  		AP_DEFAULT,
    3.20 +
    3.21 +		/// Texture fetching policy class.  Used to
    3.22 +		/// download textures via capability or SSA
    3.23 +		/// baking service.  Deep queueing of requests.
    3.24 +		/// Do not share.
    3.25 +		///
    3.26 +		/// Destination:     simhost:12046 & bake-texture:80
    3.27 +		/// Protocol:        http:
    3.28 +		/// Transfer size:   KB-MB
    3.29 +		/// Long poll:       no
    3.30 +		/// Concurrency:     high
    3.31 +		/// Request rate:    high
    3.32 +		/// Pipelined:       soon
    3.33  		AP_TEXTURE,
    3.34 +
    3.35 +		/// Legacy mesh fetching policy class.  Used to
    3.36 +		/// download textures via 'GetMesh' capability.
    3.37 +		/// To be deprecated.  Do not share.
    3.38 +		///
    3.39 +		/// Destination:     simhost:12046
    3.40 +		/// Protocol:        http:
    3.41 +		/// Transfer size:   KB-MB
    3.42 +		/// Long poll:       no
    3.43 +		/// Concurrency:     dangerously high
    3.44 +		/// Request rate:    high
    3.45 +		/// Pipelined:       no
    3.46  		AP_MESH1,
    3.47 +
    3.48 +		/// New mesh fetching policy class.  Used to
    3.49 +		/// download textures via 'GetMesh2' capability.
    3.50 +		/// Used when fetch request (typically one LOD)
    3.51 +		/// is 'small', currently defined as 2MB.
    3.52 +		/// Very deeply queued.  Do not share.
    3.53 +		///
    3.54 +		/// Destination:     simhost:12046
    3.55 +		/// Protocol:        http:
    3.56 +		/// Transfer size:   KB-MB
    3.57 +		/// Long poll:       no
    3.58 +		/// Concurrency:     high
    3.59 +		/// Request rate:    high
    3.60 +		/// Pipelined:       soon
    3.61  		AP_MESH2,
    3.62 +
    3.63 +		/// Large mesh fetching policy class.  Used to
    3.64 +		/// download textures via 'GetMesh' or 'GetMesh2'
    3.65 +		/// capability.  Used when fetch request
    3.66 +		/// is not small to avoid head-of-line problem
    3.67 +		/// when large requests block a sequence of small,
    3.68 +		/// fast requests.  Can be shared with similar
    3.69 +		/// traffic that can wait for longish stalls
    3.70 +		/// (default timeout 600S).
    3.71 +		///
    3.72 +		/// Destination:     simhost:12046
    3.73 +		/// Protocol:        http:
    3.74 +		/// Transfer size:   MB
    3.75 +		/// Long poll:       no
    3.76 +		/// Concurrency:     low
    3.77 +		/// Request rate:    low
    3.78 +		/// Pipelined:       soon
    3.79  		AP_LARGE_MESH,
    3.80 +
    3.81 +		/// Asset upload policy class.  Used to store
    3.82 +		/// assets (mesh only at the moment) via
    3.83 +		/// changeable URL.  Responses may take some
    3.84 +		/// time (default timeout 240S).
    3.85 +		///
    3.86 +		/// Destination:     simhost:12043
    3.87 +		/// Protocol:        https:
    3.88 +		/// Transfer size:   KB-MB
    3.89 +		/// Long poll:       no
    3.90 +		/// Concurrency:     low
    3.91 +		/// Request rate:    low
    3.92 +		/// Pipelined:       no
    3.93  		AP_UPLOADS,
    3.94 +
    3.95 +		/// Long-poll-type HTTP requests.  Not
    3.96 +		/// bound by a connection limit.  Requests
    3.97 +		/// will typically hang around for a long
    3.98 +		/// time (~30S).  Only shareable with other
    3.99 +		/// long-poll requests.
   3.100 +		///
   3.101 +		/// Destination:     simhost:12043
   3.102 +		/// Protocol:        https:
   3.103 +		/// Transfer size:   KB
   3.104 +		/// Long poll:       yes
   3.105 +		/// Concurrency:     unlimited but low in practice
   3.106 +		/// Request rate:    low
   3.107 +		/// Pipelined:       no
   3.108 +		AP_LONG_POLL,
   3.109 +
   3.110  		AP_COUNT						// Must be last
   3.111  	};
   3.112  	

mercurial