5.5 KiB
Capability HTTP, event queues, rate limiting, and downloads
HttpCapsClient, CapsRateLimiter, and Http::DownloadManager are native Rust
implementations of the corresponding LibreMetaverse classes. They do not call
.NET code or use a helper process. A normal GridClient owns a reqwest
client using rustls, its category limiter, and their shutdown order. Tests and
embedders can inject either a reqwest::Client or an executor-neutral
HttpMessageHandler, plus a TimeProvider for deterministic token-bucket time.
Seed capabilities and EventQueueGet
Simulator::SetSeedCaps owns a native Caps instance. It posts the complete
reference capability-name array as LLSD/XML, accepts only HTTP(S) capability
URIs, registers every accepted URI with the category limiter, and starts
EventQueueGet when advertised. Seed requests retry transient failures with
bounded exponential backoff, stop permanently on HTTP 404, and are cancelled
and joined when a simulator changes seed capability or disconnects.
The event queue sends the reference { ack, done } LLSD/XML shape, begins with
acknowledgement zero, and advances the acknowledgement only after a complete,
valid { id, events } response. Responses are limited to 1,024 events, 100,000
nodes per body, and 8 MiB of binary data per body. Raw events are invoked in
wire order, including unknown names. Known messages use the native Linden LLSD
codecs and the same named/default CAPS callback registry as UDP; otherwise the
generic packet catalog translates the body and feeds the bounded incoming UDP
dispatch queue. Transient HTTP failures use bounded deterministic-jitter
backoff, 404/410/499 close the poller, and graceful shutdown posts one final
done = true acknowledgement. Cancellation interrupts an outstanding poll and
joins its owned thread promptly.
HTTP policy
Only absolute http and https request URIs are accepted. Response headers
are stored as text without interpreting Location or Content-Location, so a
successful response containing an opaque slcaps:// value remains usable.
Production redirects are followed only while the next URI is HTTP(S) and the
configured redirect count has not been exhausted.
The default policy bounds request bodies at 32 MiB, wire response bodies at
64 MiB, decompressed bodies at 128 MiB, redirects at 10, and concurrent HTTP
requests at Settings::max_http_connections() (32). Declared and observed
response lengths are checked. Gzip and zlib/raw-deflate decoding reads through
a limiting adapter, preventing a small compressed response from expanding
beyond policy. Unsupported content encodings fail closed. Upload and download
progress follows the mapped C# ProgressReport, including two-decimal percent
values when a total length is known.
CapsHttpLimits, HttpCapsClient::with_reqwest_client, and
HttpCapsClient::with_handler_policy are the explicit injection seams. Fake
handlers receive the exact method, URI, content type, and request bytes and can
return deterministic status, headers, and bytes without external network
access. Grid-client shutdown cancels in-flight operations before disposing the
rate limiter.
Rate categories
Each category owns a bounded, oldest-first token bucket. A full waiting queue returns a non-acquired lease; the HTTP pipeline then proceeds, matching the C# handler's overload behavior rather than dropping a request.
| Category | Burst | Tokens/second | Queue |
|---|---|---|---|
| Default | 20 | 10 | 30 |
| RenderMaterials | 4 | 2 | 20 |
| AssetFetch | 24 | 12 | 60 |
| AssetUpload | 4 | 2 | 10 |
| Inventory | 6 | 3 | 20 |
| EventQueue | 3 | 2 | 3 |
| DisplayName | 5 | 2 | 15 |
| Voice | 10 | 5 | 20 |
RegisterCapUri applies the complete case-insensitive cap-name mapping from the
reference. Unregistered URIs use Default; texture/mesh, upload, inventory,
event queue, display-name/profile, material, and voice endpoints remain
isolated from one another. Cancellation removes queued tickets, and disposal
wakes all waiters immediately.
Download behavior
The manager owns a 256-entry dispatch queue and permits 1 through 32 active
downloads (8 by default). Absolute URIs are canonicalized before deduplication.
All subscribers to the same active URI share one HTTP request, receive progress,
and complete with cloned response bytes. Cancellation by any subscriber cancels
that shared operation, as in the reference. Caller-provided
TaskCompletionSource values are completed by the fire-and-forget API.
HTTP 401, 403, 404, and 410 responses are permanent. Other unsuccessful statuses and transport failures retry up to the request's configured retry count with bounded backoff. Disposal cancels active and queued work, wakes every waiter, joins the dispatcher, and prevents new requests.
Errors expose only typed categories and Debug output substitutes a redacted
marker for capability URIs. No request URL, query token, body, or response body
is logged.
Run the deterministic issue gate with:
cargo test -p libremetaverse --test caps_http
cargo test -p libremetaverse --lib event_queue::tests
cargo test -p libremetaverse --lib caps::tests
cargo test -p libremetaverse --lib message_decoder::tests
cargo test -p libremetaverse-compat-tests --test network_semantics queue_download
cargo test -p libremetaverse-compat-tests --test network_semantics put_response_with_non_http_location
The first suite uses injected handlers for all policy and concurrency cases and a loopback-only fake HTTP server for the actual reqwest redirect/stream path. It requires no live grid or Internet service.