rpc_dart_http2 0.2.4
rpc_dart_http2: ^0.2.4 copied to clipboard
HTTP/2 caller/responder transports and server bootstrap for rpc_dart.
0.2.4 #
-
BUG (non-ASCII regular header values were silently corrupted):
_headerValuebase64url-encoded any non-ASCII value on send but the decode side never reversed it, so the peer received a mangled string (the same asymmetry class as the-binbug). Per the gRPC HTTP/2 spec, ASCII metadata values must be printable ASCII (%x20-%x7E); a non-conforming value is now rejected with anArgumentErrorinstead of silently transformed. Binary or non-ASCII data must use a-binkey (base64). This also rejects CR/LF in values, closing a header injection vector. Tests intest/grpc_wire_compliance_test.dart. (grpc-messageis unaffected — the core layer percent-encodes it to ASCII before it reaches the transport.) -
BUG (
-binheader wire format was double-encoded and corrupted true binary): the metadata layer already stores-binvalues base64-encoded (e.g.base64Encode(statusDetailsBin)), but the HTTP/2 transport base64-encoded them AGAIN on send (_headerValue) and base64+utf8-decoded them on receive (http2HeadersToRpcMetadata). This double-processing was only self-consistent rpc_dart<->rpc_dart and broke interop with real gRPC peers in both directions; it also corrupted inbound binary that was valid UTF-8.-binvalues are now passed through verbatim on both send and receive (they are already the base64 string gRPC expects on the wire; the metadata getters decode on read). NOTE: this is a wire-format change forgrpc-status-details-binover HTTP/2 — a rpc_dart peer on <=0.2.3 will not interop with >=0.2.4 for status details. Regression tests intest/grpc_wire_compliance_test.dart(round-trips non-UTF8 binary). -
BUG (END_STREAM landed on the wrong message of a batch): when a single DATA frame parsed into multiple messages, both
RpcHttp2ResponderTransport(_handleIncomingData) andRpcHttp2CallerTransport(_handleDataMessage) detected the last message viamsgData == messages.last.messagesis aList<Uint8List>and==onUint8Listis identity-based, so the end-of-stream flag could land on an earlier element (e.g. when an earlier element shared the same object reference as the last). END_STREAM is now selected positionally — only the genuinely last element of the batch (i == messages.length - 1) is marked end-of-stream. Regression test:test/audit/end_of_stream_batch_test.dart.
0.2.3 #
- BUG (silent data loss on server-initiated streams):
RpcHttp2ResponderTransportsends (sendMetadata/sendMessage) now THROW aStateErrorwhen targeting a stream id that is not a known incoming (client-initiated) stream — i.e. an id minted bycreateStream()(server-push, unimplemented) or a stale/released id. Previously such sends logged a warning and returned, silently dropping the data. Legitimate unary/streaming responses, which reply on the client's stream id, are unaffected. Server-push remains unimplemented; the dead_outgoingStreamsmap (read but never populated) was removed along with its health/clear references.
0.2.2 #
Server-side hardening and a per-stream error-routing correctness fix.
- BUG A (security policy reachable):
RpcHttp2Servernow accepts aRpcSecurityPolicy(defaultconst RpcSecurityPolicy()) and forwards it to everyRpcHttp2ResponderTransport. Previously the server always used the default policy with no way to set one, somaxMessageLengthBytes/maxActiveStreamswere effectively unreachable. Also exposed onRpcHttp2Server.createWithContracts. - BUG B (per-stream error isolation): the caller and responder transports share
a single broadcast
StreamControllerforincomingMessages, andgetMessagesForStreamfiltered it bystreamId. Because.where()does not filter errors, an error on one stream was delivered to EVERY stream's subscriber (a parse error on stream 3 surfaced as an error on stream 5). Per-stream errors are now wrapped inRpcHttp2StreamErrorand routed only to the owning stream viafilterStreamEvents; connection-level fatal errors still fan out to all subscribers (correct). PublicincomingMessages/getMessagesForStreamAPI is unchanged. - BUG C (TLS / h2):
RpcHttp2Serveraccepts an optionalSecurityContext. When provided it binds aSecureServerSocketadvertising ALPNh2instead of a plaintextServerSocket; plaintext h2c remains the default. AddedRpcHttp2Server.isSecure. AddedRpcHttp2CallerTransport.viaSocket(...)so a TLSSecureSocket(with custom cert validation/pinning) can back the caller transport. Note: ALPN negotiation works at the wire level, butSecureSocket.selectedProtocolmay reportnullon some platforms (observed on the macOS Dart VM); the TLS h2 round-trip itself is verified by tests. - Fixed a latent "Concurrent modification during iteration" crash in
RpcHttp2Server.stop()(endpoint list mutated bysocket.doneduring close). RpcHttp2Server.portnow returns the OS-assigned port after binding when constructed with port0.
0.2.1 #
RpcHttp2CallerTransport: added optionalproxyUriparameter to bothsecureandinsecureconstructors — supports HTTP CONNECT proxy tunneling with optional Basic auth from URI userinfo.- Updated to
rpc_dart: ^3.1.0.
0.2.0 #
- Updated to
rpc_dart: ^3.0.0. - gRPC wire compliance fixes: correct trailers framing, binary headers, Trailers-Only responses.
RpcHttp2Server: supportsRpcReflectionRegistry.attachTo()for gRPC Server Reflection.
0.1.0 #
- Initial release: HTTP/2 caller/responder transports and
RpcHttp2Serverfor rpc_dart.