|
| 1 | +# Native protocol capacities |
| 2 | + |
| 3 | +Include `<httpserver/server/protocol_limits.hpp>` directly, or use |
| 4 | +`<httpserver/server/configuration.hpp>`. `server_options::http2()`, `http3()` |
| 5 | +and `quic()` each return mutable/const references to an optional value profile. |
| 6 | +A missing profile keeps the existing native derivation. A present profile, |
| 7 | +including an empty one, requests explicit capacity validation. Individual unset |
| 8 | +members retain their defaults. |
| 9 | + |
| 10 | +```cpp |
| 11 | +httpserver::server::server_options options; |
| 12 | +// Configure TLS, credentials, enabled protocols and matching listeners first. |
| 13 | +options.budgets().set(httpserver::server::resource::header_fields, 4096); |
| 14 | +options.budgets().set(httpserver::server::resource::quic_reassembly_bytes, |
| 15 | + 16 * 1024 * 1024); |
| 16 | +options.http2().emplace().max_concurrent_requests = 192; |
| 17 | +options.http2()->decoder_table_bytes = 8192; |
| 18 | +options.http2()->encoder_table_bytes = 16384; |
| 19 | +options.http3().emplace().max_concurrent_requests = 24; |
| 20 | +options.http3()->max_blocked_headers = 32; |
| 21 | +options.quic().emplace().max_stream_records = 96; |
| 22 | +httpserver::server::effective_protocol_limits limits; |
| 23 | +auto result = options.resolve_limits(limits); |
| 24 | +if (result.ok()) { |
| 25 | + httpserver::server::native_server server(options); |
| 26 | + result = server.effective_limits(limits); |
| 27 | +} |
| 28 | +``` |
| 29 | + |
| 30 | +`header_capacity_options` holds `max_compressed_bytes`, `max_expanded_bytes` |
| 31 | +and `max_fields`, all optional `std::size_t` values. H2/H3 profiles contain |
| 32 | +`headers`, `max_concurrent_requests`, `decoder_table_bytes` and |
| 33 | +`encoder_table_bytes`. H3 additionally contains `max_blocked_headers` and |
| 34 | +`max_retained_header_bytes`. QUIC contains `max_stream_records`. |
| 35 | + |
| 36 | +`resolve_limits(effective_protocol_limits&) const` performs semantic and storage |
| 37 | +planning, then checks compiled protocol/provider availability. It performs no I/O, |
| 38 | +credential acquisition, reservations, environment reads, binds or worker startup. |
| 39 | +`validate()` runs the same semantic/storage planner without the build availability |
| 40 | +gate; a valid TLS configuration can therefore validate in a TLS-off build while |
| 41 | +resolution returns `not_supported`. Startup completes those checks before binding |
| 42 | +any endpoint, then checks credentials/backend readiness. |
| 43 | + |
| 44 | +`native_server::effective_limits(effective_protocol_limits&) const` reads the |
| 45 | +immutable plan resolved from the server's copied options. It works before listen, |
| 46 | +while serving or draining, after a failed listen, and after stop. Credential |
| 47 | +publication/replacement and mutations of the original options cannot change that |
| 48 | +report. Both methods leave the entire output unchanged on failure. Inspection is |
| 49 | +local startup policy; it does not inspect live connections or imply a successful |
| 50 | +handshake. Managed and external loops use the same plan. |
| 51 | + |
| 52 | +H2 applies to enabled H2 with a TLS TCP listener. H3 and QUIC apply to enabled H3 |
| 53 | +with an explicit TLS UDP listener. Inapplicable effective sections are absent. |
| 54 | +A present profile for an inapplicable/disabled protocol fails `invalid_argument`. |
| 55 | +Unsupported compiled transports fail build-aware resolution/startup with |
| 56 | +`not_supported`. An unused TLS provider selection leaves plaintext HTTP/1 available. Existing TLS profile/transport rules still apply. |
| 57 | + |
| 58 | +## Defaults and bounds |
| 59 | + |
| 60 | +| Capacity | Unset native policy | |
| 61 | +| --- | --- | |
| 62 | +| H2 concurrent requests | `min(128, streams budget)` | |
| 63 | +| H2 compressed header bytes | `min(65536, body_buffer_bytes / 16)` | |
| 64 | +| H2 expanded header bytes | `min(65536, header_bytes / 3)` | |
| 65 | +| H2 fields | `min(256, header_fields budget)` | |
| 66 | +| H2 decoder/encoder table bytes | 4096 each | |
| 67 | +| H3 concurrent requests | `min(16, streams budget)` | |
| 68 | +| H3 compressed/expanded bytes/fields | 8192 / 16384 / 128 | |
| 69 | +| H3 decoder/encoder table bytes | `min(4096, qpack_table_bytes budget)` each | |
| 70 | +| H3 blocked headers | `min(8, blocked_headers budget)` | |
| 71 | +| H3 retained compressed HEADERS bytes | 65536 | |
| 72 | +| QUIC retained stream records | 64 | |
| 73 | +| Initial QUIC bidi/uni stream credit | H3 request ceiling / 8 | |
| 74 | +| Advertised/native send datagram bytes | 1200 / 1200 | |
| 75 | + |
| 76 | +Positive explicit request ceilings must fit the aggregate stream budget. H2's |
| 77 | +ceiling is 1,048,576; the historical128 is a default. H3 requests must also fit the |
| 78 | +retained ledger alongside three local and three peer control/QPACK roles. The |
| 79 | +ledger range is7..65,536; its actual prepaid storage can impose a smaller limit. |
| 80 | +Explicit H3 policy needs at least four aggregate stream seats for three peer |
| 81 | +critical roles and one request. Uni credit8 also permits unknown stream types; |
| 82 | +those records compete with requests and terminal records for the same ledger. |
| 83 | + |
| 84 | +Header bytes and fields must be positive. H2 expanded bytes fit three copies in |
| 85 | +`header_bytes`; H3 needs five aggregate header-byte charges and an additional |
| 86 | +semantic field seat (`max_fields + 1`). Field resource maxima are65,536 for H2 |
| 87 | +and65,535 for H3, subject to the configured budget. H2 compressed bytes fit the |
| 88 | +body-buffer budget after native owner, frame and one stream descriptor workspace. H3 compressed/decoded metadata, retained HEADERS, semantic |
| 89 | +copies, body/response buffers and descriptor arrays must fit its private prepaid |
| 90 | +workspace. At least one maximum compressed section must fit retained HEADERS. |
| 91 | +All arithmetic and wire conversions are checked; errors identify the field or |
| 92 | +storage/budget relationship. Explicit values are rejected instead of clamped. |
| 93 | + |
| 94 | +Compression tables permit zero, with a resource maximum16,777,216 bytes. Both |
| 95 | +local directions must fit the same aggregate compression budget for an explicit |
| 96 | +policy. H2 includes `max(4096, decoder_table_bytes) + encoder_table_bytes`, because |
| 97 | +a reduced advertised decoder maximum becomes enforceable after SETTINGS ACK. |
| 98 | +The initial4096 allowance is reported separately as |
| 99 | +`effective_http2_limits::pre_ack_decoder_table_bytes`. Zero H2 encoder capacity |
| 100 | +emits the required table-size update. See [RFC9113 §4.3.1](https://www.rfc-editor.org/rfc/rfc9113.html#section-4.3.1). |
| 101 | + |
| 102 | +H3 uses `decoder_table_bytes + encoder_table_bytes`. Decoder zero with a positive |
| 103 | +encoder reserves its output workspace; both zero still support static/literal |
| 104 | +sections without dynamic inserts. Blocked-header ceilings permit zero and are |
| 105 | +bounded by the aggregate blocked-header budget (maximum65,536). They are |
| 106 | +independent of request/record ceilings. Encoder ceilings in both protocols are |
| 107 | +local limits: actual use is additionally bounded by the peer's SETTINGS. See |
| 108 | +[RFC9204 §§3.2.3,5,7.3](https://www.rfc-editor.org/rfc/rfc9204.html). |
| 109 | + |
| 110 | +Private H3 data/critical subdivisions grow for explicit policy when the root |
| 111 | +reassembly budget permits. Planning includes the retained flow ledger, native |
| 112 | +transport/framing descriptors, bridge records and scratch/receipt/information |
| 113 | +arrays, decoded fields, semantic copies, compression instruction/output work, |
| 114 | +recovery metadata/payload, queued datagrams, TLS/CRYPTO and critical roles. The |
| 115 | +UDP listener's131072-byte charge remains outside that prepaid envelope. Runtime |
| 116 | +subdivision reservations stop at the prepaid boundary; semantic resources still |
| 117 | +reach the shared ancestors. Completely unset policy preserves its old subdivision |
| 118 | +`min(393216, floor(quic_reassembly_bytes * 3 / 8))` for each pool and retains its |
| 119 | +existing runtime pressure behavior. |
| 120 | + |
| 121 | +A successful resolution proves a bounded structural envelope and space for one |
| 122 | +maximum request alongside persistent records and critical state. It does not |
| 123 | +reserve all concurrent requests at their largest allowed sizes. Connections and |
| 124 | +streams still compete for shared budgets and can encounter ordinary resource |
| 125 | +refusal without changing the report. |
| 126 | + |
| 127 | +## Enforcement, advertisement and retention |
| 128 | + |
| 129 | +The report includes aggregate `budgets`, optional `http2`, `http3` and `quic` |
| 130 | +sections, and nested `initial_advertisement` values. H2 advertises the resolved |
| 131 | +request/decoder ceilings; H3 advertises its decoder/blocked ceilings and QUIC |
| 132 | +advertises initial request credit. Neither protocol advertises a header-list or |
| 133 | +field-section byte maximum. `max_header_list_bytes` and |
| 134 | +`max_field_section_bytes` remain absent even with explicit local header limits. |
| 135 | +Expanded size includes name/value bytes and32 bytes per field, as described in |
| 136 | +[RFC9114 §4.2.2](https://www.rfc-editor.org/rfc/rfc9114.html#section-4.2.2). |
| 137 | + |
| 138 | +Normal request retirement replenishes QUIC MAX_STREAMS credit. Initial credit is |
| 139 | +not a lifetime request count. Terminal descriptors remain retained to prevent |
| 140 | +stream recreation; eventually the total record ledger is authoritative. Larger |
| 141 | +ledgers support more retirements while keeping this bound. See |
| 142 | +[RFC9000 §§4.6,18.2](https://www.rfc-editor.org/rfc/rfc9000.html). |
| 143 | + |
| 144 | +The inspected QUIC datagram values are fixed read-only implementation policy. |
| 145 | +There is no datagram-size setter, PMTU growth or new key-update behavior here. |
| 146 | +Existing per-key usage guards remain in force; this API makes no promise about |
| 147 | +graceful connection-local key exhaustion. |
0 commit comments