114 lines
5.7 KiB
Markdown
114 lines
5.7 KiB
Markdown
# 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.
|
|
|
|
`DownloadManager::active_download_count`, `dispatcher_running`, and
|
|
`is_disposed` expose payload-free lifecycle state for leak assertions. They do
|
|
not expose capability URIs or response data.
|
|
|
|
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:
|
|
|
|
```sh
|
|
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.
|